"Errore: pagina Opzioni non trovata" nell'invio della pagina Impostazioni per un plug-in OOP


19

Sto sviluppando un plug-in usando il repository Boilerplate di Tom McFarlin come modello, che utilizza pratiche OOP. Ho cercato di capire esattamente perché non riesco a inviare correttamente le mie impostazioni. Ho provato a impostare l'attributo action su una stringa vuota come suggerito su un'altra domanda qui intorno, ma questo non ha aiutato ...

Di seguito è riportata la configurazione del codice generale che sto usando ...

Il modulo (/views/admin.php):

<div class="wrap">
    <h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
    <form action="options.php" method="post">
        <?php
        settings_fields( $this->plugin_slug );
        do_settings_sections( $this->plugin_slug );
        submit_button( 'Save Settings' );
        ?>
    </form>
</div>

Per il codice seguente, supponiamo che esistano tutti i callback per add_settings_field () e add_settings_section (), ad eccezione di 'option_list_selection'.

La classe Admin plug-in (/ {plugin_name} -class-admin.php):

namespace wp_plugin_name;

class Plugin_Name_Admin
{
    /**
     * Note: Some portions of the class code and method functions are missing for brevity
     * Let me know if you need more information...
     */

    private function __construct()
    {
        $plugin              = Plugin_Name::get_instance();

        $this->plugin_slug   = $plugin->get_plugin_slug();
        $this->friendly_name = $plugin->get_name(); // Get "Human Friendly" presentable name

        // Adds all of the options for the administrative settings
        add_action( 'admin_init', array( $this, 'plugin_options_init' ) );

        // Add the options page and menu item
        add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );


    }

    public function add_plugin_admin_menu()
    {

        // Add an Options Page
        $this->plugin_screen_hook_suffix =
        add_options_page(
            __( $this->friendly_name . " Options", $this->plugin_slug ),
            __( $this->friendly_name, $this->plugin_slug ),
            "manage_options", 
            $this->plugin_slug,
            array( $this, "display_plugin_admin_page" )
        );

    }

    public function display_plugin_admin_page()
    {
        include_once( 'views/admin.php' );
    }

    public function plugin_options_init()
    {
        // Update Settings
        add_settings_section(
            'maintenance',
            'Maintenance',
            array( $this, 'maintenance_section' ),
            $this->plugin_slug
        );

        // Check Updates Option
        register_setting( 
            'maintenance',
            'plugin-name_check_updates',
            'wp_plugin_name\validate_bool'
        );

        add_settings_field(
            'check_updates',
            'Should ' . $this->friendly_name . ' Check For Updates?',
            array( $this, 'check_updates_field' ),
            $this->plugin_slug,
            'maintenance'
        );

        // Update Period Option
        register_setting(
            'maintenance',
            'plugin-name_update_period',
            'wp_plugin_name\validate_int'
        );

        add_settings_field(
            'update_frequency',
            'How Often Should ' . $this->friendly_name . ' Check for Updates?',
            array( $this, 'update_frequency_field' ),
            $this->plugin_slug,
            'maintenance'
        );

        // Plugin Option Configurations
        add_settings_section(
            'category-option-list', 'Widget Options List',
            array( $this, 'option_list_section' ),
            $this->plugin_slug
        );
    }
}

Alcuni aggiornamenti richiesti:

Modifica dell'attributo di azione in:

<form action="../../options.php" method="post">

... provoca semplicemente un errore 404. Di seguito è riportato l'estratto dei registri di Apache. Si noti che gli script WordPress predefiniti e le code in coda CSS vengono rimossi:

# Changed to ../../options.php
127.0.0.1 - - [01/Apr/2014:15:59:43 -0400] "GET /wp-admin/options-general.php?page=pluginname-widget HTTP/1.1" 200 18525
127.0.0.1 - - [01/Apr/2014:15:59:43 -0400] "GET /wp-content/plugins/PluginName/admin/assets/css/admin.css?ver=0.1.1 HTTP/1.1" 304 -
127.0.0.1 - - [01/Apr/2014:15:59:43 -0400] "GET /wp-content/plugins/PluginName/admin/assets/js/admin.js?ver=0.1.1 HTTP/1.1" 304 -
127.0.0.1 - - [01/Apr/2014:15:59:52 -0400] "POST /options.php HTTP/1.1" 404 1305
127.0.0.1 - - [01/Apr/2014:16:00:32 -0400] "POST /options.php HTTP/1.1" 404 1305

