1<?php
2/**
3 * Displays Administration Menu.
4 *
5 * @package WordPress
6 * @subpackage Administration
7 */
8
9/**
10 * The current page.
11 *
12 * @global string $self
13 */
14$self = preg_replace( '|^.*/wp-admin/network/|i', '', $_SERVER['PHP_SELF'] );
15$self = preg_replace( '|^.*/wp-admin/|i', '', $self );
16$self = preg_replace( '|^.*/plugins/|i', '', $self );
17$self = preg_replace( '|^.*/mu-plugins/|i', '', $self );
18
19/**
20 * For when admin-header is included from within a function.
21 *
22 * @global array  $menu
23 * @global array  $submenu
24 * @global string $parent_file
25 * @global string $submenu_file
26 */
27global $menu, $submenu, $parent_file, $submenu_file;
28
29/**
30 * Filters the parent file of an admin menu sub-menu item.
31 *
32 * Allows plugins to move sub-menu items around.
33 *
34 * @since MU (3.0.0)
35 *
36 * @param string $parent_file The parent file.
37 */
38$parent_file = apply_filters( 'parent_file', $parent_file );
39
40/**
41 * Filters the file of an admin menu sub-menu item.
42 *
43 * @since 4.4.0
44 *
45 * @param string $submenu_file The submenu file.
46 * @param string $parent_file  The submenu item's parent file.
47 */
48$submenu_file = apply_filters( 'submenu_file', $submenu_file, $parent_file );
49
50get_admin_page_parent();
51
52/**
53 * Display menu.
54 *
55 * @access private
56 * @since 2.7.0
57 *
58 * @global string $self
59 * @global string $parent_file
60 * @global string $submenu_file
61 * @global string $plugin_page
62 * @global string $typenow
63 *
64 * @param array $menu
65 * @param array $submenu
66 * @param bool  $submenu_as_parent
67 */
68function _wp_menu_output( $menu, $submenu, $submenu_as_parent = true ) {
69	global $self, $parent_file, $submenu_file, $plugin_page, $typenow;
70
71	$first = true;
72	// 0 = menu_title, 1 = capability, 2 = menu_slug, 3 = page_title, 4 = classes, 5 = hookname, 6 = icon_url.
73	foreach ( $menu as $key => $item ) {
74		$admin_is_parent = false;
75		$class           = array();
76		$aria_attributes = '';
77		$aria_hidden     = '';
78		$is_separator    = false;
79
80		if ( $first ) {
81			$class[] = 'wp-first-item';
82			$first   = false;
83		}
84
85		$submenu_items = array();
86		if ( ! empty( $submenu[ $item[2] ] ) ) {
87			$class[]       = 'wp-has-submenu';
88			$submenu_items = $submenu[ $item[2] ];
89		}
90
91		if ( ( $parent_file && $item[2] === $parent_file ) || ( empty( $typenow ) && $self === $item[2] ) ) {
92			if ( ! empty( $submenu_items ) ) {
93				$class[] = 'wp-has-current-submenu wp-menu-open';
94			} else {
95				$class[]          = 'current';
96				$aria_attributes .= 'aria-current="page"';
97			}
98		} else {
99			$class[] = 'wp-not-current-submenu';
100			if ( ! empty( $submenu_items ) ) {
101				$aria_attributes .= 'aria-haspopup="true"';
102			}
103		}
104
105		if ( ! empty( $item[4] ) ) {
106			$class[] = esc_attr( $item[4] );
107		}
108
109		$class     = $class ? ' class="' . implode( ' ', $class ) . '"' : '';
110		$id        = ! empty( $item[5] ) ? ' id="' . preg_replace( '|[^a-zA-Z0-9_:.]|', '-', $item[5] ) . '"' : '';
111		$img       = '';
112		$img_style = '';
113		$img_class = ' dashicons-before';
114
115		if ( false !== strpos( $class, 'wp-menu-separator' ) ) {
116			$is_separator = true;
117		}
118
119		/*
120		 * If the string 'none' (previously 'div') is passed instead of a URL, don't output
121		 * the default menu image so an icon can be added to div.wp-menu-image as background
122		 * with CSS. Dashicons and base64-encoded data:image/svg_xml URIs are also handled
123		 * as special cases.
124		 */
125		if ( ! empty( $item[6] ) ) {
126			$img = '<img src="' . $item[6] . '" alt="" />';
127
128			if ( 'none' === $item[6] || 'div' === $item[6] ) {
129				$img = '<br />';
130			} elseif ( 0 === strpos( $item[6], 'data:image/svg+xml;base64,' ) ) {
131				$img       = '<br />';
132				$img_style = ' style="background-image:url(\'' . esc_attr( $item[6] ) . '\')"';
133				$img_class = ' svg';
134			} elseif ( 0 === strpos( $item[6], 'dashicons-' ) ) {
135				$img       = '<br />';
136				$img_class = ' dashicons-before ' . sanitize_html_class( $item[6] );
137			}
138		}
139		$arrow = '<div class="wp-menu-arrow"><div></div></div>';
140
141		$title = wptexturize( $item[0] );
142
143		// Hide separators from screen readers.
144		if ( $is_separator ) {
145			$aria_hidden = ' aria-hidden="true"';
146		}
147
148		echo "\n\t<li$class$id$aria_hidden>";
149
150		if ( $is_separator ) {
151			echo '<div class="separator"></div>';
152		} elseif ( $submenu_as_parent && ! empty( $submenu_items ) ) {
153			$submenu_items = array_values( $submenu_items );  // Re-index.
154			$menu_hook     = get_plugin_page_hook( $submenu_items[0][2], $item[2] );
155			$menu_file     = $submenu_items[0][2];
156			$pos           = strpos( $menu_file, '?' );
157
158			if ( false !== $pos ) {
159				$menu_file = substr( $menu_file, 0, $pos );
160			}
161
162			if ( ! empty( $menu_hook )
163				|| ( ( 'index.php' !== $submenu_items[0][2] )
164					&& file_exists( WP_PLUGIN_DIR . "/$menu_file" )
165					&& ! file_exists( ABSPATH . "/wp-admin/$menu_file" ) )
166			) {
167				$admin_is_parent = true;
168				echo "<a href='admin.php?page={$submenu_items[0][2]}'$class $aria_attributes>$arrow<div class='wp-menu-image$img_class'$img_style aria-hidden='true'>$img</div><div class='wp-menu-name'>$title</div></a>";
169			} else {
170				echo "\n\t<a href='{$submenu_items[0][2]}'$class $aria_attributes>$arrow<div class='wp-menu-image$img_class'$img_style aria-hidden='true'>$img</div><div class='wp-menu-name'>$title</div></a>";
171			}
172		} elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) {
173			$menu_hook = get_plugin_page_hook( $item[2], 'admin.php' );
174			$menu_file = $item[2];
175			$pos       = strpos( $menu_file, '?' );
176
177			if ( false !== $pos ) {
178				$menu_file = substr( $menu_file, 0, $pos );
179			}
180
181			if ( ! empty( $menu_hook )
182				|| ( ( 'index.php' !== $item[2] )
183					&& file_exists( WP_PLUGIN_DIR . "/$menu_file" )
184					&& ! file_exists( ABSPATH . "/wp-admin/$menu_file" ) )
185			) {
186				$admin_is_parent = true;
187				echo "\n\t<a href='admin.php?page={$item[2]}'$class $aria_attributes>$arrow<div class='wp-menu-image$img_class'$img_style aria-hidden='true'>$img</div><div class='wp-menu-name'>{$item[0]}</div></a>";
188			} else {
189				echo "\n\t<a href='{$item[2]}'$class $aria_attributes>$arrow<div class='wp-menu-image$img_class'$img_style aria-hidden='true'>$img</div><div class='wp-menu-name'>{$item[0]}</div></a>";
190			}
191		}
192
193		if ( ! empty( $submenu_items ) ) {
194			echo "\n\t<ul class='wp-submenu wp-submenu-wrap'>";
195			echo "<li class='wp-submenu-head' aria-hidden='true'>{$item[0]}</li>";
196
197			$first = true;
198
199			// 0 = menu_title, 1 = capability, 2 = menu_slug, 3 = page_title, 4 = classes.
200			foreach ( $submenu_items as $sub_key => $sub_item ) {
201				if ( ! current_user_can( $sub_item[1] ) ) {
202					continue;
203				}
204
205				$class           = array();
206				$aria_attributes = '';
207
208				if ( $first ) {
209					$class[] = 'wp-first-item';
210					$first   = false;
211				}
212
213				$menu_file = $item[2];
214				$pos       = strpos( $menu_file, '?' );
215
216				if ( false !== $pos ) {
217					$menu_file = substr( $menu_file, 0, $pos );
218				}
219
220				// Handle current for post_type=post|page|foo pages, which won't match $self.
221				$self_type = ! empty( $typenow ) ? $self . '?post_type=' . $typenow : 'nothing';
222
223				if ( isset( $submenu_file ) ) {
224					if ( $submenu_file === $sub_item[2] ) {
225						$class[]          = 'current';
226						$aria_attributes .= ' aria-current="page"';
227					}
228					// If plugin_page is set the parent must either match the current page or not physically exist.
229					// This allows plugin pages with the same hook to exist under different parents.
230				} elseif (
231					( ! isset( $plugin_page ) && $self === $sub_item[2] )
232					|| ( isset( $plugin_page ) && $plugin_page === $sub_item[2]
233						&& ( $item[2] === $self_type || $item[2] === $self || file_exists( $menu_file ) === false ) )
234				) {
235					$class[]          = 'current';
236					$aria_attributes .= ' aria-current="page"';
237				}
238
239				if ( ! empty( $sub_item[4] ) ) {
240					$class[] = esc_attr( $sub_item[4] );
241				}
242
243				$class = $class ? ' class="' . implode( ' ', $class ) . '"' : '';
244
245				$menu_hook = get_plugin_page_hook( $sub_item[2], $item[2] );
246				$sub_file  = $sub_item[2];
247				$pos       = strpos( $sub_file, '?' );
248				if ( false !== $pos ) {
249					$sub_file = substr( $sub_file, 0, $pos );
250				}
251
252				$title = wptexturize( $sub_item[0] );
253
254				if ( ! empty( $menu_hook )
255					|| ( ( 'index.php' !== $sub_item[2] )
256						&& file_exists( WP_PLUGIN_DIR . "/$sub_file" )
257						&& ! file_exists( ABSPATH . "/wp-admin/$sub_file" ) )
258				) {
259					// If admin.php is the current page or if the parent exists as a file in the plugins or admin directory.
260					if ( ( ! $admin_is_parent && file_exists( WP_PLUGIN_DIR . "/$menu_file" ) && ! is_dir( WP_PLUGIN_DIR . "/{$item[2]}" ) ) || file_exists( $menu_file ) ) {
261						$sub_item_url = add_query_arg( array( 'page' => $sub_item[2] ), $item[2] );
262					} else {
263						$sub_item_url = add_query_arg( array( 'page' => $sub_item[2] ), 'admin.php' );
264					}
265
266					$sub_item_url = esc_url( $sub_item_url );
267					echo "<li$class><a href='$sub_item_url'$class$aria_attributes>$title</a></li>";
268				} else {
269					echo "<li$class><a href='{$sub_item[2]}'$class$aria_attributes>$title</a></li>";
270				}
271			}
272			echo '</ul>';
273		}
274		echo '</li>';
275	}
276
277	echo '<li id="collapse-menu" class="hide-if-no-js">' .
278		'<button type="button" id="collapse-button" aria-label="' . esc_attr__( 'Collapse Main menu' ) . '" aria-expanded="true">' .
279		'<span class="collapse-button-icon" aria-hidden="true"></span>' .
280		'<span class="collapse-button-label">' . __( 'Collapse menu' ) . '</span>' .
281		'</button></li>';
282}
283
284?>
285
286<div id="adminmenumain" role="navigation" aria-label="<?php esc_attr_e( 'Main menu' ); ?>">
287<a href="#wpbody-content" class="screen-reader-shortcut"><?php _e( 'Skip to main content' ); ?></a>
288<a href="#wp-toolbar" class="screen-reader-shortcut"><?php _e( 'Skip to toolbar' ); ?></a>
289<div id="adminmenuback"></div>
290<div id="adminmenuwrap">
291<ul id="adminmenu">
292
293<?php
294
295_wp_menu_output( $menu, $submenu );
296/**
297 * Fires after the admin menu has been output.
298 *
299 * @since 2.5.0
300 */
301do_action( 'adminmenu' );
302
303?>
304</ul>
305</div>
306</div>
307