Under the appearance admin menu, I have customizer added by the theme, and theme options added by a plugin. I’m using this code to hide both menus ( submenu’s of Appearance )for ALL admins apart from a certain USERNAME.
function hide_menu() {
global $current_user;
$current_user = wp_get_current_user();
$user_name = $current_user->user_login;
//check condition for the user means show menu for this user
if(is_admin() && $user_name != 'USERNAME') {
remove_submenu_page( 'themes.php', 'customize' );
remove_submenu_page( 'themes.php', 'core-settings' );
}
}
add_action('admin_head', 'hide-menu');
The code its self works fine, I can hide parent menu’s. But I can’t seem to hide the two sub menu’s that I want to hide.
The two menu’s point to URLs :
domainname.com/wp-admin/customize.php
domainname.com/wp-admin/themes.php?page=core-settings
Submenu Debug :
[6] => Array
(
[0] => Customise
[1] => customize
[2] => customize.php?return=%2Fwp-admin%2Findex.php
[3] =>
[4] => hide-if-no-customize
)
[21] => Array
(
[0] => Theme Settings
[1] => manage_options
[2] => core-settings
[3] => Theme Settings
)
I’ve looked at dubug mode but im not sure what im looking at, could someone please give a solution and an explanation as to why it works.
2 Answers
Direct answer:
add_action( 'admin_menu', function() {
global $current_user;
$current_user = wp_get_current_user();
$user_name = $current_user->user_login;
//check condition for the user means show menu for this user
if(is_admin() && $user_name != 'USERNAME') {
//We need this because the submenu's link (key from the array too) will always be generated with the current SERVER_URI in mind.
$customizer_url = add_query_arg( 'return', urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ), 'customize.php' );
remove_submenu_page( 'themes.php', $customizer_url );
}
}, 999 );
You have to respect the full-path naming.
The long answer, this:
add_action( 'admin_menu', function() {
$page = remove_submenu_page( 'themes.php', 'customize.php' );
}, 999 );
Doesn’t work. But this:
add_action( 'admin_menu', function() {
$page = remove_submenu_page( 'themes.php', 'widgets.php' );
}, 999 );
Works. If we just look at the links, then we can see that it should work, but check this out. In wp-admin/menu.php, line 164
, we have:
$submenu['themes.php'][6] = array( __( 'Customize' ), 'customize', esc_url( $customize_url ), '', 'hide-if-no-customize' );
If we comment this out, bang, the Customize
link dissapears, but if we go on ahead and paste this code and go to our wp-admin/index.php
, we can see that there’s no customize.php
in the array:
add_action( 'admin_menu', function() {
global $submenu;
var_dump( $submenu );
}, 999 );
The others are here. So what’s going on? If we dump the $submenu
right after it’s created, we can see that it’s there:
[themes.php] => Array
(
[5] => Array
(
[0] => Themes
[1] => switch_themes
[2] => themes.php
)
[6] => Array
(
[0] => Customize
[1] => customize
[2] => customize.php?return=%2Fwordpress%2Fwp-admin%2Findex.php
[3] =>
[4] => hide-if-no-customize
)
[10] => Array
(
[0] => Menus
[1] => edit_theme_options
[2] => nav-menus.php
)
)
…so, somewhere, it gets lost, or so you would think, at the end of this file, we see that, yet again, we include another file:
require_once(ABSPATH . 'wp-admin/includes/menu.php');
If we then go at the end of this file and we dump the $menu
, what we get is..surprisingly, a menu without ‘customizer’ in it. But it was there just moments ago.
Surprisingly, if we do:
add_action( 'admin_menu', function() {
global $submenu;
var_dump( $submenu );
}, 999 );
It’s clear, the customize.php
is there…except…it generates itself with some parameters after: customize.php?return=%2Fwordpress%2Fwp-admin%2Findex.php
(this depends on your site).
Interesting, so if we do:
add_action( 'admin_menu', function() {
remove_submenu_page( 'themes.php', 'customize.php?return=%2Fwordpress%2Fwp-admin%2Findex.php' );
}, 999 )
Or, in your case:
add_action( 'admin_menu', function() {
remove_submenu_page( 'themes.php', 'customize.php?return=%2Fwp-admin%2Findex.php' );
}, 999 )
It works.
So what did we learn today? WP Core is a bad piece of crap, ok, it’s true but it’s your mistake for not looking at what remove_submenu_page
looks for.
function remove_submenu_page( $menu_slug, $submenu_slug ) {
global $submenu;
if ( !isset( $submenu[$menu_slug] ) )
return false;
foreach ( $submenu[$menu_slug] as $i => $item ) {
if ( $submenu_slug == $item[2] ) {
unset( $submenu[$menu_slug][$i] );
return $item;
}
}
return false;
}
It first checks to see if the menu exists, in our case themes.php
and then it goes over each item from, say, Appearance
, each being an array itself, then it looks for the third item, which corresponds to the [2] => customize.php...
item, so, of course it’s not going to find our customize.php
, that’s why we need to provide it the full link. It’s just a simple mistake.