Wordpress API Menu / Порядок подменю

11

Я занимаюсь разработкой дочерней темы с использованием Wordpress 3.4.2 и версией разработки Framework от David Price . Это моя первая тема, и я относительно новичок в этом, поэтому я изучил Кодекс Wordpress и проверил регистрацию элементов в 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';
}

Спасибо.

user1752759
источник
Вы пробовали обновленную функцию?
Адам
Спасибо, что ответили мне @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
Если бы вы могли взглянуть на это, это было бы здорово.
user1752759

Ответы:

3

Вот пример;

Сначала, чтобы выяснить порядок пунктов подменю на основе его ключа массива, вы можете выполнить var_dumpглобальную переменную $ submenu, которая выведет следующее;

(Я использую меню сообщений и подменю в качестве примера)

  //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что и ваш элемент подменю, то возникнет та же проблема. Чтобы обойти это, пример Кайзера предоставляет некоторые базовые проверки для этого.

Обновление 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....
);

Эта ревизия предотвратит перезапись элементов подменю, если они имеют одинаковый ключ (положение), поскольку она будет циклически проходить, пока не найдет доступный ключ (положение), который не существует.

Адам
источник
Спасибо за быстрый ответ userabuser, но я довольно новичок во всем этом, поэтому, пожалуйста, просто потерпите меня. Я не совсем уверен, как реализовать ваш скрипт / код выше и в какой файл его нужно поместить из-за того, насколько сжатым он был написан - уточните, пожалуйста. Хотя этот пример работает и выдает нужное число ... если пользователь позже установит плагин, который создал дополнительное меню верхнего уровня с несколькими подуровнями внутри (например, решение для электронной коммерции), будет ли это воздействовать на ключ массива и тормозить то, что было задано сделать?
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
Если бы вы могли взглянуть на это, это было бы здорово.
user1752759
2

Меню администратора (и его проблемы)

Поскольку в меню администратора отсутствуют какие-либо хуки и общедоступный API (который позволяет перемещать элементы), вы должны использовать некоторые обходные пути. Следующий ответ показывает вам, что ожидает вас в будущем и как вы можете обойти это, пока у нас есть текущее состояние ядра.

Прежде всего, я должен отметить, что scribu работает над патчем для меню администратора, который должен значительно облегчить работу с ним. Нынешняя структура довольно запутана и я написал об этом статью , которая скоро устареет. Ожидайте, что WP 3.6 полностью изменит ситуацию.

Кроме того, есть еще один момент: вам больше не следует использовать страницы параметров для тем. Для этого есть - в настоящее время - « Theme Customizer» .

Плагин

Я написал плагин, который проверяет это на странице параметров темы по умолчанию для страницы параметров 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 )скажем, изменить порядок не только меню , но и темы Опции , виджеты , редактор и т. Д. Делают его достаточно гибким, а также чтобы элементы не перекрывали друг друга? Спасибо.
user1752759
@ user1752759 Плагин уже обладает такой гибкостью. Конфликт безопасности (избегать переопределения) является еще одной проблемой. Это невозможно в 100% сценарии, поскольку вы не можете назначить свое действие последним. Всегда есть что-то, что может работать позже. В любом случае: Пожалуйста, откройте новый вопрос и ссылку на него.
Кайзер
спасибо и сделаю кайзер. если спросить не так уж много, не могли бы вы обновить вышеприведенный сценарий, чтобы показать, как выполняется несколько элементов (например, меню и виджеты ), которые я мог бы использовать в качестве руководства для выполнения того же действия с другими элементами? Будучи довольно новым для PHP, я не думаю, что делаю это правильно, возможно, из-за цифр. ура
user1752759
Пожалуйста, просто задайте новый вопрос и ссылку на него. Спасибо.
Кайзер