“ ข้อผิดพลาด: ไม่พบหน้าตัวเลือก” ในการส่งหน้าการตั้งค่าสำหรับปลั๊กอิน OOP


19

ฉันกำลังพัฒนาปลั๊กอินโดยใช้ที่เก็บข้อมูลBoilerplate ของ Tom McFarlinเป็นแม่แบบซึ่งใช้วิธีปฏิบัติของ OOP ฉันพยายามหาสาเหตุที่แน่ชัดว่าทำไมฉันจึงไม่สามารถส่งการตั้งค่าของฉันได้อย่างถูกต้อง ฉันได้ลองตั้งค่าแอ็ตทริบิวต์การกระทำเป็นสตริงว่างตามที่แนะนำในคำถามอื่นรอบ ๆ ที่นี่ แต่นั่นไม่ได้ช่วย ...

ด้านล่างเป็นการตั้งค่ารหัสทั่วไปที่ฉันใช้ ...

แบบฟอร์ม (/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>

สำหรับรหัสต่อไปนี้ให้ถือว่าการเรียกกลับทั้งหมดสำหรับ add_settings_field () และ add_settings_section () มีอยู่ยกเว้น 'option_list_selection'

คลาสผู้ดูแลระบบปลั๊กอิน (/ {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
        );
    }
}

การปรับปรุงที่ร้องขอบางอย่าง:

การเปลี่ยนแอตทริบิวต์การกระทำเป็น:

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

... เพียงส่งผลให้เกิดข้อผิดพลาด 404 ต่อไปนี้เป็นข้อความที่ตัดตอนมาจาก Apache Logs โปรดทราบว่าสคริปต์ WordPress และ CSS en-queues เริ่มต้นจะถูกลบออก:

# 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-errors.log และไฟล์ debug.log เมื่อ WP_DEBUG เป็นจริงว่างเปล่า

คลาสปลั๊กอิน (/{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;

    }

}

อ้างอิง:


คำอธิบายรางวัลรายงาน: "โปรดให้ข้อมูลบางอย่างเกี่ยวกับการปฏิบัติที่ดีที่สุด " การใช้ซิงเกิลตันกับคอนสตรัคเตอร์ส่วนตัวและการกระทำภายใน: การฝึกฝนที่ไม่ดีและยากที่จะทดสอบไม่ใช่ความผิดของคุณ
gmazzap

1
ใช้ ../../options.php หลังจากทดสอบโค้ดของคุณ
ravi patel

คุณกรุณาแสดงให้ get_plugin_slug () ได้ไหม
vancoder

@vancoder ฉันได้แก้ไขโพสต์ด้านบนด้วยข้อมูลที่เกี่ยวข้อง ...
gate_engineer

เหตุใดจึงมีเครื่องหมายแบ็กสแลชในการเรียกกลับด้านการฆ่าเชื้อใน register_settings ของคุณ ฉันไม่คิดว่ามันจะได้ผล
Bjorn

คำตอบ:


21

ข้อผิดพลาด "ไม่พบหน้าตัวเลือก"

นี่เป็นปัญหาที่ทราบใน API การตั้งค่า WP มีตั๋วเปิดหลายปีมาแล้วและมันถูกทำเครื่องหมายว่าแก้ไขแล้ว แต่ข้อผิดพลาดยังคงอยู่ใน WordPress รุ่นล่าสุด นี่คือสิ่งที่หน้า Codex (ลบออกแล้ว) พูดเกี่ยวกับสิ่งนี้ :

ไม่พบหน้าตัวเลือก "ข้อผิดพลาด: ตัวเลือก" ปัญหา (รวมถึงทางออกและคำอธิบาย):

ปัญหาคือว่าตัวกรอง 'whitelist_options' ไม่มีดัชนีที่เหมาะสมสำหรับข้อมูลของคุณ มันจะถูกนำไปใช้กับ options.php # 98 (WP 3.4)

register_settings()$new_whitelist_optionsเพิ่มข้อมูลของคุณไปทั่วโลก สิ่งนี้จะถูกรวมเข้ากับโกลบอล $whitelist_optionsภายในการเรียกกลับ ( option_update_filter()resp. add_option_whitelist()) การเรียกกลับเหล่านั้นเพิ่มข้อมูลของคุณไปยังทั่วโลก$new_whitelist_optionsด้วย$option_groupดัชนี เมื่อคุณพบ "ข้อผิดพลาด: ไม่พบหน้าตัวเลือก" หมายความว่าดัชนีของคุณไม่ได้รับการยอมรับ สิ่งที่ทำให้เข้าใจผิดว่าเป็นอาร์กิวเมนต์แรกจะใช้เป็นดัชนีและการตั้งชื่อ$options_groupเมื่อตรวจสอบที่เกิดขึ้นจริงใน options.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ของคุณ:) เป็นรหัสส่วนของคุณจะแก้ไข อย่างไรก็ตามตัวเลือกทั้งหมดของคุณจะต้องอยู่ในส่วนเดียว