#Changed to options.php
127.0.0.1 - - [01/Apr/2014:16:00:35 -0400] "GET /wp-admin/options-general.php?page=pluginname-widget HTTP/1.1" 200 18519
127.0.0.1 - - [01/Apr/2014:16:00:35 -0400] "GET /wp-content/plugins/PluginName/admin/assets/css/admin.css?ver=0.1.1 HTTP/1.1" 304 -
127.0.0.1 - - [01/Apr/2014:16:00:35 -0400] "GET /wp-content/plugins/PluginName/admin/assets/js/admin.js?ver=0.1.1 HTTP/1.1" 304 -
127.0.0.1 - - [01/Apr/2014:16:00:38 -0400] "POST /wp-admin/options.php HTTP/1.1" 500 2958

Sia il file php-errors.log che il file debug.log quando WP_DEBUG è true sono vuoti.

The Plugin Class (/{plugin-name}-class.php)

namespace wp_plugin_name;

class Plugin_Name
{
    const VERSION = '1.1.2';
    const TABLE_VERSION = 1;
    const CHECK_UPDATE_DEFAULT = 1;
    const UPDATE_PERIOD_DEFAULT = 604800;

    protected $plugin_slug = 'pluginname-widget';
    protected $friendly_name = 'PluginName Widget';

    protected static $instance = null;

    private function __construct()
    {

        // Load plugin text domain
        add_action( 'init',
                    array(
            $this,
            'load_plugin_textdomain' ) );

        // Activate plugin when new blog is added
        add_action( 'wpmu_new_blog',
                    array(
            $this,
            'activate_new_site' ) );

        // Load public-facing style sheet and JavaScript.
        add_action( 'wp_enqueue_scripts',
                    array(
            $this,
            'enqueue_styles' ) );
        add_action( 'wp_enqueue_scripts',
                    array(
            $this,
            'enqueue_scripts' ) );

        /* Define custom functionality.
         * Refer To http://codex.wordpress.org/Plugin_API#Hooks.2C_Actions_and_Filters
         */

    }

    public function get_plugin_slug()
    {
        return $this->plugin_slug;
    }

    public function get_name()
    {
        return $this->friendly_name;
    }

    public static function get_instance()
    {

        // If the single instance hasn't been set, set it now.
        if ( null == self::$instance )
        {
            self::$instance = new self;
        }

        return self::$instance;

    }

    /**
     * The member functions activate(), deactivate(), and update() are very similar.
     * See the Boilerplate plugin for more details...
     *
     */

    private static function single_activate()
    {
        if ( !current_user_can( 'activate_plugins' ) )
            return;

        $plugin_request = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';

        check_admin_referer( "activate-plugin_$plugin_request" );

        /**
         *  Test to see if this is a fresh installation
         */
        if ( get_option( 'plugin-name_version' ) === false )
        {
            // Get the time as a Unix Timestamp, and add one week
            $unix_time_utc = time() + Plugin_Name::UPDATE_PERIOD_DEFAULT;

            add_option( 'plugin-name_version', Plugin_Name::VERSION );
            add_option( 'plugin-name_check_updates',
                        Plugin_Name::CHECK_UPDATE_DEFAULT );
            add_option( 'plugin-name_update_frequency',
                        Plugin_Name::UPDATE_PERIOD_DEFAULT );
            add_option( 'plugin-name_next_check', $unix_time_utc );

            // Create options table
            table_update();

            // Let user know PluginName was installed successfully
            is_admin() && add_filter( 'gettext', 'finalization_message', 99, 3 );
        }
        else
        {
            // Let user know PluginName was activated successfully
            is_admin() && add_filter( 'gettext', 'activate_message', 99, 3 );
        }

    }

    private static function single_update()
    {
        if ( !current_user_can( 'activate_plugins' ) )
            return;

        $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';

        check_admin_referer( "activate-plugin_{$plugin}" );

        $cache_plugin_version         = get_option( 'plugin-name_version' );
        $cache_table_version          = get_option( 'plugin-name_table_version' );
        $cache_deferred_admin_notices = get_option( 'plugin-name_admin_messages',
                                                    array() );

        /**
         * Find out what version of our plugin we're running and compare it to our
         * defined version here
         */
        if ( $cache_plugin_version > self::VERSION )
        {
            $cache_deferred_admin_notices[] = array(
                'error',
                "You seem to be attempting to revert to an older version of " . $this->get_name() . ". Reverting via the update feature is not supported."
            );
        }
        else if ( $cache_plugin_version === self::VERSION )
        {
            $cache_deferred_admin_notices[] = array(
                'updated',
                "You're already using the latest version of " . $this->get_name() . "!"
            );
            return;
        }

        /**
         * If we can't determine what version the table is at, update it...
         */
        if ( !is_int( $cache_table_version ) )
        {
            update_option( 'plugin-name_table_version', TABLE_VERSION );
            table_update();
        }

        /**
         * Otherwise, we'll just check if there's a needed update
         */
        else if ( $cache_table_version < TABLE_VERSION )
        {
            table_update();
        }

        /**
         * The table didn't need updating.
         * Note we cannot update any other options because we cannot assume they are still
         * the defaults for our plugin... ( unless we stored them in the db )
         */

    }

