WordPress API菜单/子菜单顺序


11

我正在开发使用儿童题材的WordPress 3.4.2和开发版的选项框架大卫价格。这是我的第一个主题,对此我还比较陌生,因此我研究了Wordpress Codex,并检查了将项目注册到API中的情况。

在不篡改主题之外的任何外部文件的情况下,我想知道是否有一种方法可以重新排列“ 主题选项”页面在外观菜单层次结构中的位置-因此,当激活主题时,位置不像第一张图片,但是像第二张一样。

旧新

我知道您可以创建一个菜单(例如“ 外观”选项卡,插件用户等)或一个子菜单(“ 主题”,“ 小部件”,“ 菜单”等),但是我该如何设置子菜单呢?从顶部?

从我收集到的信息来看,某处有一个命令被调用,functions.php文件中的其他任何页面都放在这些命令之后?

在我的functions.php文件中:

// Add our "Theme Options" page to the Wordpress API admin menu.
if ( !function_exists( 'optionsframework_init' ) ) {
    define( 'OPTIONS_FRAMEWORK_DIRECTORY', get_template_directory_uri() . '/inc/' );
    require_once dirname( __FILE__ ) . '/inc/options-framework.php';
}

谢谢。


您是否尝试过更新的功能?
亚当

感谢您回到我@userabuser。我已经复制粘贴了更新的脚本,并且似乎可以在列表中上下移动这些项目,而不会覆盖其他任何内容...但是,在新的更新中,“ 小部件”菜单上仍然出现一些错误。Warning: Invalid argument supplied for foreach() in /wp-content/themes/mythemename/functions.php on line 1444 1444行: foreach ($submenu[$menus] as $index => $value){Warning: ksort() expects parameter 1 to be array, null given in /wp-content/themes/mythemename/functions.php on line 1468 1468行: ksort($submenu[$menus]);
user1752759 2013年

如果您可以看一下,那就太好了。
user1752759 2013年

Answers:


3

这是一个例子。

首先,根据其数组键确定子菜单项的顺序,您可以var_dump在$ submenu全局变量上执行a ,该变量将输出以下内容;

(我以“帖子”菜单和子菜单为例)

  //shortened for brevity....

  ["edit.php"]=>
  array(6) {
    [5]=>
    array(3) {
      [0]=> string(9) "All Posts"
      [1]=> string(10) "edit_posts"
      [2]=> string(8) "edit.php"
    }
    [10]=>
    array(3) {
      [0]=> string(7) "Add New"
      [1]=> string(10) "edit_posts"
      [2]=> string(12) "post-new.php"
    }
    [15]=>
    array(3) {
      [0]=> string(10) "Categories"
      [1]=> string(17) "manage_categories"
      [2]=> string(31) "edit-tags.php?taxonomy=category"
    }
    [17]=>
    array(3) {
      [0]=> string(14) "Sub Menu Title"
      [1]=> string(10) "edit_posts"
      [2]=> string(17) "sub_menu_page.php"
    }
  }

我们可以看到我的子菜单项被添加到数组中,默认项之后的键为17。

例如,如果要添加子菜单项,则需要在“ 所有帖子”子菜单项之后直接添加我的数组,方法是将阵列键设置为6、7、8或9(分别在5和10之前的任何值)。

这就是你的做法...

function change_submenu_order() {

    global $menu;
    global $submenu;

     //set our new key
    $new_key['edit.php'][6] = $submenu['edit.php'][17];

    //unset the old key
    unset($submenu['edit.php'][17]);

    //get our new key back into the array
    $submenu['edit.php'][6] = $new_key['edit.php'][6];


    //sort the array - important! If you don't the key will be appended
    //to the end of $submenu['edit.php'] array. We don't want that, we
    //our keys to be in descending order
    ksort($submenu['edit.php']);

}

结果,

  ["edit.php"]=>
  array(6) {
    [5]=>
    array(3) {
      [0]=> string(9) "All Posts"
      [1]=> string(10) "edit_posts"
      [2]=> string(8) "edit.php"
    }
    [6]=>
    array(3) {
      [0]=> string(14) "Sub Menu Title"
      [1]=> string(10) "edit_posts"
      [2]=> string(17) "sub_menu_page.php"
    }
    [10]=>
    array(3) {
      [0]=> string(7) "Add New"
      [1]=> string(10) "edit_posts"
      [2]=> string(12) "post-new.php"
    }
    [15]=>
    array(3) {
      [0]=> string(10) "Categories"
      [1]=> string(17) "manage_categories"
      [2]=> string(31) "edit-tags.php?taxonomy=category"
    }
  }

...尝试一下,让我们知道您的前进方向!

更新1:

将此添加到您的functions.php文件中;

function change_post_menu_label() {

    global $menu;
    global $submenu;

    $my_menu  = 'example_page'; //set submenu page via its ID
    $location = 1; //set the position (1 = first item etc)
    $target_menu = 'edit.php'; //the menu we are adding our item to

    /* ----- do not edit below this line ----- */


    //check if our desired location is already used by another submenu item
    //if TRUE add 1 to our value so menu items don't clash and override each other
    $existing_key = array_keys( $submenu[$target_menu] );
    if ($existing_key = $location)
    $location = $location + 1;

    $key = false;
    foreach ( $submenu[$target_menu] as $index => $values ){

        $key = array_search( $my_menu, $values );

        if ( false !== $key ){
            $key = $index;
            break;
        }
    }

     $new['edit.php'][$location] = $submenu[$target_menu][$key];
     unset($submenu[$target_menu][$key]);
     $submenu[$target_menu][$location] = $new[$target_menu][$location];

    ksort($submenu[$target_menu]);

}

我的更新包括一个稍微简单一些的方法来处理菜单位置的设置,您只需要指定子菜单页面的名称以及菜单中所需的位置即可。但是,如果选择的子菜单页$location与现有键的子菜单页相同,它将用您的键覆盖该键,因此菜单项将消失,而菜单项将位于其位置。在这种情况下,增加或减少数字以正确排序菜单。类似地,如果有人安装了一个效果相同的菜单区域且其$location子菜单项与之相同的插件,则将发生相同的问题。为了避免这种情况,Kaiser的示例对此进行了一些基本检查。

更新2:

我添加了一个额外的代码块,用于检查数组中所有现有键$location是否与我们期望的键相符;如果找到匹配项,则将增加我们的$location值,1以避免菜单项相互覆盖。这是负责的代码,

   //excerpted snippet only for example purposes (found in original code above)
   $existing_key = array_keys( $submenu[$target_menu] );
   if ($existing_key = $location)
   $location = $location + 1;

更新3 :(对脚本进行了修改,以允许对多个子菜单项进行排序)

add_action('admin_init', 'move_theme_options_label', 999);

function move_theme_options_label() {
    global $menu;
    global $submenu;

$target_menu = array(
    'themes.php' => array(
        array('id' => 'optionsframework', 'pos' => 2),
        array('id' => 'bp-tpack-options', 'pos' => 4),
        array('id' => 'multiple_sidebars', 'pos' => 3),
        )
);

$key = false;

foreach ( $target_menu as $menus => $atts ){

    foreach ($atts as $att){

        foreach ($submenu[$menus] as $index => $value){

        $current = $index;  

        if(array_search( $att['id'], $value)){ 
        $key = $current;
        }

            while (array_key_exists($att['pos'], $submenu[$menus]))
                $att['pos'] = $att['pos'] + 1;

            if ( false !== $key ){

                if (array_key_exists($key, $submenu[$menus])){
                    $new[$menus][$key] = $submenu[$menus][$key];
                    unset($submenu[$menus][$key]);
                    $submenu[$menus][$att['pos']] = $new[$menus][$key];

                } 
            }
        }
    }
}

ksort($submenu[$menus]);
return $submenu;

}

在上面的示例中,可以通过在$target_menu包含多维值数组的变量内相应地设置参数,来定位多个子菜单和每个子菜单多个项目。

$target_menu = array(
//menu to target (e.g. appearance menu)
'themes.php' => array(
    //id of menu item you want to target followed by the position you want in sub menu
    array('id' => 'optionsframework', 'pos' => 2),
    //id of menu item you want to target followed by the position you want in sub menu
    array('id' => 'bp-tpack-options', 'pos' => 3),
    //id of menu item you want to target followed by the position you want in sub menu
    array('id' => 'multiple_sidebars', 'pos' => 4),
    )
 //etc....
);

此修订将防止子菜单项具有相同的键(位置)时相互覆盖,因为它将循环滚动直到找到不存在的可用键(位置)。


感谢您对用户滥用者的快速响应,但是我对这一切还是陌生的,所以请耐心等待。由于编写的简洁程度,我不太确定如何实现上面的脚本/代码以及需要将其放在哪个文件中,请详细说明。在这个示例中正常工作并输出了所需的数量...如果用户稍后要安装插件,从而创建了一个附加的顶层菜单,其中包含几个子级别(例如电子商务解决方案),这会影响阵列键并制动,它打算做什么?
user1752759

1
@Rob进行了一些轻微的调整,应有助于避免菜单项相互覆盖的情况。
亚当

@ user1752759这与上面有什么关系?您在上述注释中提供的functions.php文件的完整路径是什么?该文件中的代码是什么?在最后一次对话中,这对您有用。它也对我有用。因此,我怀疑如果您上次重复两个代码片段并且在函数周围没有正确的括号,如果我上次记错的话,您的代码中可能还缺少其他内容。
亚当

感谢您回到我@userabuser。我已经复制粘贴了更新的脚本,并且似乎可以在列表中上下移动这些项目,而不会覆盖其他任何内容...但是,在新的更新中,“小部件”菜单上仍然出现一些错误。Warning: Invalid argument supplied for foreach() in /wp-content/themes/mythemename/functions.php on line 1444 1444行 foreach ($submenu[$menus] as $index => $value){Warning: ksort() expects parameter 1 to be array, null given in /wp-content/themes/mythemename/functions.php on line 1468 1468行: ksort($submenu[$menus]);
user1752759 2013年

如果您可以看一下,那就太好了。
user1752759

2

管理菜单(及其问题)

由于管理菜单严重缺少任何挂钩和公共API(允许在其中移动项目),因此您必须使用一些解决方法。以下答案向您展示了未来正在等待的事情,以及只要我们拥有当前的核心状态,您将如何解决。

首先,我要指出的是,scribu正在开发一个管理菜单补丁,该补丁应该使操作更加容易。当前的结构非常混乱,我写了一篇有关它的文章,很快就会过时。期望WP 3.6能够完全改变一切。

还有一点是,您不应再使用“选项”页面作为主题。如今,有“主题定制器”

插件

我编写了一个插件,使用TwentyEleven / Ten选项页面的默认“主题选项”页面对此进行了测试。如您所见,没有真正的API允许任何位置。因此,我们必须拦截全局。

简而言之:只需遵循注释并查看管理员通知,我已添加这些通知给您一些调试输出。

<?php
/** Plugin Name: (#70916) Move Submenu item */

add_action( 'plugins_loaded', array( 'wpse70916_admin_submenu_items', 'init' ) );

class wpse70916_admin_submenu_items
{
    protected static $instance;

    public $msg;

    public static function init()
    {
        is_null( self :: $instance ) AND self :: $instance = new self;
        return self :: $instance;
    }

    public function __construct()
    {
        add_action( 'admin_notices', array( $this, 'add_msg' ) );

        add_filter( 'parent_file', array( $this, 'move_submenu_items' ) );
    }

    public function move_submenu_items( $parent_file )
    {
        global $submenu;
        $parent = $submenu['themes.php'];

        $search_for = 'theme_options';

        // Find current position
        $found = false;
        foreach ( $parent as $pos => $item )
        {
            $found = array_search( $search_for, $item );
            if ( false !== $found )
            {
                $found = $pos;
                break;
            }
        }
        // DEBUG: Tell if we didn't find it.
        if ( empty( $found ) )
            return $this->msg = 'That search did not work out...';

        // Now we need to determine the first and second item position
        $temp = array_keys( $parent );
        $first_item  = array_shift( $temp );
        $second_item = array_shift( $temp );

        // DEBUG: Check if it the item fits between the first two items:
        $distance = ( $second_item - $first_item );
        if ( 1 >= $distance )
            return $this->msg = 'We do not have enough space for your item';

        // Temporary container for our item data
        $target_data = $parent[ $found ];

        // Now we can savely remove the current options page
        if ( false === remove_submenu_page( 'themes.php', $search_for ) )
            return $this->msg = 'Failed to remove the item';

        // Shuffle items (insert options page)
        $submenu['themes.php'][ $first_item + 1 ] = $target_data;
        // Need to resort the items by their index/key
        ksort( $submenu['themes.php'] );
    }

    // DEBUG Messages
    public function add_msg()
    {
        return print sprintf(
             '<div class="update-nag">%s</div>'
            ,$this->msg
        );
    }
} // END Class wpse70916_admin_submenu_items

祝好运并玩得开心点。


2

自定义过滤器

还有另一种可能性可以实现这一目标。不要问我为什么我没有更早地考虑过它。无论如何,有一个专门用于自定义菜单顺序的过滤器。只需将其设置true为允许自定义订单即可。然后,您有了第二个钩子来订购主菜单项。在这里,我们只是截取global $submenu并在子菜单项之间切换。

在此处输入图片说明

本示例将“ 菜单”项 移到“ 小部件”项上方,以演示其功能。您可以根据自己的喜好进行调整。

<?php
defined( 'ABSPATH' ) OR exit;
/**
 * Plugin Name: (#70916) Custom Menu Order
 * Description: Changes the menu order of a submenu item.
 */

// Allow a custom order
add_filter( 'custom_menu_order', '__return_true' );
add_filter( 'menu_order', 'wpse70916_custom_submenu_order' );
function wpse70916_custom_submenu_order( $menu )
{
    // Get the original position/index
    $old_index = 10;
    // Define a new position/index
    $new_index = 6;

    // We directly interact with the global
    $submenu = &$GLOBALS['submenu'];
    // Assign our item at the new position/index
    $submenu['themes.php'][ $new_index ] = $submenu['themes.php'][ $old_index ];
    // Get rid of the old item
    unset( $submenu['themes.php'][ $old_index ] );
    // Restore the order
    ksort( $submenu['themes.php'] );

    return $menu;
}

在使用PHP @kaiser时我不太自信,但是您可能会知道一种实现上述脚本的方法,以便在同一脚本中包含多个项时function wpse70916_custom_submenu_order( $menu ),不仅要对Menu还要对Theme进行重新排序选项窗口小部件编辑器等使它非常灵活,并且项目也不会彼此覆盖吗?谢谢。
user1752759

@ user1752759插件已经具有这种灵活性。冲突安全性(避免压倒一切)是另一个问题。在100%的情况下,这是不可能的,因为您不能最后分配动作。总有一些东西可以稍后运行。无论如何:请打开一个新问题并链接到该问题。
kaiser

谢谢你,会做凯撒。如果不是要问太多,您能否更新上面的脚本以显示如何完成多个项目(例如MenuWidgets),我应该可以将其用作对其他项目执行相同操作的指南?作为PHP的新手,我认为我做的不正确,也许是因为数字。欢呼声
user1752759

请问一个新问题并链接到这个问题。谢谢。
kaiser
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.