วิธีการแก้

สำหรับโซลูชันที่มีประสิทธิภาพมากขึ้นทำการเปลี่ยนแปลงใน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()


หมายเหตุอื่น ๆ เกี่ยวกับรหัสของคุณ

  • รหัสฟอร์มของคุณถูกต้อง รูปแบบของคุณมีการส่งไปยัง options.php เป็นแหลมออกมาให้ฉันโดย @Chris_O และตามที่ระบุไว้ใน WP API การตั้งค่าเอกสาร
  • การกำหนดเนมสเปซมีข้อดี แต่สามารถทำให้การดีบักมีความซับซ้อนมากขึ้นและลดความเข้ากันได้ของรหัสของคุณ (ต้องใช้ PHP> = 5.3, ปลั๊กอิน / ชุดรูปแบบอื่น ๆ ที่ใช้ตัวโหลดอัตโนมัติเป็นต้น) ดังนั้นหากไม่มีเหตุผลที่ดีในการกำหนดไฟล์ของคุณ คุณกำลังหลีกเลี่ยงความขัดแย้งในการตั้งชื่อโดยใส่รหัสของคุณไว้ในคลาส ทำให้ชื่อคลาสของคุณเฉพาะเจาะจงมากขึ้นและนำการvalidate()เรียกกลับของคุณเข้าสู่ชั้นเรียนเป็นวิธีสาธารณะ
  • เมื่อเปรียบเทียบการอ้างถึงปลั๊กอินสำเร็จรูปของคุณกับโค้ดดูเหมือนว่ารหัสของคุณนั้นอิงจากทางแยกหรือไอเท็มรุ่นเก่า แม้ชื่อไฟล์และเส้นทางจะแตกต่างกัน คุณสามารถย้ายปลั๊กอินไปเป็นเวอร์ชันล่าสุดได้ แต่โปรดทราบว่าปลั๊กอินสำเร็จรูปนี้อาจไม่เหมาะกับความต้องการของคุณ ทำให้การใช้ singletons ซึ่งมักจะท้อแท้ มีหลายกรณีที่รูปแบบของซิงเกิลนั้นมีความสมเหตุสมผลแต่นี่ควรเป็นการตัดสินใจที่มีสติไม่ใช่คำตอบของ goto

1
ยินดีที่ได้ทราบว่ามีข้อผิดพลาดใน API ฉันพยายามตรวจสอบรหัสที่ฉันเขียนเพื่อหาบั๊กที่อาจแนะนำ แน่นอนว่าฉันรู้เรื่องหนึ่งหรือสองอย่าง
gate_engineer

สำหรับทุกคนที่เจอปัญหานี้: ดูตัวอย่าง OOP ใน codex: codex.wordpress.org/Creating_Options_Pages#Example_.232
maysi

5

ฉันเพิ่งพบโพสต์นี้ในขณะที่ค้นหาปัญหาเดียวกัน วิธีการแก้ปัญหานั้นง่ายกว่าที่คิดเพราะเอกสารคู่มือมีการเข้าใจผิด: ในregister_setting ()อาร์กิวเมนต์แรกที่ชื่อว่า$option_groupคือหน้ากระสุนของคุณไม่ใช่ส่วนที่คุณต้องการแสดงการตั้งค่า

ในรหัสข้างต้นคุณควรใช้

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

สิ่งนี้ไม่ถูกต้อง โปรดดูตัวอย่างการทำงานนี้ (ไม่ใช่ของฉัน) - gist.github.com/annalinneajohansson/5290405
Xdg

2

ในขณะที่ลงทะเบียนหน้าตัวเลือกด้วย:

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


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

ฉันหวังว่านี่จะช่วยได้


0

ฉันเคยเจอปัญหานี้มาหลายวันแล้วข้อผิดพลาดนี้ก็หยุดลงเมื่อฉันแสดงความคิดเห็นในบรรทัดของ:

// settings_fields($this->plugin_slug);

หลังจากนั้นฉันเปลี่ยนเส้นทางไปที่options.phpแต่ฉันยังไม่สามารถแก้ปัญหาsetting_fieldsได้


ฉันแก้ไขมันจากฟังก์ชั่นการตรวจสอบ !! ;)
G.Karles
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.