diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 2af08fba70af9..ee6acda7ea976 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -1527,7 +1527,7 @@ function wp_ajax_edit_comment() { function wp_ajax_add_menu_item() { check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' ); - if ( ! current_user_can( 'edit_theme_options' ) ) { + if ( ! current_user_can( 'manage_nav_menus' ) ) { wp_die( -1 ); } @@ -1879,7 +1879,7 @@ function wp_ajax_update_welcome_panel() { * @since 3.1.0 */ function wp_ajax_menu_get_metabox() { - if ( ! current_user_can( 'edit_theme_options' ) ) { + if ( ! current_user_can( 'manage_nav_menus' ) ) { wp_die( -1 ); } @@ -1966,7 +1966,7 @@ function wp_ajax_wp_link_ajax() { * @since 3.1.0 */ function wp_ajax_menu_locations_save() { - if ( ! current_user_can( 'edit_theme_options' ) ) { + if ( ! current_user_can( 'manage_nav_menus' ) ) { wp_die( -1 ); } @@ -2022,7 +2022,7 @@ function wp_ajax_meta_box_order() { * @since 3.1.0 */ function wp_ajax_menu_quick_search() { - if ( ! current_user_can( 'edit_theme_options' ) ) { + if ( ! current_user_can( 'manage_nav_menus' ) ) { wp_die( -1 ); } diff --git a/src/wp-admin/menu.php b/src/wp-admin/menu.php index 57d94c75e26f2..dd922d9a85f85 100644 --- a/src/wp-admin/menu.php +++ b/src/wp-admin/menu.php @@ -204,7 +204,16 @@ $menu[59] = array( '', 'read', 'separator2', '', 'wp-menu-separator' ); -$appearance_capability = current_user_can( 'switch_themes' ) ? 'switch_themes' : 'edit_theme_options'; +$themes_capability = current_user_can( 'switch_themes' ) ? 'switch_themes' : 'edit_theme_options'; +$appearance_capability = $themes_capability; + +if ( + ! current_user_can( $appearance_capability ) && + current_user_can( 'manage_nav_menus' ) && + ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) +) { + $appearance_capability = 'manage_nav_menus'; +} $menu[60] = array( __( 'Appearance' ), $appearance_capability, 'themes.php', '', 'menu-top menu-icon-appearance', 'menu-appearance', 'dashicons-admin-appearance' ); @@ -222,7 +231,7 @@ } /* translators: %s: Number of available theme updates. */ - $submenu['themes.php'][5] = array( sprintf( __( 'Themes %s' ), $count ), $appearance_capability, 'themes.php' ); + $submenu['themes.php'][5] = array( sprintf( __( 'Themes %s' ), $count ), $themes_capability, 'themes.php' ); if ( wp_is_block_theme() ) { $submenu['themes.php'][6] = array( _x( 'Editor', 'site editor menu item' ), 'edit_theme_options', 'site-editor.php' ); @@ -248,20 +257,20 @@ } if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) { - $submenu['themes.php'][10] = array( __( 'Menus' ), 'edit_theme_options', 'nav-menus.php' ); + $submenu['themes.php'][10] = array( __( 'Menus' ), 'manage_nav_menus', 'nav-menus.php' ); } if ( current_theme_supports( 'custom-header' ) && current_user_can( 'customize' ) ) { $customize_header_url = add_query_arg( array( 'autofocus' => array( 'control' => 'header_image' ) ), $customize_url ); - $submenu['themes.php'][15] = array( _x( 'Header', 'custom image header' ), $appearance_capability, esc_url( $customize_header_url ), '', 'hide-if-no-customize' ); + $submenu['themes.php'][15] = array( _x( 'Header', 'custom image header' ), $themes_capability, esc_url( $customize_header_url ), '', 'hide-if-no-customize' ); } if ( current_theme_supports( 'custom-background' ) && current_user_can( 'customize' ) ) { $customize_background_url = add_query_arg( array( 'autofocus' => array( 'control' => 'background_image' ) ), $customize_url ); - $submenu['themes.php'][20] = array( _x( 'Background', 'custom background' ), $appearance_capability, esc_url( $customize_background_url ), '', 'hide-if-no-customize' ); + $submenu['themes.php'][20] = array( _x( 'Background', 'custom background' ), $themes_capability, esc_url( $customize_background_url ), '', 'hide-if-no-customize' ); } -unset( $customize_url, $appearance_capability ); +unset( $customize_url, $appearance_capability, $themes_capability ); // Add 'Theme File Editor' to the bottom of the Appearance (non-block themes) or Tools (block themes) menu. if ( ! is_multisite() ) { diff --git a/src/wp-admin/nav-menus.php b/src/wp-admin/nav-menus.php index 808574f1250d6..66e2397738ed9 100644 --- a/src/wp-admin/nav-menus.php +++ b/src/wp-admin/nav-menus.php @@ -20,10 +20,10 @@ } // Permissions check. -if ( ! current_user_can( 'edit_theme_options' ) ) { +if ( ! current_user_can( 'manage_nav_menus' ) ) { wp_die( '

' . __( 'You need a higher level of permission.' ) . '

' . - '

' . __( 'Sorry, you are not allowed to edit theme options on this site.' ) . '

', + '

' . __( 'Sorry, you are not allowed to manage navigation menus on this site.' ) . '

', 403 ); } diff --git a/src/wp-includes/admin-bar.php b/src/wp-includes/admin-bar.php index 50868b11a2870..00d25b473b2c1 100644 --- a/src/wp-includes/admin-bar.php +++ b/src/wp-includes/admin-bar.php @@ -1151,11 +1151,7 @@ function wp_admin_bar_appearance_menu( $wp_admin_bar ) { ); } - if ( ! current_user_can( 'edit_theme_options' ) ) { - return; - } - - if ( current_theme_supports( 'widgets' ) ) { + if ( current_user_can( 'edit_theme_options' ) && current_theme_supports( 'widgets' ) ) { $wp_admin_bar->add_node( array( 'parent' => 'appearance', @@ -1166,7 +1162,7 @@ function wp_admin_bar_appearance_menu( $wp_admin_bar ) { ); } - if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) { + if ( current_user_can( 'manage_nav_menus' ) && ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) ) { $wp_admin_bar->add_node( array( 'parent' => 'appearance', @@ -1177,7 +1173,7 @@ function wp_admin_bar_appearance_menu( $wp_admin_bar ) { ); } - if ( current_theme_supports( 'custom-background' ) ) { + if ( current_user_can( 'edit_theme_options' ) && current_theme_supports( 'custom-background' ) ) { $wp_admin_bar->add_node( array( 'parent' => 'appearance', @@ -1191,7 +1187,7 @@ function wp_admin_bar_appearance_menu( $wp_admin_bar ) { ); } - if ( current_theme_supports( 'custom-header' ) ) { + if ( current_user_can( 'edit_theme_options' ) && current_theme_supports( 'custom-header' ) ) { $wp_admin_bar->add_node( array( 'parent' => 'appearance', diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php index 028e61ec414a8..61b762d27dcc4 100644 --- a/src/wp-includes/capabilities.php +++ b/src/wp-includes/capabilities.php @@ -34,6 +34,7 @@ * `edit_app_password`, `delete_app_passwords`, `delete_app_password`, * and `update_https` capabilities. * @since 6.7.0 Added the `edit_block_binding` capability. + * @since 7.1.0 Added the `manage_nav_menus` capability. * * @global array $post_type_meta_caps Used to get post type meta capabilities. * @@ -136,6 +137,13 @@ function map_meta_cap( $cap, $user_id, ...$args ) { break; } + // Route nav_menu_item edit/delete through manage_nav_menus + // instead of the post type's primitive edit_theme_options caps. + if ( 'nav_menu_item' === $post->post_type ) { + $caps = map_meta_cap( 'manage_nav_menus', $user_id ); + break; + } + if ( ! $post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we would re-call map_meta_cap here. @@ -239,6 +247,13 @@ function map_meta_cap( $cap, $user_id, ...$args ) { break; } + // Route nav_menu_item edit/delete through manage_nav_menus + // instead of the post type's primitive edit_theme_options caps. + if ( 'nav_menu_item' === $post->post_type ) { + $caps = map_meta_cap( 'manage_nav_menus', $user_id ); + break; + } + if ( ! $post_type->map_meta_cap ) { $caps[] = $post_type->cap->$cap; // Prior to 3.1 we would re-call map_meta_cap here. @@ -698,6 +713,9 @@ function map_meta_cap( $cap, $user_id, ...$args ) { case 'customize': $caps[] = 'edit_theme_options'; break; + case 'manage_nav_menus': + $caps[] = 'edit_theme_options'; + break; case 'delete_site': if ( is_multisite() ) { $caps[] = 'manage_options'; diff --git a/src/wp-includes/class-wp-customize-control.php b/src/wp-includes/class-wp-customize-control.php index 43f0ac6d4ca64..cb6e340c28699 100644 --- a/src/wp-includes/class-wp-customize-control.php +++ b/src/wp-includes/class-wp-customize-control.php @@ -636,7 +636,7 @@ protected function render_content() { echo $dropdown; ?> - allow_addition && current_user_can( 'publish_pages' ) && current_user_can( 'edit_theme_options' ) ) : // Currently tied to menus functionality. ?> + allow_addition && current_user_can( 'publish_pages' ) && current_user_can( 'manage_nav_menus' ) ) : // Currently tied to menus functionality. ?>