问题描述
我正在开发一个使用 Tom McFarlin’s 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>
对于以下代码,假设存在 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_namevalidate_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_namevalidate_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
);
}
}
一些请求的更新:
将 action 属性更改为:
<form action="../../options.php" method="post">
只是导致 404 错误。以下是 Apache 日志的摘录。请注意,默认的 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
当 WP_DEBUG 为真时,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;
}
}
参考文献:
最佳解决方案
「错误:选项页面未找到」 错误
这是 WP Settings API 中的 known issue 。几年前开了 a ticket,它被标记为已解决 – 但是 BugPress 仍然存在于最新版本的 WordPress 中。
简单修复
使用自定义页面名称 (在您的情况下:$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()
。
您的代码的其他注意事项
-
您的表单代码是正确的。您的表单必须提交到 options.php,由 @Chris_O 指出,并且如 WP Settings API documentation 所示。
-
命名空间具有优势,但它可以使调试更加复杂,并降低代码的兼容性 (需要 PHP> = 5.3,使用自动加载程序的其他插件/主题等) 。所以如果没有很好的理由来命名你的文件,不要。您已经通过将代码封装在类中来避免命名冲突。使您的类名更具体,并将您的
validate()
回调作为公共方法进入类。 -
将您引用的 plugin boilerplate 与您的代码进行比较,您的代码实际上基于叉或旧版本的样板。即使文件名和路径也不同。您可以将插件迁移到最新版本,但请注意,此插件样板可能不适合您的需要。它使用单体,通常是 discouraged 。有些情况下单身人格模式是 sensible,但这应该是有意识的决定,而不是 goto 解决方案。
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。