"Fehler: Optionsseite nicht gefunden" auf der Seite "Einstellungen" für ein OOP-Plugin

19

Ich entwickle ein Plugin, das das Boilerplate- Repository von Tom McFarlin als Vorlage verwendet und OOP-Praktiken verwendet. Ich habe versucht, genau herauszufinden, warum ich meine Einstellungen nicht korrekt übermitteln kann. Ich habe versucht, das Aktionsattribut auf eine leere Zeichenfolge zu setzen, wie in einer anderen Frage hier vorgeschlagen, aber das hat nicht geholfen ...

Unten ist das allgemeine Code-Setup, das ich verwende ...

Das Formular (/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>

Nehmen Sie für den folgenden Code an, dass alle Rückrufe für add_settings_field () und add_settings_section () vorhanden sind, mit Ausnahme von 'option_list_selection'.

Die Plugin Admin Klasse (/ {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
        );
    }
}

Einige angeforderte Updates:

Ändern des Aktionsattributs in:

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

... führt einfach zu einem 404-Fehler. Das Folgende ist der Auszug aus den Apache-Protokollen. Beachten Sie, dass die Standard-WordPress-Skripte und CSS-Warteschlangen entfernt werden:

# 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

Sowohl die Datei php-errors.log als auch die Datei debug.log sind leer, wenn WP_DEBUG auf true gesetzt ist.

Die Plugin Klasse (/{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;

    }

}

Verweise:

gate_engineer
quelle
In der Kopfgeldbeschreibung heißt es: "Bitte geben Sie einige Informationen zu bewährten Methoden " . Verwenden von Singletons mit privaten Konstruktoren und einer Reihe von Aktionen in ihnen: schlechte Praxis und schwer zu testen, jedoch nicht deine Schuld.
gmazzap
1
benutze ../../options.php nachdem du deinen Code getestet hast.
Ravi Patel
Können Sie bitte get_plugin_slug () anzeigen.
Vancoder
@vancoder Ich habe den obigen Beitrag mit den relevanten Informationen bearbeitet ...
gate_engineer
Warum gibt es Backslashes in Ihren Desinfektionsrückrufen in Ihren register_settings? Ich denke nicht, dass das funktionieren würde.
Bjorn

Antworten:

21

"Fehler: Optionsseite nicht gefunden" Fehler

Dies ist ein bekanntes Problem in der WP-Einstellungs-API. Es gab ein Ticket, das vor Jahren eröffnet wurde und als gelöst markiert wurde - aber der Fehler bleibt in den neuesten Versionen von WordPress bestehen. Dies ist, was die (jetzt entfernte) Codex-Seite dazu sagte :

Die Seite "Fehler: Optionen nicht gefunden" Problem (einschließlich einer Lösung und Erklärung):

Das Problem ist dann, dass der Filter 'whitelist_options' nicht den richtigen Index für Ihre Daten hat. Es wird auf options.php # 98 (WP 3.4) angewendet.

register_settings()fügt Ihre Daten dem globalen hinzu $new_whitelist_options. Dieser wird dann mit dem Global $whitelist_optionsinnerhalb des option_update_filter()(bzw. der add_option_whitelist()) Callbacks zusammengeführt. Diese Rückrufe fügen Ihre Daten dem globalen $new_whitelist_optionsmit dem $option_groupIndex as hinzu. Wenn Sie auf "Fehler: Optionsseite nicht gefunden" stoßen Es bedeutet, dass Ihr Index nicht erkannt wurde. Das irreführende ist, dass das erste Argument als Index verwendet und benannt wird $options_group, wenn das eigentliche Einchecken in options.php # 112 erfolgt $options_page, wogegen das ist $hook_suffix, was Sie als @return-Wert erhalten add_submenu_page().

Kurz gesagt, eine einfache Lösung besteht darin, eine $option_groupÜbereinstimmung zu erzielen $option_name. Eine weitere Ursache für diesen Fehler ist ein ungültiger Wert für $pageparameter, wenn entweder add_settings_section( $id, $title, $callback, $page )oder aufgerufen wird add_settings_field( $id, $title, $callback, $page, $section, $args ).

Hinweis: $pageSollte mit $menu_slugder Funktionsreferenz übereinstimmen / Themenseite hinzufügen.