    private static function single_deactivate()
    {

        // Determine if the current user has the proper permissions
        if ( !current_user_can( 'activate_plugins' ) )
            return;

        // Is there any request data?
        $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';

        // Check if the nonce was valid
        check_admin_referer( "deactivate-plugin_{$plugin}" );

        // We'll, technically the plugin isn't included when deactivated so...
        // Do nothing

    }

    public function load_plugin_textdomain()
    {

        $domain = $this->plugin_slug;
        $locale = apply_filters( 'plugin_locale', get_locale(), $domain );

        load_textdomain( $domain,
                         trailingslashit( WP_LANG_DIR ) . $domain . '/' . $domain . '-' . $locale . '.mo' );
        load_plugin_textdomain( $domain, FALSE,
                                basename( plugin_dir_path( dirname( __FILE__ ) ) ) . '/languages/' );

    }

    public function activate_message( $translated_text, $untranslated_text,
                                      $domain )
    {
        $old = "Plugin <strong>activated</strong>.";
        $new = FRIENDLY_NAME . " was  <strong>successfully activated</strong> ";

        if ( $untranslated_text === $old )
            $translated_text = $new;

        return $translated_text;

    }

    public function finalization_message( $translated_text, $untranslated_text,
                                          $domain )
    {
        $old = "Plugin <strong>activated</strong>.";
        $new = "Captain, The Core is stable and PluginName was <strong>successfully installed</strong> and ready for Warp speed";

        if ( $untranslated_text === $old )
            $translated_text = $new;

        return $translated_text;

    }

}

Riferimenti:


La descrizione della taglia riporta: "Fornire alcune informazioni sulle migliori pratiche " . Usare singleton con costruttori privati ​​e un mucchio di azioni al loro interno: cattiva pratica e difficile da testare, non per colpa tua, comunque.
gmazzap

1
usa ../../options.php dopo aver testato il tuo codice.
ravi patel,

Puoi per favore mostrare get_plugin_slug ().
vancoder

@vancoder Ho modificato il post sopra con le informazioni pertinenti ...
gate_engineer

Perché ci sono barre rovesciate nei callback di sanificazione nelle impostazioni register_settings? Non penso che funzionerebbe.
Bjorn,

Risposte:


21

Bug "Errore: Pagina opzioni non trovata"

Questo è un problema noto nell'API delle impostazioni WP. C'era un biglietto aperto anni fa ed era contrassegnato come risolto, ma il bug persiste nelle ultime versioni di WordPress. Questo è ciò che la pagina (ora rimossa) del Codice ha detto a riguardo :

"Errore: pagina delle opzioni non trovata". problema (compresa una soluzione e spiegazione):

Il problema quindi è che il filtro "whitelist_options" non ha l'indice giusto per i tuoi dati. Viene applicato su options.php # 98 (WP 3.4).

register_settings()aggiunge i tuoi dati al globale $new_whitelist_options. Questo viene quindi unito al globale $whitelist_optionsall'interno del option_update_filter()(resp. add_option_whitelist()) Callback (s). Quei callback aggiungono i tuoi dati al globale $new_whitelist_optionscon l' $option_groupindice as. Quando si verifica "Errore: pagina delle opzioni non trovata". significa che il tuo indice non è stato riconosciuto. La cosa fuorviante è che il primo argomento viene usato come indice e nominato $options_group, quando si verifica il vero check in options.php # 112 $options_page, che è il $hook_suffixvalore da cui si ottiene come valore @return add_submenu_page().

In breve, una soluzione semplice è quella di $option_groupabbinare $option_name. Un'altra causa di questo errore è avere un valore non valido per il $pageparametro quando si chiama add_settings_section( $id, $title, $callback, $page )o add_settings_field( $id, $title, $callback, $page, $section, $args ).

Suggerimento: $pagedovrebbe corrispondere $menu_slugdalla pagina Riferimento funzioni / aggiungi tema.

Correzione semplice

Usando il nome della pagina personalizzata (nel tuo caso:) $this->plugin_slugcome ID della tua sezione si aggirerebbe il problema. Tuttavia, tutte le opzioni dovrebbero essere contenute in un'unica sezione.

Soluzione

Per una soluzione più solida, apporta queste modifiche alla tua Plugin_Name_Adminclasse:

Aggiungi al costruttore:

// Tracks new sections for whitelist_custom_options_page()
$this->page_sections = array();
// Must run after wp's `option_update_filter()`, so priority > 10
add_action( 'whitelist_options', array( $this, 'whitelist_custom_options_page' ),11 );

Aggiungi questi metodi:

