admin_menuaction-hookWP 1.5.0

Allows changing admin menu items.

This hook is used to add submenus and top-level menu items to the admin menu structure with add_menu_page(), add_submenu_page(), and their derived functions.

This hook cannot be invoked on admin_init, because admin_init fires after admin_menu.

The hook runs after the basic admin menu structure has been created.

After this hook, all data in the global $menu and $submenu variables is processed further:

  • User capability to access each menu item is checked; see current_user_can(). If the user lacks access, the menu item is removed. The capability to check is stored at index 1 of the menu item: $data[1].

  • Menu sorting must first be enabled with add_filter( 'custom_menu_order', '__return_true' );. Then specify the required order with add_filter( 'menu_order', 'my_menu_order' ). See an example here.

  • CSS classes are added to menu items.

  • Access to the admin page is checked. At the end of menu creation, user_can_access_admin_page() verifies access to the current admin page using the current menu data. If access is denied, WordPress stops with this error:
    wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 );

Usage

add_action( 'admin_menu', 'wp_kama_admin_menu_action' );

/**
 * Function for `admin_menu` action-hook.
 * 
 * @param string $context Empty context.
 *
 * @return void
 */
function wp_kama_admin_menu_action( $context ){

	// action...
}
$context(string)
An empty string.

Examples

#1 Structure of the global $menu and $submenu variables

global $menu:

/*
$menu = Array (
	[2] => Array (
		[0] => Dashboard
		[1] => read
		[2] => index.php
		[3] =>
		[4] => menu-top menu-top-first menu-icon-dashboard
		[5] => menu-dashboard
		[6] => dashicons-dashboard
	)

	[4] => Array (
		[0] =>
		[1] => read
		[2] => separator1
		[3] =>
		[4] => wp-menu-separator
	)

	[10] => Array (
		[0] => Media
		[1] => upload_files
		[2] => upload.php
		[3] =>
		[4] => menu-top menu-icon-media
		[5] => menu-media
		[6] => dashicons-admin-media
	)

	[25] => Array (
		[0] => Comments <span class="awaiting-mod count-0"><span class="pending-count" aria-hidden="true">0</span><span class="comments-in-moderation-text screen-reader-text">0 Comments in moderation</span></span>
		[1] => edit_posts
		[2] => edit-comments.php
		[3] =>
		[4] => menu-top menu-icon-comments
		[5] => menu-comments
		[6] => dashicons-admin-comments
	)

	[5] => Array (
		[0] => Posts
		[1] => edit_posts
		[2] => edit.php
		[3] =>
		[4] => menu-top menu-icon-post open-if-no-js
		[5] => menu-posts
		[6] => dashicons-admin-post
	)

	[20] => Array (
		[0] => Pages
		[1] => edit_pages
		[2] => edit.php?post_type=page
		[3] =>
		[4] => menu-top menu-icon-page
		[5] => menu-pages
		[6] => dashicons-admin-page
	)

	[59] => Array (
		[0] =>
		[1] => read
		[2] => separator2
		[3] =>
		[4] => wp-menu-separator
	)

	[60] => Array (
		[0] => Appearance
		[1] => switch_themes
		[2] => themes.php
		[3] =>
		[4] => menu-top menu-icon-appearance
		[5] => menu-appearance
		[6] => dashicons-admin-appearance
	)

	[65] => Array (
		[0] => Plugins
		[1] => activate_plugins
		[2] => plugins.php
		[3] =>
		[4] => menu-top menu-icon-plugins
		[5] => menu-plugins
		[6] => dashicons-admin-plugins
	)

	[70] => Array (
		[0] => Users
		[1] => list_users
		[2] => users.php
		[3] =>
		[4] => menu-top menu-icon-users
		[5] => menu-users
		[6] => dashicons-admin-users
	)

	[75] => Array (
		[0] => Tools
		[1] => edit_posts
		[2] => tools.php
		[3] =>
		[4] => menu-top menu-icon-tools
		[5] => menu-tools
		[6] => dashicons-admin-tools
	)

	[80] => Array (
		[0] => Settings
		[1] => manage_options
		[2] => options-general.php
		[3] =>
		[4] => menu-top menu-icon-settings
		[5] => menu-settings
		[6] => dashicons-admin-settings
	)

	[99] => Array (
		[0] =>
		[1] => read
		[2] => separator-last
		[3] =>
		[4] => wp-menu-separator
	)

	[99.31337] => Array (
		[0] => SEO <span class="update-plugins count-0"><span class="plugin-count" aria-hidden="true">0</span><span class="screen-reader-text">0 notifications</span></span>
		[1] => wpseo_manage_options
		[2] => wpseo_dashboard
		[3] => Yoast SEO: Dashboard
		[4] => menu-top toplevel_page_wpseo_dashboard
		[5] => toplevel_page_wpseo_dashboard
		[6] => data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgc3R5bGU9ImZpbGw6IzgyODc4YyIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHJvbGU9ImltZyIgYXJpYS1oaWRkZW49InRydWUiIGZvY3VzYWJsZT0iZmFsc2UiPjxnPjxnPjxnPjxnPjxwYXRoIGQ9Ik0yMDMuNiwzOTVjNi44LTE3LjQsNi44LTM2LjYsMC01NGwtNzkuNC0yMDRoNzAuOWw0Ny43LDE0OS40bDc0LjgtMjA3LjZIMTE2LjRjLTQxLjgsMC03NiwzNC4yLTc2LDc2VjM1N2MwLDQxLjgsMzQuMiw3Niw3Niw3NkgxNzNDMTg5LDQyNC4xLDE5Ny42LDQxMC4zLDIwMy42LDM5NXoiLz48L2c+PGc+PHBhdGggZD0iTTQ3MS42LDE1NC44YzAtNDEuOC0zNC4yLTc2LTc2LTc2aC0zTDI4NS43LDM2NWMtOS42LDI2LjctMTkuNCw0OS4zLTMwLjMsNjhoMjE2LjJWMTU0Ljh6Ii8+PC9nPjwvZz48cGF0aCBzdHJva2Utd2lkdGg9IjIuOTc0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0zMzgsMS4zbC05My4zLDI1OS4xbC00Mi4xLTEzMS45aC04OS4xbDgzLjgsMjE1LjJjNiwxNS41LDYsMzIuNSwwLDQ4Yy03LjQsMTktMTksMzcuMy01Myw0MS45bC03LjIsMXY3Nmg4LjNjODEuNywwLDExOC45LTU3LjIsMTQ5LjYtMTQyLjlMNDMxLjYsMS4zSDMzOHogTTI3OS40LDM2MmMtMzIuOSw5Mi02Ny42LDEyOC43LTEyNS43LDEzMS44di00NWMzNy41LTcuNSw1MS4zLTMxLDU5LjEtNTEuMWM3LjUtMTkuMyw3LjUtNDAuNywwLTYwbC03NS0xOTIuN2g1Mi44bDUzLjMsMTY2LjhsMTA1LjktMjk0aDU4LjFMMjc5LjQsMzYyeiIvPjwvZz48L2c+PC9zdmc+
	)
)
*/