Einfaches Fix

Die Verwendung des benutzerdefinierten Seitennamens (in Ihrem Fall $this->plugin_slug:) als Abschnitts-ID würde das Problem umgehen. Alle Ihre Optionen müssten jedoch in einem einzigen Abschnitt enthalten sein.

Lösung

Nehmen Sie für eine robustere Lösung die folgenden Änderungen in Ihrer Plugin_Name_AdminKlasse vor:

Zum Konstruktor hinzufügen:

// 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 );

Fügen Sie diese Methoden hinzu:

// 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;
    }
}

Und Veränderung add_settings_section()ruft: $this->add_settings_section().


Weitere Hinweise zu Ihrem Code

  • Ihr Formularcode ist korrekt. Ihr Formular muss an options.php gesendet werden, wie mir von @Chris_O mitgeteilt und in der Dokumentation zur API für WP-Einstellungen angegeben .
  • Namespacing hat seine Vorteile, kann aber das Debuggen komplexer machen und die Kompatibilität Ihres Codes verringern (erfordert PHP> = 5.3, andere Plugins / Themes, die Autoloader usw. verwenden). Wenn es keinen guten Grund gibt, einen Namespace für Ihre Datei zu erstellen, tun Sie dies nicht. Sie vermeiden bereits Namenskonflikte, indem Sie Ihren Code in eine Klasse einschließen. Machen Sie Ihre Klassennamen spezifischer und bringen Sie Ihre validate()Rückrufe als öffentliche Methoden in die Klasse.
  • Vergleicht man Ihr zitiertes Plugin-Boilerplate mit Ihrem Code, sieht es so aus, als würde Ihr Code tatsächlich von einer Gabel oder einer alten Version des Boilerplates ausgehen. Sogar die Dateinamen und Pfade sind unterschiedlich. Sie können Ihr Plugin auf die neueste Version migrieren. Beachten Sie jedoch, dass dieses Plugin-Boilerplate möglicherweise nicht Ihren Anforderungen entspricht. Es werden Singletons verwendet, von denen generell abgeraten wird . Es gibt Fälle, in denen das Singleton-Muster sinnvoll ist , aber dies sollte eine bewusste Entscheidung sein, nicht die Lösung.
Stephen M. Harris
quelle
1
Es ist schön zu wissen, dass es einen Fehler in der API gibt. Ich versuche immer, den Code, den ich schreibe, nach Fehlern zu durchsuchen, die ich einführen kann. Das setzt natürlich voraus, dass ich ein oder zwei Dinge weiß.
gate_engineer
Für alle, die auf dieses Problem stoßen
Sie sich
5

Ich habe diesen Beitrag gerade gefunden, als ich nach dem gleichen Thema gesucht habe. Die Lösung ist viel einfacher, als es aussieht, da die Dokumentation irreführend ist: In register_setting () ist das erste genannte Argument $option_groupIhr Seiten-Slug, nicht der Abschnitt, in dem Sie die Einstellung anzeigen möchten.

Im obigen Code sollten Sie verwenden

    // 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
    );
86Dev
quelle
Das ist nicht richtig. Bitte sehen Sie dieses Arbeitsbeispiel (nicht meins) - gist.github.com/annalinneajohansson/5290405
Xdg
2

Während der Registrierung der Optionsseite mit:

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

Und registrieren von Einstellungen mit

register_setting( string $option_group, string $option_name );

$option_group sollte so sein wie $menu_slug

Cafer Elgin
quelle
1

Ich hatte den gleichen Fehler, habe ihn aber auf eine andere Weise erhalten:

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

Ich weiß nicht, warum das passiert ist, aber es scheint, dass register_settingdas nicht im Rückruf von sein sollteadd_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');

ich hoffe das hilft

Eine neue 1
quelle
0

Ich habe dieses Problem auch seit einigen Tagen konfrontiert, dieser Fehler hatte aufgehört, als ich in Kommentare die Zeile von:

// settings_fields($this->plugin_slug);

danach leite ich auf options.php um, aber ich kann das Problem von setting_fieldsnoch nicht lösen .

G.Karles
quelle
Ich habe es aus der Validierungsfunktion behoben !! ;)
G.Karles