// White-lists options on custom pages.
// Workaround for second issue: http://j.mp/Pk3UCF
public function whitelist_custom_options_page( $whitelist_options ){
    // Custom options are mapped by section id; Re-map by page slug.
    foreach($this->page_sections as $page => $sections ){
        $whitelist_options[$page] = array();
        foreach( $sections as $section )
            if( !empty( $whitelist_options[$section] ) )
                foreach( $whitelist_options[$section] as $option )
                    $whitelist_options[$page][] = $option;
            }
    return $whitelist_options;
}

// Wrapper for wp's `add_settings_section()` that tracks custom sections
private function add_settings_section( $id, $title, $cb, $page ){
    add_settings_section( $id, $title, $cb, $page );
    if( $id != $page ){
        if( !isset($this->page_sections[$page]))
            $this->page_sections[$page] = array();
        $this->page_sections[$page][$id] = $id;
    }
}

E il cambiamento add_settings_section()chiama: $this->add_settings_section().


Altre note sul tuo codice

  • Il tuo codice modulo è corretto. Il modulo deve essere inviato a options.php, come indicato da @Chris_O e come indicato nella documentazione dell'API delle impostazioni di WP .
  • Lo spazio dei nomi ha i suoi vantaggi, ma può rendere più complesso il debug e riduce la compatibilità del codice (richiede PHP> = 5.3, altri plugin / temi che utilizzano caricatori automatici, ecc.). Quindi, se non c'è una buona ragione per lo spazio dei nomi del tuo file, non farlo. Stai già evitando conflitti di denominazione racchiudendo il codice in una classe. Rendi i nomi delle tue classi più specifici e porta i tuoi validate()callback nella classe come metodi pubblici.
  • Confrontando il boilerplate del plug-in citato con il codice, sembra che il codice sia effettivamente basato su un fork o su una versione precedente del plateplate. Anche i nomi dei file e i percorsi sono diversi. È possibile migrare il plug-in all'ultima versione, ma si noti che questo plug-in della caldaia potrebbe non essere adatto alle proprie esigenze. Fa uso di singoli, che sono generalmente scoraggiati . Ci sono casi in cui il modello singleton è ragionevole , ma questa dovrebbe essere una decisione consapevole, non la soluzione goto.

1
È bello sapere che c'è un bug nell'API. Provo sempre a cercare nel codice che scrivo i bug che posso introdurre. Naturalmente, ciò presuppone che io conosca una cosa o due.
gate_engineer

Per chiunque incontri
maysi

5

Ho appena trovato questo post mentre cercavo lo stesso problema. La soluzione è molto più semplice di quanto sembri perché la documentazione è fuorviante: in register_setting () il primo argomento chiamato $option_groupè il tuo slug di pagina, non la sezione in cui si desidera visualizzare l'impostazione.

Nel codice sopra devi usare

    // Update Settings
    add_settings_section(
        'maintenance', // section slug
        'Maintenance', // section title
        array( $this, 'maintenance_section' ), // section display callback
        $this->plugin_slug // page slug
    );

    // Check Updates Option
    register_setting( 
        $this->plugin_slug, // page slug, not the section slug
        'plugin-name_check_updates', // setting slug
        'wp_plugin_name\validate_bool' // invalid, should be an array of options, see doc for more info
    );

    add_settings_field(
        'plugin-name_check_updates', // setting slug
        'Should ' . $this->friendly_name . ' Check For Updates?', // setting title
        array( $this, 'check_updates_field' ), //setting display callback
        $this->plugin_slug, // page slug
        'maintenance' // section slug
    );

Questo non è corretto Si prega di vedere questo esempio funzionante (non mio) - gist.github.com/annalinneajohansson/5290405
Xdg

2

Durante la registrazione della pagina delle opzioni con:

add_submenu_page( string $parent_slug, string $page_title, string $menu_title, string $capability, string $menu_slug, callable $function = '' )

E registrando le impostazioni con

register_setting( string $option_group, string $option_name );

$option_group dovrebbe essere uguale a $menu_slug


1

Ho avuto lo stesso errore ma l'ho preso in un modo diverso:

// no actual code
// this failed
add_settings_field('id','title', /*callback*/ function($arguments) {
    // echo $htmlcode; 
    register_setting('option_group', 'option_name');
}), 'page', 'section');

Non so perché sia ​​successo, ma sembra che register_settingnon dovrebbe essere nel callback diadd_settings_field

// no actual code
// this worked
add_settings_field('id','title', /*callback*/ function($arguments) {echo $htmlcode;}), 'page', 'section');
register_setting('option_group', 'option_name');

spero che questo possa essere d'aiuto


0

Ho riscontrato questo problema anche da alcuni giorni, questo errore si era interrotto quando ho inserito nei commenti la riga di:

// settings_fields($this->plugin_slug);

dopo di che sto reindirizzando a options.php ma non riesco ancora a risolvere il problema setting_fields.


l'ho risolto dalla funzione di validazione !! ;)
G.Karles,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.