通常情况下,插件和主题的作者都需要为其提供一个配置界面,以方便用户去自定义它们。将这个配置界面展示给用户的最好方式,就是在后台管理员界面的配置菜单中添加一个选项,然后当用户点击这个选项的时候再去打开它。这篇文章将会告诉你如何去实现这样的事情。
注意:在继续阅读之前,你应当对如何写一个插件,以及Actions和Filters的插件API函数有一定的了解。
参考函数
一般函数
菜单页面
- add_menu_page()
- add_object_page()
- add_utility_page()
- remove_menu_page()
子菜单页面
- add_submenu_page()
- remove_submenu_page()
WordPress后台菜单
- add_dashboard_page()
- add_posts_page()
- add_media_page()
- add_links_page()
- add_pages_page()
- add_comments_page()
- add_theme_page()
- add_plugins_page()
- add_users_page()
- add_management_page()
- add_options_page()
一切皆需钩子
想要向管理员界面的配置菜单中添加一个选项,你必须要完成三件事:
定义一个函数,使其能够创建菜单选项,并调出配置界面
将这个函数挂载到名为admin_menu的Action钩子上。(如果你需要创建的是与网络相关的菜单选项,那么就需要将该函数挂载到network_admin_menu钩子上)。
编写当用户点击菜单选项时,需要显示出的配置页面的内容。
第二步是新手们往往会忽略掉的步骤。必须要记住:光写出代码是不够的,你还必须要把这段代码放到一个函数中,并且把这个函数挂载到相应的钩子上,它才能发挥相应的作用。
以下是包含这三个步骤的简单示例。这个插件将会在设置菜单的主选项下创建一个子选项,当该选项被点击时,将会显示出一个非常简单的页面。
注意:这些代码应当被写到PHP主文件中,或是一个单独的PHP文件中。
<?php /** 第1步:定义添加菜单选项的函数 */ function my_plugin_menu() { add_options_page( 'My Plugin Options', 'My Plugin', 'manage_options', 'my-unique-identifier', 'my_plugin_options' ); } /** 第2步:将函数注册到钩子中 */ add_action( 'admin_menu', 'my_plugin_menu' ); /** 第3步:定义选项被点击时打开的页面 */ function my_plugin_options() { if ( !current_user_can( 'manage_options' ) ) { wp_die( __( 'You do not have sufficient permissions to access this page.' ) ); } echo '<div class="wrap">'; echo '<p>Here is where the form would go if I actually had options.</p>'; echo '</div>'; } ?>
在第1步中,my_plugin_menu()函数的作用是通过调用add_options_page()函数,向管理员界面的配置菜单中添加一个新的菜单选项(关于更复杂的多级选项,将在之后进行详述)。
在第2步中,add_action()函数的作用就是将第1步中定义的my_plugin_menu()函数,挂载到名为admin_menu的钩子下。此时如果忘了调用add_action()函数的话,那么在你试图激活这个插件时就会提示’undefined function’错误。
在最后一步中,my_plugin_options()函数的作用就是显示一个配置页面,并显示出用户可配置的内容(可以由PHP的代码编写),这个函数也是在第1步中的add_action()函数中被调用的。这样当用户点击菜单选项的时候,就会弹出这个配置页面。
在下面的章节中,将会对这些步骤进行更详细的说明。再重复一遍:切记要把创建菜单选项的代码放到函数中!并且记得将函数挂载到admin_menu钩子中!这样你的整个代码流程才能正确的运行。
确定新菜单的位置
在创建新菜单之前,首先确定该菜单应该是顶级菜单还是子级菜单项。顶级菜单在管理菜单中显示为新部分,并包含子菜单项。子级菜单表示菜单项是现有菜单的成员。
很少有插件需要创建顶级菜单。如果插件向WordPress引入了一个全新的概念或功能,并且需要很多屏幕才能做到这一点,那么该插件可能需要一个新的顶级菜单。只有当你真的需要多个相关的屏幕来让WordPress做一些它最初设计不是要完成的事情时,才应该考虑添加一个顶级菜单。新的顶级菜单的示例可能包括作业管理或会议管理。请注意,使用原生的帖子类型注册,WordPress会自动创建顶级菜单来管理这些功能。
如果不需要创建顶级菜单,请决定将子菜单项放置在哪个顶级菜单下。作为参考,大多数插件在现有的WordPress顶级菜单下添加子级菜单项。例如,备份插件向工具顶层菜单添加了一个子级菜单选项。请注意,在进行分类注册时,WordPress会自动在适用的顶级菜单下创建子级菜单来管理这些功能。
使用WordPress顶级菜单的此指南来确定子菜单项的正确位置:
仪表盘
信息集中于您的站点,并包括更新WordPress核心、插件和主题的更新选项。
文章
显示用于编写日志(面向时间的内容)的工具。
媒体
上传和管理您的图片、视频和音频。
链接
管理对其他感兴趣的博客和站点的引用。
页面
显示用于编写称为页面的静态内容的工具。
评论
控制和调节读者对帖子的回应。
外观
显示用于处理主题/样式文件、侧边栏等的控件。
插件
显示处理插件管理的控件,而不是插件本身的配置选项。
用户
显示用于用户管理的控件。
工具
管理博客数据的导出、导入甚至备份。
设置
显示只有管理员才能查看的插件选项(另请参阅创建设置页)。
网络管理员
显示网络上设置的插件选项。不应使用“ADMIN_MENU”,而应使用“NETWORK_ADMIN_MENU”(另请参阅创建网络)。
管理菜单功能
既然您已经决定了在哪里添加顶级菜单或子级菜单,下一步就是告诉WordPress您的新页面。所有这些都将在注册到“ADMIN_MENU”操作的函数中发生。本节的末尾提供了一个工作示例。
顶层菜单
如果您已经决定您的插件需要一个全新的顶级菜单,那么您需要做的第一件事就是使用add_menu_page函数创建一个。注意:如果不需要顶级菜单,请跳到子菜单。
<?php add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position ); ?>
参数值:
page_title
选择菜单时要在页面的标题标签中显示的文本。
menu_title
菜单的屏幕名称文本。
capability
向用户显示此菜单所需的功能。在使用设置API处理表单时,您应该在此处使用“Manage_Options”,因为如果没有它,用户将无法保存选项。用户级别已弃用,不应在此处使用!
menu_slug
引用此菜单的辅助程序名称(对于此菜单应该是唯一的)。在3.0版之前,这称为文件(或句柄)参数。如果省略了函数参数,则MENU_SLUG应该是处理菜单页面内容显示的php文件。
function
显示菜单页的页面内容的函数。从技术上讲,function参数是可选的,但是如果没有提供它,那么WordPress基本上会假设包含PHP文件将生成管理屏幕,而不调用函数。页面生成代码可以在主插件文件内的函数中编写。如果指定了Function参数,则可以使用任何字符串作为FILE参数。这允许使用?page=my_superplugin_page这样的页面,而不是?page=my-Super-plugin/admin-options.php。
必须以以下两种方式之一引用该函数:
如果函数是插件中某个类的成员,则应将其引用为数组($this,‘function_name’)。
在所有其他情况下,使用函数名本身就足够了。
icon_url
要用于此菜单的图标的URL。此参数是可选的。
position
此菜单应出现的菜单顺序中的位置。默认情况下,如果省略此参数,菜单将显示在菜单结构的底部。要查看当前菜单位置,请在菜单加载后使用print_r($GLOBALS[‘menu’])。
子菜单
一旦定义了顶级菜单,或者选择使用现有的WordPress顶级菜单,就可以使用add_submenu_page函数定义一个或多个子菜单项。确保按您希望的显示顺序添加子级别菜单项。
使用 add_submenu_page
<?php add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function); ?>
参数值:
parent_slug
父菜单的插件名称,或标准WordPress管理文件的文件名,该文件提供您要在其中插入子菜单的顶级菜单,如果此子菜单将进入自定义顶级菜单,则提供插件文件。
例如:
- 对于仪表板:add_submenu_page(‘index.php’,…)。
- 对于文章:add_submenu_page(‘edit.php’,…)。
- 对于媒体:add_submenu_page(‘upload.php’,…)。
- 对于链接:add_submenu_page(‘link-manager.php’,…)。
- 对于页面:add_submenu_page(‘edit.php?post_type=page’,…)。
- 对于评论:add_submenu_page(‘edit-comments.php’,…)。
- 对于自定义类型:add_submenu_page(‘edit.php?post_type=your_post_type’,…)。
- 外观:add_submenu_page(‘themes.php’,…)。
- 插件:add_submenu_page(‘plugins.php’,…)。
- 对于用户:add_submenu_page(‘users.php’,…)。
- 对于工具:add_submenu_page(‘tools.php’,…)。
- 对于设置:add_submenu_page(‘options-general.php’,…)。
page_title
当子菜单处于活动状态时将进入页面的页面标题的文本。
menu_title
选择菜单时要在页面的标题标签中显示的文本。
capability
向用户显示此菜单所需的功能。用户级别已弃用,不应在此处使用!
menu_slug
对于现有的wordpress菜单,是处理菜单页面内容显示的php文件。对于自定义顶级菜单的子菜单,该子菜单页的唯一标识符。
在插件创建自己的顶级菜单的情况下,第一个子菜单通常与顶级菜单具有相同的链接标题,因此链接将被复制。第一次调用ADD_SUBMENU_PAGE函数,同时为parent_slug和mena_slug参数赋予相同的值,可以避免重复的链接标题。
function
显示菜单页的页面内容的函数。
从技术上讲,就像在ADD_MENU_PAGE函数中一样,function参数是可选的,但是如果没有提供它,那么WordPress基本上会假设包含PHP文件将生成管理屏幕,而不调用函数。大多数插件作者选择将页面生成代码放在其主插件文件内的函数中。
如果指定了函数参数,则可以使用任何字符串作为文件参数。这允许使用?page=my_super_plugin_page这样的页面,而不是?page=my-super-plugin/admin-options.php。
有关如何将类成员引用为函数参数的说明,请参见顶级菜单。
示例
下面是一个快速示例,说明如何插入顶级菜单页面和子菜单页面,其中子菜单页面上的标题与顶级页面不同。在此示例中,‘my_magic_function’是显示第一个子菜单页面的函数的名称:
<?php add_menu_page('Page title', 'Top-level menu title', 'manage_options', 'my-top-level-handle', 'my_magic_function'); add_submenu_page( 'my-top-level-handle', 'Page title', 'Sub-menu title', 'manage_options', 'my-submenu-handle', 'my_magic_function'); ?>
下面是在自定义页面类型菜单块下添加选项日志的示例(另请参阅此处):
<?php add_submenu_page('edit.php?post_type=wiki', 'Options', 'Options', 'manage_options', 'wiki-options', array(&$this, 'options_page') ); ?>
使用包装函数
由于大多数子级菜单都属于“设置”、“工具”或“外观”菜单,WordPress提供了包装器功能,使向这些顶级菜单添加子级菜单项变得更容易。请注意,函数名称可能与管理用户界面中看到的名称不匹配,因为它们会随着时间的推移而更改:
Dashboard
<?php add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Posts
<?php add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Media
<?php add_media_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Links
<?php add_links_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Pages
<?php add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Comments
<?php add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Appearance
<?php add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Plugins
<?php add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Users
<?php add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Tools
<?php add_management_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
Settings
<?php add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function); ?>
插入页面
下面是一个将新菜单插入到不同位置的WordPress插件示例:
<?php /* Plugin Name: Menu Test Plugin URI: http://codex.wordpress.org/Adding_Administration_Menus Description: Menu Test Author: Codex authors Author URI: http://example.com */ // Hook for adding admin menus add_action('admin_menu', 'mt_add_pages'); // action function for above hook function mt_add_pages() { // Add a new submenu under Settings: add_options_page(__('Test Settings','menu-test'), __('Test Settings','menu-test'), 'manage_options', 'testsettings', 'mt_settings_page'); // Add a new submenu under Tools: add_management_page( __('Test Tools','menu-test'), __('Test Tools','menu-test'), 'manage_options', 'testtools', 'mt_tools_page'); // Add a new top-level menu (ill-advised): add_menu_page(__('Test Toplevel','menu-test'), __('Test Toplevel','menu-test'), 'manage_options', 'mt-top-level-handle', 'mt_toplevel_page' ); // Add a submenu to the custom top-level menu: add_submenu_page('mt-top-level-handle', __('Test Sublevel','menu-test'), __('Test Sublevel','menu-test'), 'manage_options', 'sub-page', 'mt_sublevel_page'); // Add a second submenu to the custom top-level menu: add_submenu_page('mt-top-level-handle', __('Test Sublevel 2','menu-test'), __('Test Sublevel 2','menu-test'), 'manage_options', 'sub-page2', 'mt_sublevel_page2'); } // mt_settings_page() displays the 页面 content for the Test settings submenu function mt_settings_page() { echo "<h2>" . __( 'Test Settings', 'menu-test' ) . "</h2>"; } // mt_tools_page() displays the 页面 content for the Test Tools submenu function mt_tools_page() { echo "<h2>" . __( 'Test Tools', 'menu-test' ) . "</h2>"; } // mt_toplevel_page() displays the 页面 content for the custom Test Toplevel menu function mt_toplevel_page() { echo "<h2>" . __( 'Test Toplevel', 'menu-test' ) . "</h2>"; } // mt_sublevel_page() displays the 页面 content for the first submenu // of the custom Test Toplevel menu function mt_sublevel_page() { echo "<h2>" . __( 'Test Sublevel', 'menu-test' ) . "</h2>"; } // mt_sublevel_page2() displays the 页面 content for the second submenu // of the custom Test Toplevel menu function mt_sublevel_page2() { echo "<h2>" . __( 'Test Sublevel2', 'menu-test' ) . "</h2>"; } ?>
示例菜单页面
上面的示例包含几个伪函数,如mt_settings_page,作为实际页面内容的占位符。我们需要把它们变成真正的菜单页面。因此,让我们假设我们的插件有一个名为mt_favorite_color的选项,并且我们希望允许站点所有者通过设置页面输入他/她喜欢的颜色。mt_options_page 函数需要在屏幕上放置一个数据输入表单以启用此功能,并处理输入的数据。下面是一个执行此操作的函数:
// mt_settings_page() displays the 页面 content for the Test settings submenu function mt_settings_page() { //must check that the user has the required capability if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.') ); } // variables for the field and option names $opt_name = 'mt_favorite_color'; $hidden_field_name = 'mt_submit_hidden'; $data_field_name = 'mt_favorite_color'; // Read in existing option value from database $opt_val = get_option( $opt_name ); // See if the user has posted us some information // If they did, this hidden field will be set to 'Y' if( isset($_POST[ $hidden_field_name ]) && $_POST[ $hidden_field_name ] == 'Y' ) { // Read their posted value $opt_val = $_POST[ $data_field_name ]; // Save the posted value in the database update_option( $opt_name, $opt_val ); // Put an settings updated message on the screen ?> <div class="updated"><p><strong><?php _e('settings saved.', 'menu-test' ); ?></strong></p></div> <?php } // Now display the settings editing screen echo '<div class="wrap">'; // header echo "<h2>" . __( 'Menu Test Plugin Settings', 'menu-test' ) . "</h2>"; // settings form ?> <form name="form1" method="post" action=""> <input type="hidden" name="<?php echo $hidden_field_name; ?>" value="Y"> <p><?php _e("Favorite Color:", 'menu-test' ); ?> <input type="text" name="<?php echo $data_field_name; ?>" value="<?php echo $opt_val; ?>" size="20"> </p><hr /> <p class="submit"> <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e('Save Changes') ?>" /> </p> </form> </div> <?php }
以下是几点注意事项:
- 诸如add_menu_page 和add_submenu_page 这样的WordPress函数具有一个功能,该功能将用于确定是显示顶级菜单还是显示子级菜单。
- 挂接以处理页面输出的函数必须检查用户是否也具有所需的功能。
- WordPress管理函数负责验证用户登录,因此您不必在函数中担心这一点。
- 上面的函数示例已经国际化–有关更多信息,请参阅编写插件的国际化部分。
- 该函数在将数据输入表单放到屏幕上之前处理所有输入的数据,因此新值将显示在表单中(而不是数据库中的值)。
- 您不必担心第一次会正常工作,因为WordPress update_option函数会自动向数据库添加一个选项(如果该选项不存在)。
- 每次导航到管理中的页面时,都会解析这些管理菜单添加过程。因此,如果您正在编写一个没有选项页面的插件,但稍后添加一个选项页面,您可以使用上面的说明添加它,然后重新上传,然后进行调整,直到您满意为止。换句话说,菜单不会在激活插件时“永久添加”或放入数据库。它们是动态解析的,因此您可以随意添加或删减菜单项,重新上传,更改将立即反映出来。
页面 Hook Suffix
添加新管理菜单(add_menu_page()、add_submenu_page()及其专用版本(如add_options_page()的每个函数都返回一个称为Page Hook Suffix的特殊值。以后可以将其用作仅在该特定页面上调用的操作可以注册到的挂钩。
一个这样的操作钩子是 load-{page_hook},其中{page_hook} 是这些add_*_page()函数之一返回的值。此挂接在加载特定页面时调用。在下面的示例中,它用来在除插件的选项页面之外的所有管理页面上显示“插件未配置”的提示:
<?php add_action('admin_menu', 'my_plugin_menu'); // Here you can check if plugin is configured (e.g. check if some option is set). If not, add new hook. // In this example hook is always added. add_action( 'admin_notices', 'my_plugin_admin_notices' ); function my_plugin_menu() { // Add the new admin menu and 页面 and save the returned hook suffix $hook_suffix = add_options_page('My Plugin Options', 'My Plugin', 'manage_options', 'my-unique-identifier', 'my_plugin_options'); // Use the hook suffix to compose the hook and register an action executed when plugin's options 页面 is loaded add_action( 'load-' . $hook_suffix , 'my_load_function' ); } function my_load_function() { // Current admin 页面 is the options 页面 for our plugin, so do not display the notice // (remove the action responsible for this) remove_action( 'admin_notices', 'my_plugin_admin_notices' ); } function my_plugin_admin_notices() { echo "<div id='notice' class='updated fade'><p>My Plugin is not configured yet. Please do it now.</p></div>\n"; } function my_plugin_options() { if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.') ); } echo '<div class="wrap">'; echo '<p>Here is where the form would go if I actually had options.</p>'; echo '</div>'; } ?>