| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Build Administration Menu. 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 if ( is_network_admin() ) { 10 11 /** 12 * Fires before the administration menu loads in the Network Admin. 13 * 14 * The hook fires before menus and sub-menus are removed based on user privileges. 15 * 16 * @since 3.1.0 17 * @access private 18 */ 19 do_action( '_network_admin_menu' ); 20 } elseif ( is_user_admin() ) { 21 22 /** 23 * Fires before the administration menu loads in the User Admin. 24 * 25 * The hook fires before menus and sub-menus are removed based on user privileges. 26 * 27 * @since 3.1.0 28 * @access private 29 */ 30 do_action( '_user_admin_menu' ); 31 } else { 32 33 /** 34 * Fires before the administration menu loads in the admin. 35 * 36 * The hook fires before menus and sub-menus are removed based on user privileges. 37 * 38 * @since 2.2.0 39 * @access private 40 */ 41 do_action( '_admin_menu' ); 42 } 43 44 /** 45 * @global array $menu 46 * @global array $submenu 47 * @global array $compat 48 */ 49 global $menu, $submenu, $compat; 50 51 // Create list of page plugin hook names. 52 foreach ( $menu as $menu_page ) { 53 $pos = strpos( $menu_page[2], '?' ); 54 55 if ( false !== $pos ) { 56 // Handle post_type=post|page|foo pages. 57 $hook_name = substr( $menu_page[2], 0, $pos ); 58 $hook_args = substr( $menu_page[2], $pos + 1 ); 59 wp_parse_str( $hook_args, $hook_args ); 60 61 // Set the hook name to be the post type. 62 if ( isset( $hook_args['post_type'] ) ) { 63 $hook_name = $hook_args['post_type']; 64 } else { 65 $hook_name = basename( $hook_name, '.php' ); 66 } 67 unset( $hook_args ); 68 } else { 69 $hook_name = basename( $menu_page[2], '.php' ); 70 } 71 72 $hook_name = sanitize_title( $hook_name ); 73 74 if ( isset( $compat[ $hook_name ] ) ) { 75 $hook_name = $compat[ $hook_name ]; 76 } elseif ( ! $hook_name ) { 77 continue; 78 } 79 80 $admin_page_hooks[ $menu_page[2] ] = $hook_name; 81 } 82 unset( $menu_page, $compat ); 83 84 $_wp_submenu_nopriv = array(); 85 $_wp_menu_nopriv = array(); 86 // Loop over submenus and remove pages for which the user does not have privs. 87 foreach ( $submenu as $parent => $sub ) { 88 foreach ( $sub as $index => $data ) { 89 if ( ! current_user_can( $data[1] ) ) { 90 unset( $submenu[ $parent ][ $index ] ); 91 $_wp_submenu_nopriv[ $parent ][ $data[2] ] = true; 92 } 93 } 94 unset( $index, $data ); 95 96 if ( empty( $submenu[ $parent ] ) ) { 97 unset( $submenu[ $parent ] ); 98 } 99 } 100 unset( $sub, $parent ); 101 102 /* 103 * Loop over the top-level menu. 104 * Menus for which the original parent is not accessible due to lack of privileges 105 * will have the next submenu in line be assigned as the new menu parent. 106 */ 107 foreach ( $menu as $id => $data ) { 108 if ( empty( $submenu[ $data[2] ] ) ) { 109 continue; 110 } 111 112 $subs = $submenu[ $data[2] ]; 113 $first_sub = reset( $subs ); 114 $old_parent = $data[2]; 115 $new_parent = $first_sub[2]; 116 117 /* 118 * If the first submenu is not the same as the assigned parent, 119 * make the first submenu the new parent. 120 */ 121 if ( $new_parent !== $old_parent ) { 122 $_wp_real_parent_file[ $old_parent ] = $new_parent; 123 124 $menu[ $id ][2] = $new_parent; 125 126 foreach ( $submenu[ $old_parent ] as $index => $data ) { 127 $submenu[ $new_parent ][ $index ] = $submenu[ $old_parent ][ $index ]; 128 unset( $submenu[ $old_parent ][ $index ] ); 129 } 130 unset( $submenu[ $old_parent ], $index ); 131 132 if ( isset( $_wp_submenu_nopriv[ $old_parent ] ) ) { 133 $_wp_submenu_nopriv[ $new_parent ] = $_wp_submenu_nopriv[ $old_parent ]; 134 } 135 } 136 } 137 unset( $id, $data, $subs, $first_sub, $old_parent, $new_parent ); 138 139 if ( is_network_admin() ) { 140 141 /** 142 * Fires before the administration menu loads in the Network Admin. 143 * 144 * @since 3.1.0 145 * 146 * @param string $context Empty context. 147 */ 148 do_action( 'network_admin_menu', '' ); 149 } elseif ( is_user_admin() ) { 150 151 /** 152 * Fires before the administration menu loads in the User Admin. 153 * 154 * @since 3.1.0 155 * 156 * @param string $context Empty context. 157 */ 158 do_action( 'user_admin_menu', '' ); 159 } else { 160 161 /** 162 * Fires before the administration menu loads in the admin. 163 * 164 * @since 1.5.0 165 * 166 * @param string $context Empty context. 167 */ 168 do_action( 'admin_menu', '' ); 169 } 170 171 /* 172 * Remove menus that have no accessible submenus and require privileges 173 * that the user does not have. Run re-parent loop again. 174 */ 175 foreach ( $menu as $id => $data ) { 176 if ( ! current_user_can( $data[1] ) ) { 177 $_wp_menu_nopriv[ $data[2] ] = true; 178 } 179 180 /* 181 * If there is only one submenu and it is has same destination as the parent, 182 * remove the submenu. 183 */ 184 if ( ! empty( $submenu[ $data[2] ] ) && 1 === count( $submenu[ $data[2] ] ) ) { 185 $subs = $submenu[ $data[2] ]; 186 $first_sub = reset( $subs ); 187 188 if ( $data[2] === $first_sub[2] ) { 189 unset( $submenu[ $data[2] ] ); 190 } 191 } 192 193 // If submenu is empty... 194 if ( empty( $submenu[ $data[2] ] ) ) { 195 // And user doesn't have privs, remove menu. 196 if ( isset( $_wp_menu_nopriv[ $data[2] ] ) ) { 197 unset( $menu[ $id ] ); 198 } 199 } 200 } 201 unset( $id, $data, $subs, $first_sub ); 202 203 /** 204 * Adds a CSS class to a string. 205 * 206 * @since 2.7.0 207 * 208 * @param string $class_to_add The CSS class to add. 209 * @param string $classes The string to add the CSS class to. 210 * @return string The string with the CSS class added. 211 * 212 * @phpstan-template T of string 213 * @phpstan-param T $class_to_add 214 * @phpstan-return ( $classes is empty ? T : non-empty-string ) 215 */ 216 function add_cssclass( $class_to_add, $classes ) { 217 if ( empty( $classes ) ) { 218 return $class_to_add; 219 } 220 221 return $classes . ' ' . $class_to_add; 222 } 223 224 /** 225 * Adds CSS classes for top-level administration menu items. 226 * 227 * The list of added classes includes `.menu-top-first` and `.menu-top-last`. 228 * 229 * @since 2.7.0 230 * 231 * @param array $menu The array of administration menu items. 232 * @return array The array of administration menu items with the CSS classes added. 233 */ 234 function add_menu_classes( $menu ) { 235 $first_item = false; 236 $last_order = false; 237 $items_count = count( $menu ); 238 239 $i = 0; 240 241 foreach ( $menu as $order => $top ) { 242 ++$i; 243 244 if ( 0 === $order ) { // Dashboard is always shown/single. 245 $menu[0][4] = add_cssclass( 'menu-top-first', $top[4] ); 246 $last_order = 0; 247 continue; 248 } 249 250 if ( str_starts_with( $top[2], 'separator' ) && false !== $last_order ) { // If separator. 251 $first_item = true; 252 $classes = $menu[ $last_order ][4]; 253 254 $menu[ $last_order ][4] = add_cssclass( 'menu-top-last', $classes ); 255 continue; 256 } 257 258 if ( $first_item ) { 259 $first_item = false; 260 $classes = $menu[ $order ][4]; 261 262 $menu[ $order ][4] = add_cssclass( 'menu-top-first', $classes ); 263 } 264 265 if ( $i === $items_count ) { // Last item. 266 $classes = $menu[ $order ][4]; 267 268 $menu[ $order ][4] = add_cssclass( 'menu-top-last', $classes ); 269 } 270 271 $last_order = $order; 272 } 273 274 /** 275 * Filters administration menu array with classes added for top-level items. 276 * 277 * @since 2.7.0 278 * 279 * @param array $menu Associative array of administration menu items. 280 */ 281 return apply_filters( 'add_menu_classes', $menu ); 282 } 283 284 uksort( $menu, 'strnatcasecmp' ); // Make it all pretty. 285 286 /** 287 * Filters whether to enable custom ordering of the administration menu. 288 * 289 * See the {@see 'menu_order'} filter for reordering menu items. 290 * 291 * @since 2.8.0 292 * 293 * @param bool $custom Whether custom ordering is enabled. Default false. 294 */ 295 if ( apply_filters( 'custom_menu_order', false ) ) { 296 $menu_order = array(); 297 298 foreach ( $menu as $menu_item ) { 299 $menu_order[] = $menu_item[2]; 300 } 301 unset( $menu_item ); 302 303 $default_menu_order = $menu_order; 304 305 /** 306 * Filters the order of administration menu items. 307 * 308 * A truthy value must first be passed to the {@see 'custom_menu_order'} filter 309 * for this filter to work. Use the following to enable custom menu ordering: 310 * 311 * add_filter( 'custom_menu_order', '__return_true' ); 312 * 313 * @since 2.8.0 314 * 315 * @param array $menu_order An ordered array of menu items. 316 */ 317 $menu_order = apply_filters( 'menu_order', $menu_order ); 318 $menu_order = array_flip( $menu_order ); 319 320 $default_menu_order = array_flip( $default_menu_order ); 321 322 /** 323 * @global array $menu_order 324 * @global array $default_menu_order 325 * 326 * @param array $a 327 * @param array $b 328 * @return int 329 */ 330 function sort_menu( $a, $b ) { 331 global $menu_order, $default_menu_order; 332 333 $a = $a[2]; 334 $b = $b[2]; 335 336 if ( isset( $menu_order[ $a ] ) && ! isset( $menu_order[ $b ] ) ) { 337 return -1; 338 } elseif ( ! isset( $menu_order[ $a ] ) && isset( $menu_order[ $b ] ) ) { 339 return 1; 340 } elseif ( isset( $menu_order[ $a ] ) && isset( $menu_order[ $b ] ) ) { 341 return $menu_order[ $a ] <=> $menu_order[ $b ]; 342 } else { 343 return $default_menu_order[ $a ] <=> $default_menu_order[ $b ]; 344 } 345 } 346 347 usort( $menu, 'sort_menu' ); 348 unset( $menu_order, $default_menu_order ); 349 } 350 351 // Prevent adjacent separators. 352 $prev_menu_was_separator = false; 353 foreach ( $menu as $id => $data ) { 354 if ( false === stristr( $data[4], 'wp-menu-separator' ) ) { 355 356 // This item is not a separator, so falsey the toggler and do nothing. 357 $prev_menu_was_separator = false; 358 } else { 359 360 // The previous item was a separator, so unset this one. 361 if ( true === $prev_menu_was_separator ) { 362 unset( $menu[ $id ] ); 363 } 364 365 // This item is a separator, so truthy the toggler and move on. 366 $prev_menu_was_separator = true; 367 } 368 } 369 unset( $id, $data, $prev_menu_was_separator ); 370 371 // Remove the last menu item if it is a separator. 372 $last_menu_key = array_keys( $menu ); 373 $last_menu_key = array_pop( $last_menu_key ); 374 if ( ! empty( $menu ) && 'wp-menu-separator' === $menu[ $last_menu_key ][4] ) { 375 unset( $menu[ $last_menu_key ] ); 376 } 377 unset( $last_menu_key ); 378 379 if ( ! wp_admin_page_exists() ) { 380 381 /** 382 * Fires when a request is made to access an admin page that does not exist. 383 * 384 * @since 7.2.0 385 */ 386 do_action( 'wp_admin_page_not_found' ); 387 388 wp_die( __( 'The requested page does not exist.' ), 404 ); 389 } elseif ( ! user_can_access_admin_page() ) { 390 391 /** 392 * Fires when access to an admin page is denied. 393 * 394 * @since 2.5.0 395 */ 396 do_action( 'admin_page_access_denied' ); 397 398 wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); 399 } 400 401 $menu = add_menu_classes( $menu );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Oct 15 08:20:32 2026 | Cross-referenced by PHPXref |