WordPress как на ладони
Наставник Трепачёв Д.П., phphtml.net wordpress jino

add_menu_page() WP 1.5

Добавляет пункт (страницу) верхнего уровня в меню админ-панели (в один ряд с постами, страницами, пользователями и т.д.).

add_menu_page() используется для создания главного пункта меню в админ-панели и прикрепления к этому пункту функции, которая будет отвечать за страницу в админ-панели связанную с этим пунктом меню.

Если нужно добавить дочерний пункт меню, используйте add_submenu_page().

Если вы видите ошибку "You do not have sufficient permissions to access this page." при попытке зайти на страницу, это значит, что вы подключаете функцию слишком рано, подключаете функцию не на нужный хук. Нужно использовать хук admin_menu.

Хуков нет.

Возвращает

Строку. Название хука, название страницы меню или false, если пользователь не имеет прав доступа к меню.

Использование

add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
$page_title(строка) (обязательный)
Текст, который будет использован в теге <title> на странице, относящейся к пункту меню.
$menu_title(строка) (обязательный)
Название пункта меню в сайдбаре админ-панели.
$capability(строка) (обязательный)
Права пользователя (возможности), необходимые чтобы пункт меню появился в списке. Таблицу возможностей смотрите здесь.
$menu_slug(строка) (обязательный)

Уникальное название (slug), по которому затем можно обращаться к этому меню.

Если параметр $function не указан, этот параметр должен равняться названию PHP файла относительно каталога плагинов, который отвечает за вывод кода страницы этого пункта меню.

$function(строка)

Название функции, которая выводит контент страницы пункта меню.Этот параметр необязательный и если он не указан, WordPress ожидает что текущий подключаемый PHP файл генерирует страницу код страницы админ-меню, без вызова функции. Большинство авторов плагинов предпочитают указывать этот параметр.

Два варианта установки параметра:

  1. Если функция является методом класса, она вызывается по ссылке:
    array( $this, 'function_name' )
    или статически:
    array( __CLASS__, 'function_name' ).

  2. Во всех остальных случаях указываем название функции в виде строки.
    По умолчанию: нет
$icon_url(строка)

Иконка для пункта меню.

  • Если вам нужно подключить произвольную картинку, можно использовать функцию plugin_dir_url( __FILE__ ), чтобы получить УРЛ до папки файла плагина и затем дописать к нему название картинки: plugin_dir_url( __FILE__ ) .'plugin-icon.png'. Размеры иконки должны быть 20х20 пикселей или меньше.

  • С версии 3.8, WP использует специальные иконки dashicons, чтобы указать одну из этих иконок, выберите нужную в коллекции иконок и укажите в этом параметре название иконки. Например, иконка консоли называется dashicons-dashboard указываем это название.

  • С версии 3.8. можно указывать закодированную в base64 строку, которая будет содержать картинку: data:image/svg+xml;base64.... В этом случае, иконка будет указана как фон слоя. Закодировать картинку можно в сервисе: http://duri.me/.

  • Если указать 'none', то будет создан слой div картинку для которого можно затем указать в CSS стилях.

  • По умолчанию, когда указана пустая строка '', используется иконка консоли из списка dashicons и будет добавлен CSS класс menu-icon-generic в слою иконки.
    По умолчанию: ''
$position(число)

Число определяющее позицию меню. Чем больше цифра, тем ниже будет расположен пункт меню.

Внимание! Если два пункта используют одинаковую цифру-позицию, один из пунктов меню может быть перезаписан и будет показан только один пункт из двух. Чтобы избежать конфликта, можно использовать десятичные значения, вместо целых чисел: 63.3 вместо 63. Используйте кавычки для кода: "63.3".

По умолчанию, пункт меню будет добавлен в конец списка.

Список позиций для базовых пунктов меню:

2 Консоль
4 Разделитель
5 Посты
10 Медиа
15 Ссылки
20 Страницы
25 Комментарии
59 Разделитель
60 Внешний вид
65 Плагины
70 Пользователи
75 Инструменты
80 Настройки
99 Разделитель

По умолчанию: в конце списка иконок

Примеры

#1 Пункт настройки темы

Этот пример показывает как добавить страницу настроек темы, в главное меню админ-панели WordPress.

add_action('admin_menu', function(){
	add_menu_page( 'Дополнительные настройки сайта', 'Пульт', 'manage_options', 'site-options', 'add_my_setting', '', 4 ); 
} );

