提交OOP插件的设置页面时出现错误:找不到选项页面

时间:2014-03-30 作者:gate_engineer

我正在开发一个插件,使用Tom McFarlin\'s Boilerplate 存储库作为模板,它利用OOP实践。我一直在试图弄清楚为什么我无法正确提交设置。我已经尝试将action属性设置为一个空字符串,正如这里的另一个问题所建议的那样,但这没有帮助。。。

下面是我正在使用的常规代码设置。。。

The Form (/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>
对于下面的代码,假设除了“option\\u list\\u selection”之外,add\\u settings\\u field()和add\\u settings\\u section()的所有回调都存在。

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

Some Requested Updates:

将操作属性更改为:

<form action="../../options.php" method="post">
。。。只会导致404错误。下面是Apache日志的摘录。请注意,默认WordPress脚本和CSS en队列已删除:

# 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
这两个php错误。日志文件和调试。WP\\u DEBUG为true时的日志文件为空。

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;

    }

}

References:

5 个回复
最合适的回答,由SO网友:Stephen M. Harris 整理而成

“错误:未找到选项页”错误known issue 在WP设置API中。有a ticket 多年前打开,并被标记为已解决,但该错误仍然存在于最新版本的WordPress中。这是什么the (now removed) Codex page said about this:

“错误:找不到选项页。”问题(包括解决方案和解释):

问题是,“whitelist\\u options”过滤器没有为您的数据建立正确的索引。它应用于选项。php#98(WP 3.4)。

register_settings() 将数据添加到全局$new_whitelist_options. 然后将其与全局$whitelist_options 内部option_update_filter() (分别为。add_option_whitelist()) 回调。这些回调将您的数据添加到全局$new_whitelist_options 使用$option_group 作为索引。当您遇到“错误:找不到选项页”这意味着您的索引尚未被识别。令人误解的是,第一个参数被用作索引并命名为$options_group, 当实际签入选项时。php#112发生在$options_page, 哪个是$hook_suffix, 您从中获得@返回值add_submenu_page().

简而言之,一个简单的解决方案是$option_group 火柴$option_name. 此错误的另一个原因是的值无效$page 调用add_settings_section( $id, $title, $callback, $page )add_settings_field( $id, $title, $callback, $page, $section, $args ).

提示:$page 应匹配$menu_slug 从功能参考/添加主题页面。

使用自定义页面名称进行简单修复(在您的情况下:$this->plugin_slug) 因为你的部门id可以绕过这个问题。但是,您的所有选项都必须包含在单个部分中。

解决方案要获得更强健的解决方案,请对Plugin_Name_Admin 类别:

添加到构造函数:

// 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 );
添加以下方法:

// 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;
    }
}
和更改add_settings_section() 致电:$this->add_settings_section().

代码上的其他注释您的表单代码是正确的。您的表单必须提交到选项。php,正如@Chris\\u O向我指出的,以及WP设置API中指出的documentation.=5.3、其他使用自动加载程序的插件/主题等)。因此,如果没有很好的理由对文件命名,就不要这样做。通过将代码包装到类中,已经可以避免命名冲突。使您的类名更加具体,并validate() 作为公共方法回调到类中plugin boilerplate 对于您的代码,看起来您的代码实际上是基于fork或旧版本的样板文件。甚至文件名和路径也不同。您可以将插件迁移到最新版本,但请注意,此插件样板文件可能不适合您的需要。它使用单例,通常discouraged. 有些情况下,单例模式是sensible, 但这应该是有意识的决定,而不是goto解决方案

SO网友:86Dev

我只是在寻找同样的问题时发现了这篇文章。解决方案比看起来要简单得多,因为文档具有误导性:在register_setting() 名为的第一个参数$option_group 是页面slug,而不是要在其中显示设置的部分。

在上面的代码中,您应该使用

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

SO网友:Cafer Elgin

注册选项页面时:

add_submenu_page( string $parent_slug, string $page_title, string $menu_title, string $capability, string $menu_slug, callable $function = \'\' )
并将设置注册到

register_setting( string $option_group, string $option_name );
$option_group 应与相同$menu_slug

SO网友:A new 1

我也有同样的错误,但有不同的方法:

// no actual code
// this failed
add_settings_field(\'id\',\'title\', /*callback*/ function($arguments) {
    // echo $htmlcode; 
    register_setting(\'option_group\', \'option_name\');
}), \'page\', \'section\');
我不知道为什么会这样,但看起来register_setting 不应该在的回调中add_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\');
我希望这有帮助

SO网友:G.Karles

我也已经面临这个问题好几天了,当我在评论行中输入以下内容时,这个错误已经停止了:

// settings_fields($this->plugin_slug);
之后,我将重定向到options.php 但我无法解决setting_fields 然而

结束

相关推荐

如何在wp-Options中删除数组中的孤立用户数据

我有一个选项,订阅论坛电子邮件通知的成员。电子邮件列表作为选项值位于一个数组中。一切正常,所有订阅和取消订阅都使用update_option(); 我的问题是,如果一个用户被删除,不进入数据库就很难取消他的订阅。所以我认为这个函数可以做到这一点,但我肯定遗漏了什么:add_action( \'delete_user\', \'forum_remove_deleted_user\' ); function forum_remove_deleted_user($user_id) {