OOP 플러그인의 설정 페이지 제출에서“오류 : 옵션 페이지를 찾을 수 없음”


19

Tom McFarlin의 Boilerplate 저장소를 템플릿으로 사용하여 플러그인을 개발 중이며 OOP 사례를 활용합니다. 설정을 올바르게 제출할 수없는 이유를 정확히 파악하려고 노력했습니다. 여기 주변의 다른 질문에서 제안한 것처럼 action 속성을 빈 문자열로 설정하려고 시도했지만 도움이되지 않았습니다 ...

아래는 내가 사용하는 일반적인 코드 설정입니다 ...

양식 (/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_list_selection'을 제외하고 add_settings_field () 및 add_settings_section ()에 대한 모든 콜백이 존재한다고 가정하십시오.

플러그인 관리 클래스 (/{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 로그에서 발췌 한 것입니다. 기본 WordPress 스크립트 및 CSS 대기열이 제거됩니다.

# 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

WP_DEBUG가 true 일 때 php-errors.log 파일과 debug.log 파일이 모두 비어 있습니다.

플러그인 클래스 (/{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

"오류 : 옵션 페이지를 찾을 수 없음"버그

이것은 WP 설정 API에서 알려진 문제 입니다. 있었다 티켓은 오픈 년 전, 그리고 해결로는 표시되었다 -하지만 워드 프레스의 최신 버전에서 버그 지속됩니다. 이것은 (현재 제거 된) 코덱스 페이지가 이것에 대해 말한 것입니다 .

"오류 : 옵션 페이지를 찾을 수 없습니다." 문제 (해결 방법 및 설명 포함) :

문제는 'whitelist_options'필터가 데이터에 적합한 인덱스를 얻지 못했다는 것입니다. options.php # 98 (WP 3.4)에 적용됩니다.

register_settings()데이터를 전역에 추가합니다 $new_whitelist_options. 그런 다음 (resp. ) 콜백 $whitelist_options내부 의 전역과 병합됩니다 . 이러한 콜백 은 as 인덱스 를 사용하여 데이터를 전역에 추가합니다 . "오류 : 옵션 페이지를 찾을 수 없습니다."가 발생하는 경우 색인이 인식되지 않았 음을 의미합니다. 오해 일이 첫 번째 인수가 인덱스로 사용이라는 것입니다 때 # 112에 발생 options.php의 실제 검사 는 IS, 당신은에서 @return 값으로 얻을 .option_update_filter()add_option_whitelist()$new_whitelist_options$option_group$options_group$options_page$hook_suffixadd_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().


코드에 대한 기타 참고 사항

  • 양식 코드가 정확합니다. @Chris_O가 지적하고 WP 설정 API 설명서에 표시된대로 양식을 options.php에 제출해야합니다. .
  • 네임 스페이스는 장점이 있지만 디버깅하기가 더 복잡해지고 코드 호환성이 떨어질 수 있습니다 (PHP> = 5.3, 오토로더를 사용하는 다른 플러그인 / 테마 등 필요). 따라서 파일을 네임 스페이스 화해야 할 이유가 없다면 그렇게하지 마십시오. 클래스에 코드를 배치하여 이름 충돌을 이미 피하고 있습니다. 수업 이름을 좀 더 구체적으로 만들고validate() 콜백을 공개 메소드로 클래스에 .
  • 인용 된 플러그인 상용구 를 코드와 비교하면 코드가 실제로 상용구의 포크 또는 이전 버전을 기반으로하는 것처럼 보입니다. 파일 이름과 경로도 다릅니다. 플러그인을 최신 버전으로 마이그레이션 할 수 있지만이 플러그인 상용구가 사용자의 요구에 맞지 않을 수 있습니다. 일반적으로 사용 하지 않는 싱글 톤을 사용 합니다. 싱글 톤 패턴이 합리적인 경우가 있지만, 이것은 고토 솔루션이 아니라 의식적인 결정이어야합니다.

1
API에 버그가 있음을 아는 것이 좋습니다. 나는 항상 내가 소개 할 수있는 버그에 대해 작성한 코드를 살펴 보려고한다. 물론, 그것은 내가 한두 가지를 알고 있다고 가정합니다.
gate_engineer


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.