global $submenu:

/*
$submenu = Array (
	[upload.php] => Array (
		[5] => Array (
				[0] => Library
				[1] => upload_files
				[2] => upload.php
			)

		[10] => Array (
				[0] => Add New
				[1] => upload_files
				[2] => media-new.php
			)
	)

	[edit-comments.php] => Array (
		[0] => Array (
				[0] => All Comments
				[1] => edit_posts
				[2] => edit-comments.php
			)
	)

	[edit.php] => Array (
		[5] => Array (
				[0] => All Posts
				[1] => edit_posts
				[2] => edit.php
			)

		[10] => Array (
				[0] => Add New
				[1] => edit_posts
				[2] => post-new.php
			)

		[15] => Array (
				[0] => Categories
				[1] => manage_categories
				[2] => edit-tags.php?taxonomy=category
			)

		[16] => Array (
				[0] => Tags
				[1] => manage_post_tags
				[2] => edit-tags.php?taxonomy=post_tag
			)
	)

	[edit.php?post_type=page] => Array (
		[5] => Array (
				[0] => All Pages
				[1] => edit_pages
				[2] => edit.php?post_type=page
			)

		[10] => Array (
				[0] => Add New
				[1] => edit_pages
				[2] => post-new.php?post_type=page
			)
	)

	[themes.php] => Array (
		[5] => Array (
				[0] => Themes
				[1] => switch_themes
				[2] => themes.php
			)

		[6] => Array (
				[0] => Customize
				[1] => customize
				[2] => customize.php?return=%2Fwp-admin%2Fpost.php%3Fpost%3D5710%26action%3Dedit
				[3] =>
				[4] => hide-if-no-customize
			)

		[7] => Array (
				[0] => Widgets
				[1] => edit_theme_options
				[2] => widgets.php
			)

		[10] => Array (
				[0] => Menus
				[1] => edit_theme_options
				[2] => nav-menus.php
			)
	)

	[plugins.php] => Array (
		[5] => Array (
				[0] => Installed Plugins
				[1] => activate_plugins
				[2] => plugins.php
			)
	)

	[users.php] => Array (
		[5] => Array (
				[0] => All Users
				[1] => list_users
				[2] => users.php
			)

		[10] => Array (
				[0] => Add New
				[1] => create_users
				[2] => user-new.php
			)

		[15] => Array (
				[0] => Profile
				[1] => read
				[2] => profile.php
			)
	)

	[tools.php] => Array (
		[5] => Array (
				[0] => Available Tools
				[1] => edit_posts
				[2] => tools.php
			)

		[10] => Array (
				[0] => Import
				[1] => import
				[2] => import.php
			)

		[15] => Array (
				[0] => Export
				[1] => export
				[2] => export.php
			)

		[20] => Array (
				[0] => Site Health
				[1] => view_site_health_checks
				[2] => site-health.php
			)
	)

	[options-general.php] => Array (
		[10] => Array (
				[0] => General
				[1] => manage_options
				[2] => options-general.php
			)

		[15] => Array (
				[0] => Writing
				[1] => manage_options
				[2] => options-writing.php
			)

		[20] => Array (
				[0] => Reading
				[1] => manage_options
				[2] => options-reading.php
			)

		[25] => Array (
				[0] => Discussion
				[1] => manage_options
				[2] => options-discussion.php
			)

		[30] => Array (
				[0] => Media
				[1] => manage_options
				[2] => options-media.php
			)

		[40] => Array (
				[0] => Permalinks
				[1] => manage_options
				[2] => options-permalink.php
			)

		[45] => Array (
				[0] => Privacy
				[1] => manage_privacy_options
				[2] => options-privacy.php
			)
	)

	[wpseo_dashboard] => Array (
		[0] => Array (
				[0] => General
				[1] => wpseo_manage_options
				[2] => wpseo_dashboard
				[3] => General - Yoast SEO
			)

		[1] => Array (
				[0] => Search Appearance
				[1] => wpseo_manage_options
				[2] => wpseo_titles
				[3] => Search Appearance - Yoast SEO
			)

		[2] => Array (
				[0] => Social
				[1] => wpseo_manage_options
				[2] => wpseo_social
				[3] => Social - Yoast SEO
			)

		[3] => Array (
				[0] => Tools
				[1] => wpseo_manage_options
				[2] => wpseo_tools
				[3] => Tools - Yoast SEO
			)

		[4] => Array (
				[0] => Premium
				[1] => wpseo_manage_options
				[2] => wpseo_licenses
				[3] => Premium - Yoast SEO
			)

		[5] => Array (
				[0] => Workouts
				[1] => edit_others_posts
				[2] => wpseo_workouts
				[3] => Workouts - Yoast SEO
			)
	)
)
*/