// функция отвечает за вывод страницы настроек
// подробнее смотрите API Настроек: http://wp-kama.ru/id_3773/api-optsiy-nastroek.html
function add_my_setting(){
	?>
	<div class="wrap">
		<h2><?php echo get_admin_page_title() ?></h2>

		<?php
		// settings_errors() не срабатывает автоматом на страницах отличных от опций
		if( get_current_screen()->parent_base !== 'options-general' )
			settings_errors('название_опции');
		?>

		<form action="options.php" method="POST">
			<?php
				settings_fields("opt_group");     // скрытые защитные поля
				do_settings_sections("opt_page"); // секции с настройками (опциями).
				submit_button();
			?>
		</form>
	</div>
	<?php

}

#2 Добавление меню для администратора

Добавим пункт меню в админ-панель, который будет виден только администраторам:

Вариант 1 (только для плагинов):

add_action( 'admin_menu', 'register_my_custom_menu_page' );
function register_my_custom_menu_page(){
	add_menu_page( 
		'custom menu title', 'custom menu', 'manage_options', 'myplugin/myplugin-admin.php', '', plugins_url( 'myplugin/images/icon.png' ), 6 
	);
}

В этом случае код страницы должен быть расположен в файле wp-content/plugins/myplugin/myplugin-admin.php:

<?php
	echo "Код страницы.";
?>

Вариант 2:

add_action( 'admin_menu', 'register_my_custom_menu_page' );
function register_my_custom_menu_page(){
	add_menu_page( 
		'custom menu title', 'custom menu', 'manage_options', 'custompage', 'my_custom_menu_page', plugins_url( 'myplugin/images/icon.png' ), 6 
	); 
}

function my_custom_menu_page(){
	echo "Код страницы.";   
}

#3 Добавление пункта меню, с проверкой что его еще нет

Допустим, что перед добавление пункта меню нужно убедится что этот пункт еще не добавлен из другого места. Для того чтобы такую проверку сделать очень быстрой, можно использовать глобальную переменную $admin_page_hooks

global $admin_page_hooks;

if( isset($admin_page_hooks['идентификатор_пункта_меню']) ){
	add_submenu_page( ... );
}
else {
	add_menu_page( ..., ..., ..., 'идентификатор_пункта_меню' );
	add_submenu_page( ... );
}

#4 Проверка наличия пункта меню или пункта подменю

Эта функция проверяет наличие пункта меня или пункта подменю по указанному идентификатору этого пункта.

/**
 * Находит указанный элемент меню или подменю админки.
 *
 * Нужно использовать после события 'admin_menu'.
 * 
 * Пример использования: if( is_admin_menu_item_exists('options-general.php') ){  }
 *
 * @param  string  $handle        Идентификатор пункта меню. Указывается в 4 параметре add_menu_page() или add_submenu_page()
 * @param  boolean [$sub = false] Указан ID меню или подменю?
 * @return boolean Есть пункт меню или нет
 */
function is_admin_menu_item_exists( $handle, $sub = false ){
	if( !is_admin() || (defined('DOING_AJAX') && DOING_AJAX) )
		return false;

	global $menu, $submenu;

	$check_menu = $sub ? $submenu : $menu;

	if( empty($check_menu) )
		return false;

	foreach( $check_menu as $k => $item ){
		if( $sub ){
			foreach( $item as $sm ){
			  if( $handle == $sm[2] )
				return true;
			}
		}
		elseif( $handle == $item[2] )
			return true;
	}

	return false;
}

Заметки

  1. Функция проверяет права пользователя, чтобы отобразить пункт меню. Функция, которая выводит код страницы должна проверять эти права отдельно.

  2. Если вы используете API настроек для сохранения данных и вам нужно чтобы сохранение работало для пользователей с правами ниже администратора, вам нужно изменить разрешение через хук option_page_capability_{$option_page}, где $option_page должно быть равно параметру $menu_slug.

Пример, как разрешить Редакторам (Editor) сохранять данные:

// Добавим видимость пункта меню для Редакторов
add_action( 'admin_menu', 'register_my_page' );
function register_my_page(){
	add_menu_page( 'My Page Title', 'My Page', 'edit_others_posts', 'my_page_slug', 'my_page_function', plugins_url( 'myplugin/images/icon.png' ), 6 ); 
}

// Изменим права
function my_page_capability( $capability ) {
	return 'edit_others_posts';
}
add_filter( 'option_page_capability_my_page_slug', 'my_page_capability' );

Код add menu page: wp-admin/includes/plugin.php WP 4.8.1

<?php
function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;

	$menu_slug = plugin_basename( $menu_slug );

	$admin_page_hooks[$menu_slug] = sanitize_title( $menu_title );

	$hookname = get_plugin_page_hookname( $menu_slug, '' );

	if ( !empty( $function ) && !empty( $hookname ) && current_user_can( $capability ) )
		add_action( $hookname, $function );

	if ( empty($icon_url) ) {
		$icon_url = 'dashicons-admin-generic';
		$icon_class = 'menu-icon-generic ';
	} else {
		$icon_url = set_url_scheme( $icon_url );
		$icon_class = '';
	}

	$new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url );

	if ( null === $position ) {
		$menu[] = $new_menu;
	} elseif ( isset( $menu[ "$position" ] ) ) {
	 	$position = $position + substr( base_convert( md5( $menu_slug . $menu_title ), 16, 10 ) , -5 ) * 0.00001;
		$menu[ "$position" ] = $new_menu;
	} else {
		$menu[ $position ] = $new_menu;
	}

	$_registered_pages[$hookname] = true;

	// No parent as top level
	$_parent_pages[$menu_slug] = false;

	return $hookname;
}

Cвязанные функции

Из метки: Меню администрирования (admin menu)

Еще из раздела: Админ-панель

add_menu_page 27 комментариев
Полезные 2 Вопросы 1 Все
  • campusboy1746 cайт: wp-plus.ru

    А как вытащить тайтл или название страницы, которую указываешь в этой функции? Ну что-то типа the_title, только для этих страниц? А то надоело менять в функции add_menu_page, а потом в шаблоне самой страницы.

    Ответить1.2 года назад #
  • campusboy1746 cайт: wp-plus.ru

    Из примера:

    add_menu_page( 
     'custom menu title', 'custom menu', 'manage_options', 'myplugin/myplugin-admin.php', '', plugins_url( 'myplugin/images/icon.png' ), 6 
    );

    Что-то при передачи такого аргумента:

    'myplugin/myplugin-admin.php'

    Ссылка получается от корня, то есть:

    http://site.ru/wp-admin/myplugin/myplugin-admin.php

    Я правда пробовал подключить файл темы так. Может для плагинов он как-то понимает, что надо путь переделать.

    Если делаю так:

    get_stylesheet_directory_uri().'/inc/add_my_setting_page.php'

    то получаю это:

    http://site.ru/site.ru/wp-content/themes/fs/inc/add_my_setting_page.php

    В итоге сделал по старинке smile

    function register_add_my_setting(){
     add_menu_page( 'Дополнительные настройки сайта', 'Пульт', 'manage_options', 'site-options', 'add_my_setting', '', 4 ); 
    }
    add_action( 'admin_menu', 'register_add_my_setting' );
    
    function add_my_setting(){
     include 'inc/add_my_setting_page.php';
    }
    1
    Ответить1.1 года назад #
    • Kama4477

      Я правда пробовал подключить файл темы так. Может для плагинов он как-то понимает, что надо путь переделать.

      Да так и есть, подправил в описании этот момент.

      Вообще лучше указать все параметры, вот как ты в конце сделал, так лучше всего! Надо пример добавить в описание smile

      П.С. Добавил еще пример, сделал его первым, там немного твоего кода smile

      1
      Ответить1.1 года назад #
      • campusboy1746 cайт: wp-plus.ru

        Почаще бы меня посещали умные мысли, я б чаще делился хорошим кодом laugh
        Вопрос: правильно ли на кастомной странице с настройками отправлять для сохранения данные на адрес options.php? Это по 1 примеру.

        Ответить1.1 года назад #
  • campusboy1746 cайт: wp-plus.ru

    Нет ли ошибки в 1 примере? Ругаетсоооо!

    Fatal error: Using $this when not in object context in

    P>S> Как удалить свой говнокоммент?))

    Ответитьгод назад #
    • Kama4477

      Да была ошибка поправил, там называние опции должно быть.

      Какой коммент надо удалить?

      Ответитьгод назад #
      • campusboy1746 cайт: wp-plus.ru

        Получается, я не ошибся, согрешив на $this. Думал, что ошибся, потому попросил удалить коммент выше.

        Ответитьгод назад #
  • Дмитрий

    Спасибо за интересную публикацию!

    Kama не подскажите, есть ли возможность в add_menu_page() изменить получаемую страницу admin.php, например, на product.php. Заранее большое спасибо!

    Ответитьгод назад #
    • Kama4477

      Сморите фильтры функции... Все делается через фильтры. Я не понял что конкретно вам надо.

      Ответитьгод назад #
      • Дмитрий

        Спасибо большое за ответ! Попробую изложить детальней. Я взял функцию add_menu_page() и создал страницу с циклом для получения одного поста авторизованного автора из произвольной записи.

        Мне нужно, чтобы кастомная роль могла из определенной произвольной записи получать лишь один свой пост в административном меню... Кроме этого, созданная роль для редактирования своей опубликованной записи произвольного типа должна заходить через кнопку редактировать...

        Вообщем смысл в том, что используя add_menu_page() я получаю в админке такой URL: wp-admin/admin.php?page=edit-package

        А мне нужно чтобы URL не содержал admin.php, а что-то другое. Например, product.php. В итоге URL был бы таким:

        wp-admin/product.php?page=edit-package

        То есть можно как-то повлиять на функцию (с помощью фильтров или может нужно использовать какую-то другую функцию), чтобы получить желаемый URL. Спасибо!

        Ответитьгод назад #
        • Kama4477

          Так admin.php это же не просто текст в URL - это файл с кучей кода, который описывает страницу админки, со всякими проверками по ролям, функциями и хуками.

          Если вам нужно что-то уникальное для отдельной роли. Например, редактирование какого-то поста. То может проще сделать это во фронте? Или если у роли есть доступ в админку, стандартно через add_menu_page(), а там уже при обработке вывода, проверять права и раздавать кому что нужно. А URL путь будет стандартный. Поменять его конечно можно, но это больше проблем получите, проще придумать другое решение проблемы в рамках текущего URL и текущей среды. Как-то так мне кажется...

          Ответитьгод назад #
          • Дмитрий

            Очень благодарен за ваше мнение. Во фронте не пойдет. У кастомных ролей в дминке есть профиль и возможность редактировать еще два произвольных типа записи. Там все стандартно как и с post.

            А вот именно в этом типе, нужно исключить выборку всех записей, но при этом вывести всего одну и дать возможность ее редактировать. Вот такая вот задача.

            Буду думать. Просто до вашего поста где-то читал, что add_menu_page() имеет параметр, который позволяет задавать уникальное имя для страницы... Видимо имелся ввиду фильтр $menu_slug.

            Цитирую: Или если у роли есть доступ в админку, стандартно через add_menu_page(), а там уже при обработке вывода, проверять права и раздавать кому что нужно.

            Отвечаю: Именно так и сделал. Одна заминка с этим admin.php.

            Не подскажите, может можно создать свою страницу, а потом каким-то образом привязать ее к административному меню? Или может еще какие-то подобные функции (add_menu_page()) есть. Большое спасибо!!!

            Ответитьгод назад #
            • Kama4477

              Я так и не понял зачем нужно заменить admin.php. Сделать это будет вроде не просто...

              А вот именно в этом типе, нужно исключить выборку всех записей, но при этом вывести всего одну и дать возможность ее редактировать. Вот такая вот задача.

              Если нужно убрать все записи из таблицы записей и оставить одну. То это можно сделать через хук pre_get_posts

              Ответитьгод назад #
              • Дмитрий

                Спасибо! Изменить нужно, чтобы admin.php не попадал под условия запрета доступа. То есть, я жестко закрыл все страницы имеющие admin.php от непрошеных гостей. Вижу, что проще переписать условие... Спасибо Kama!!! Удачи!

                Ответитьгод назад #
                • Kama4477

                  Зацепись на хук current_screen и используй функцию get_current_screen() через нее открой все что хочешь открыть, остальное закрой. admin.php при этом не обязательно закрывать... Там смотри в сторону элементов массива base, parent_base, parent_file. Через них можно типами страницы закрыть/открыть... Да там кучу разных условий можно придумать под нужды и не трогать admin.php, так будет в разы проще...

                  Ответитьгод назад #
                  • Дмитрий

                    Очень благодарен good Удачи!

                    Ответитьгод назад #
  • Подскажите, пожалуйста, возможно ли каким-либо цивилизованным способом решить следующую проблему. У меня установлен плагин Media Library Plus. Он добавляет в боковое меню админки отдельный пункт "Медиафайлы +" сразу вслед за стандартным "Медиафайлы".

    Я хочу засунуть его в подменю стандартного пункта. На текущий момент я тупо закомментировала в исходном файле плагина ненужные пункты подменю и строку add_menu_page(), заменив её строкой add_submenu_page() с нужными параметрами. Это, естественно, влечёт за собой необходимость каждый раз при обновлении плагина править исходный файл. Может быть можно оформить всё это через какой-нибудь фильтр или ещё что-нибудь в этом роде?

    • campusboy1746 cайт: wp-plus.ru

      Есть специальные плагины, которые позволяют довольно гибко управлять правым меню админки. Но можно и без плагина, конечно, к примеру, воспользоваться функцией remove_menu_page. А после удаления вставить ту строчку с submenu, которая у Вас уже готова.

      2
  • Доброго времени суток.
    Столкнулся с необходимость проверки на существование в админке пункта меню для добавления в него подменю.

    Сделал так:

    global $menu;
    $position = '5';
    
    if ( array_key_exists( $position, $menu ) ) {
    	add_submenu_page( ... );
    } else {
    	add_menu_page( ... );
    	add_submenu_page( ... );
    }
    

    ...но, зная WordPress, гложат смутные сомнения что есть более кашерный путь решения задачи...

    • Kama4477

      Так не правильно!

      В Админке есть глоб. переменные $admin_page_hooks - хранит все пункты меню. Правильнее будет проверить существование меню через эту переменную. Нужно проверить существование нужного элемента массива.

      Вариант 1:

      global $admin_page_hooks;
      
      if( isset($admin_page_hooks['идентификатор_пункта_меню']) ){
      	add_submenu_page( ... );
      }
       else {
      	add_menu_page( 1, 2, 3 'идентификатор_пункта_меню' );
      	add_submenu_page( ... );
      }

      Вариант 2:

      Вот еще другой вариант для проверки наличия подменю, на основе глоб. переменных $menu и $submenu....

      /**
       * Находит указанный элемент меню или подменю админки.
       *
       * Нужно исползовать после события 'admin_menu'.
       * 
       * Пример использования: if( is_admin_menu_item_exists('options-general.php') ){  }
       *
       * @param  string  $handle        Идентификатор пункта меню. Указывается в 4 параметре add_menu_page() или add_submenu_page()
       * @param  boolean [$sub = false] Указан ID меню или подменю?
       * @return boolean Есть пункт меню или нет
       */
      function is_admin_menu_item_exists( $handle, $sub = false ){
      	if( !is_admin() || (defined('DOING_AJAX') && DOING_AJAX) )
      		return false;
      
      	global $menu, $submenu;
      
      	$check_menu = $sub ? $submenu : $menu;
      
      	if( empty($check_menu) )
      		return false;
      
      	foreach( $check_menu as $k => $item ){
      		if( $sub ){
      			foreach( $item as $sm ){
      			  if( $handle == $sm[2] )
      				return true;
      			}
      		}
      		elseif( $handle == $item[2] )
      			return true;
      	}
      
      	return false;
      }

      от сюда: http://wordpress.stackexchange.com/questions/6311/how-to-check-if-an-admin-submenu-already-exists

  • Vlad

    Подскажите плиз кто сталкивался. добавляю кастомную страницу. меняю на ней хтмл захожу в админку перзагружаю страницу изменений нет. Жду примерно 20 сек презагружаю изменения есть. Но если изменять подключенный к странице js то все обновляется с первого раза.
    Пробовал записывать в отдельный пхп файл и вставлять линкой.
    пробывал подтягивать файл через гет запрос js и вставлять .innerHTML
    пробовал кучу плигинов для чистки кеша.
    пробовал вписывать в function.php запрет на кеш(не помню код, нагуглил)
    пробовал менять .htaccess (тоже нагуглил какото код)
    пробовал чистить в браузере кеш и любые комбинации перезагрузки
    подскажите в чем может быть трабла? у меня просто кончились идеи(

    вставляю на странице через get .innerHTML, а расширение html у подключаемого файла. php кеширет. бред. но блин.....

Здравствуйте, !

Ваш комментарий