“Erro: Página de opções não encontrada” na submissão da página de configurações para um complemento OOP

Estou desenvolvendo um plugin usando o repository Boilerplate de Tom McFarlin como modelo, que utiliza práticas de OOP. Eu tentei descobrir exatamente por que não posso enviar minhas configurações corretamente. Eu tentei configurar o atributo de ação em uma string vazia, como sugerido em outra pergunta aqui, mas isso não ajudou …

Abaixo está a configuração de código geral que estou usando …

O Formulário (/views/admin.php):

plugin_slug ); do_settings_sections( $this->plugin_slug ); submit_button( 'Save Settings' ); ?>

Para o seguinte código, assuma todas as devoluções de chamada para add_settings_field () e add_settings_section (), exceto para ‘option_list_selection’.

A Classe Admin do Plugin (/ {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 ); } } 

Algumas atualizações solicitadas:

Alterando o atributo de ação para:

 

… simplesmente resulta em um erro 404. O seguinte abaixo é o trecho dos logs do Apache. Observe que os scripts WordPress padrão e CSS en-filas são removidos:

 # 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 

Tanto o arquivo php-errors.log quanto o arquivo debug.log quando WP_DEBUG são verdadeiros estão vazios.

A Classe do Plugin (/{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 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 activated."; $new = FRIENDLY_NAME . " was successfully activated "; if ( $untranslated_text === $old ) $translated_text = $new; return $translated_text; } public function finalization_message( $translated_text, $untranslated_text, $domain ) { $old = "Plugin activated."; $new = "Captain, The Core is stable and PluginName was successfully installed and ready for Warp speed"; if ( $untranslated_text === $old ) $translated_text = $new; return $translated_text; } }

Referências:

  • A API de Configurações

    • add_settings_section ()
    • add_settings_field ()
    • register_setting ()
  • Criando páginas de opções

Solutions Collecting From Web of "“Erro: Página de opções não encontrada” na submissão da página de configurações para um complemento OOP"

“Erro: página de opções não encontrada” Bug

Este é um problema conhecido na API de configurações do WP. Havia um bilhete aberto há anos, e foi marcado como resolvido – mas o bug persiste nas últimas versões do WordPress. Isto é o que a página do Codex (agora removida) falou sobre isso :

A página “Erro: opções não encontradas”. problema (incluindo uma solução e explicação):

O problema, então, é que o filtro ‘whitelist_options’ não obteve o índice certo para seus dados. Ele é aplicado nas opções.php # 98 (WP 3.4).

register_settings() adiciona seus dados às $new_whitelist_options globais. Isso então é mesclado com as option_update_filter() $whitelist_options globais dentro da option_update_filter() (resp. add_option_whitelist() ) callback (s). Esses callbacks adicionam seus dados às $new_whitelist_options globais com o $option_group como índice. Quando você encontrar “Erro: página de opções não encontrada”. Isso significa que seu índice não foi reconhecido. O argumento enganador é que o primeiro argumento é usado como índice e nomeado $options_group , quando a verificação real em options.php # 112 acontece contra $options_page , que é o $hook_suffix , que você obtém como @return value from add_submenu_page() .

Em suma, uma solução fácil é fazer $option_group match $option_name . Outra causa para este erro é ter um valor inválido para o parâmetro $page ao chamar add_settings_section( $id, $title, $callback, $page ) ou add_settings_field( $id, $title, $callback, $page, $section, $args ) .

Dica: $page deve coincidir com $menu_slug da Referência de Função / adicionar a página do tema.

Fixação simples

Usando o nome da página personalizada (no seu caso: $this->plugin_slug ), pois o ID da seção resolveria o problema. No entanto, todas as suas opções deveriam estar contidas em uma única seção.

Solução

Para uma solução mais robusta, faça essas alterações em sua class Plugin_Name_Admin :

Adicionar ao construtor:

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

Adicione estes methods:

 // 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 mude as add_settings_section() para: $this->add_settings_section() .


Outras notas no seu código

  • Seu código de formulário está correto. Seu formulário deve enviar para options.php, como me foi indicado por @Chris_O, e como indicado na documentação da WP Settings API.
  • O Namespacing tem vantagens, mas pode torná-lo mais complexo para depurar e reduz a compatibilidade do seu código (requer PHP> = 5.3, outros plugins / temas que utilizam carregadores automáticos, etc.). Então, se não há boas razões para o namespace do seu arquivo, não. Você já está evitando conflitos de nomeação envolvendo seu código em uma class. Faça os nomes das suas classs mais específicos e traga suas devoluções de validate() para a class como methods públicos.
  • Comparando o seu boilerplate de plugin citado com o seu código, parece que seu código está baseado em um garfo ou uma versão antiga do boilerplate. Mesmo os nomes dos arquivos e os caminhos são diferentes. Você pode migrar o seu plug-in para a versão mais recente, mas observe que este boilerplate do plugin pode não ser adequado às suas necessidades. Faz uso de singletons, que geralmente são desencorajados . Há casos em que o padrão singleton é sensível , mas esta deve ser uma decisão consciente, e não a solução goto.

Acabei de encontrar esta publicação enquanto procurava o mesmo problema. A solução é muito mais simples do que parece, porque a documentação é enganosa: no register_setting (), o primeiro argumento chamado $option_group é o slug da página, e não a seção na qual deseja exibir a configuração.

No código acima, você deve usar

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