#2 Change menu item names and add separators

Suppose we want to change the default admin menu item names. For example, we need separators between some items and want to rename Appearance to Themes and Plugins to Site Plugins.

Create a file (class) containing the following code:

<?php

final class Change_Admin_Menu {

	public static function init(): void {
		add_action( 'admin_menu', [ __CLASS__, 'change' ] );
	}

	public static function change() {
		global $menu;

		self::add_separator( 9 );
		self::add_separator( 70 );

		foreach( $menu as & $item ){
			// Rename Appearance to Theme
			if( 'themes.php' === $item[2] ){
				$item[0] = __( 'Theme', 'km' );
			}

			// Rename Plugins
			if( 'plugins.php' === $item[2] ){
				$item[0] = __( 'Site Plugins', 'km' );
			}
		}
		unset( $item );
	}

	/**
	 * Adds a separator to the admin menu.
	 * If the specified index already exists, increments it by one
	 * so existing menu items are not overwritten.
	 *
	 * @param int $position Index at which to add the separator.
	 *
	 * @author kama
	 * @ver 1.0
	 */
	protected static function add_separator( int $position ): void {
		global $menu;

		static $index;
		$index || ( $index = 1 );

		foreach( $menu as $mindex => $section ){

			if( $mindex >= $position ){

				while( isset( $menu[ $position ] ) ){
					++$position;
				}

				$menu[ $position ] = [
					'',
					'read',
					"separator-my{$index}",
					'',
					'wp-menu-separator'
				];

				$index++;

				break;
			}
		}

		ksort( $menu );
	}
}

Include the created file in functions.php and initialize the class:

require_once __DIR__ . '/rel/path/to/Change_Admin_Menu.php';
Change_Admin_Menu::init();

Changelog

Since 1.5.0 Introduced.

Where the hook is called

In file: /wp-admin/includes/menu.php
admin_menu
wp-admin/includes/menu.php 168
do_action( 'admin_menu', '' );

Where the hook is used in WordPress

wp-admin/includes/class-custom-background.php 55
add_action( 'admin_menu', array( $this, 'init' ) );
wp-admin/includes/class-custom-image-header.php 62
add_action( 'admin_menu', array( $this, 'init' ) );
wp-admin/menu.php 269
add_action( 'admin_menu', '_add_themes_utility_last', 101 );
wp-admin/menu.php 331
add_action( 'admin_menu', '_add_plugin_file_editor_to_tools', 101 );
wp-includes/default-filters.php 592
add_action( 'admin_menu', '_add_post_type_submenus' );
wp-includes/theme.php 3117
remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
wp-includes/theme.php 3130
remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );