nav-menu-template.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. <?php
  2. /**
  3. * Nav Menu API: Template functions
  4. *
  5. * @package WordPress
  6. * @subpackage Nav_Menus
  7. * @since 3.0.0
  8. */
  9. /** Walker_Nav_Menu class */
  10. require_once ABSPATH . WPINC . '/class-walker-nav-menu.php';
  11. /**
  12. * Displays a navigation menu.
  13. *
  14. * @since 3.0.0
  15. * @since 4.7.0 Added the `item_spacing` argument.
  16. *
  17. * @staticvar array $menu_id_slugs
  18. *
  19. * @param array $args {
  20. * Optional. Array of nav menu arguments.
  21. *
  22. * @type int|string|WP_Term $menu Desired menu. Accepts a menu ID, slug, name, or object. Default empty.
  23. * @type string $menu_class CSS class to use for the ul element which forms the menu. Default 'menu'.
  24. * @type string $menu_id The ID that is applied to the ul element which forms the menu.
  25. * Default is the menu slug, incremented.
  26. * @type string $container Whether to wrap the ul, and what to wrap it with. Default 'div'.
  27. * @type string $container_class Class that is applied to the container. Default 'menu-{menu slug}-container'.
  28. * @type string $container_id The ID that is applied to the container. Default empty.
  29. * @type callable|bool $fallback_cb If the menu doesn't exists, a callback function will fire.
  30. * Default is 'wp_page_menu'. Set to false for no fallback.
  31. * @type string $before Text before the link markup. Default empty.
  32. * @type string $after Text after the link markup. Default empty.
  33. * @type string $link_before Text before the link text. Default empty.
  34. * @type string $link_after Text after the link text. Default empty.
  35. * @type bool $echo Whether to echo the menu or return it. Default true.
  36. * @type int $depth How many levels of the hierarchy are to be included. 0 means all. Default 0.
  37. * @type object $walker Instance of a custom walker class. Default empty.
  38. * @type string $theme_location Theme location to be used. Must be registered with register_nav_menu()
  39. * in order to be selectable by the user.
  40. * @type string $items_wrap How the list items should be wrapped. Default is a ul with an id and class.
  41. * Uses printf() format with numbered placeholders.
  42. * @type string $item_spacing Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' or 'discard'. Default 'preserve'.
  43. * }
  44. * @return string|false|void Menu output if $echo is false, false if there are no items or no menu was found.
  45. */
  46. function wp_nav_menu( $args = array() ) {
  47. static $menu_id_slugs = array();
  48. $defaults = array(
  49. 'menu' => '',
  50. 'container' => 'div',
  51. 'container_class' => '',
  52. 'container_id' => '',
  53. 'menu_class' => 'menu',
  54. 'menu_id' => '',
  55. 'echo' => true,
  56. 'fallback_cb' => 'wp_page_menu',
  57. 'before' => '',
  58. 'after' => '',
  59. 'link_before' => '',
  60. 'link_after' => '',
  61. 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
  62. 'item_spacing' => 'preserve',
  63. 'depth' => 0,
  64. 'walker' => '',
  65. 'theme_location' => '',
  66. );
  67. $args = wp_parse_args( $args, $defaults );
  68. if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
  69. // invalid value, fall back to default.
  70. $args['item_spacing'] = $defaults['item_spacing'];
  71. }
  72. /**
  73. * Filters the arguments used to display a navigation menu.
  74. *
  75. * @since 3.0.0
  76. *
  77. * @see wp_nav_menu()
  78. *
  79. * @param array $args Array of wp_nav_menu() arguments.
  80. */
  81. $args = apply_filters( 'wp_nav_menu_args', $args );
  82. $args = (object) $args;
  83. /**
  84. * Filters whether to short-circuit the wp_nav_menu() output.
  85. *
  86. * Returning a non-null value to the filter will short-circuit
  87. * wp_nav_menu(), echoing that value if $args->echo is true,
  88. * returning that value otherwise.
  89. *
  90. * @since 3.9.0
  91. *
  92. * @see wp_nav_menu()
  93. *
  94. * @param string|null $output Nav menu output to short-circuit with. Default null.
  95. * @param stdClass $args An object containing wp_nav_menu() arguments.
  96. */
  97. $nav_menu = apply_filters( 'pre_wp_nav_menu', null, $args );
  98. if ( null !== $nav_menu ) {
  99. if ( $args->echo ) {
  100. echo $nav_menu;
  101. return;
  102. }
  103. return $nav_menu;
  104. }
  105. // Get the nav menu based on the requested menu
  106. $menu = wp_get_nav_menu_object( $args->menu );
  107. // Get the nav menu based on the theme_location
  108. $locations = get_nav_menu_locations();
  109. if ( ! $menu && $args->theme_location && $locations && isset( $locations[ $args->theme_location ] ) ) {
  110. $menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
  111. }
  112. // get the first menu that has items if we still can't find a menu
  113. if ( ! $menu && ! $args->theme_location ) {
  114. $menus = wp_get_nav_menus();
  115. foreach ( $menus as $menu_maybe ) {
  116. $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) );
  117. if ( $menu_items ) {
  118. $menu = $menu_maybe;
  119. break;
  120. }
  121. }
  122. }
  123. if ( empty( $args->menu ) ) {
  124. $args->menu = $menu;
  125. }
  126. // If the menu exists, get its items.
  127. if ( $menu && ! is_wp_error( $menu ) && ! isset( $menu_items ) ) {
  128. $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
  129. }
  130. /*
  131. * If no menu was found:
  132. * - Fall back (if one was specified), or bail.
  133. *
  134. * If no menu items were found:
  135. * - Fall back, but only if no theme location was specified.
  136. * - Otherwise, bail.
  137. */
  138. if ( ( ! $menu || is_wp_error( $menu ) || ( isset( $menu_items ) && empty( $menu_items ) && ! $args->theme_location ) )
  139. && isset( $args->fallback_cb ) && $args->fallback_cb && is_callable( $args->fallback_cb ) ) {
  140. return call_user_func( $args->fallback_cb, (array) $args );
  141. }
  142. if ( ! $menu || is_wp_error( $menu ) ) {
  143. return false;
  144. }
  145. $nav_menu = '';
  146. $items = '';
  147. $show_container = false;
  148. if ( $args->container ) {
  149. /**
  150. * Filters the list of HTML tags that are valid for use as menu containers.
  151. *
  152. * @since 3.0.0
  153. *
  154. * @param array $tags The acceptable HTML tags for use as menu containers.
  155. * Default is array containing 'div' and 'nav'.
  156. */
  157. $allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );
  158. if ( is_string( $args->container ) && in_array( $args->container, $allowed_tags ) ) {
  159. $show_container = true;
  160. $class = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-' . $menu->slug . '-container"';
  161. $id = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
  162. $nav_menu .= '<' . $args->container . $id . $class . '>';
  163. }
  164. }
  165. // Set up the $menu_item variables
  166. _wp_menu_item_classes_by_context( $menu_items );
  167. $sorted_menu_items = array();
  168. $menu_items_with_children = array();
  169. foreach ( (array) $menu_items as $menu_item ) {
  170. $sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
  171. if ( $menu_item->menu_item_parent ) {
  172. $menu_items_with_children[ $menu_item->menu_item_parent ] = true;
  173. }
  174. }
  175. // Add the menu-item-has-children class where applicable
  176. if ( $menu_items_with_children ) {
  177. foreach ( $sorted_menu_items as &$menu_item ) {
  178. if ( isset( $menu_items_with_children[ $menu_item->ID ] ) ) {
  179. $menu_item->classes[] = 'menu-item-has-children';
  180. }
  181. }
  182. }
  183. unset( $menu_items, $menu_item );
  184. /**
  185. * Filters the sorted list of menu item objects before generating the menu's HTML.
  186. *
  187. * @since 3.1.0
  188. *
  189. * @param array $sorted_menu_items The menu items, sorted by each menu item's menu order.
  190. * @param stdClass $args An object containing wp_nav_menu() arguments.
  191. */
  192. $sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );
  193. $items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
  194. unset( $sorted_menu_items );
  195. // Attributes
  196. if ( ! empty( $args->menu_id ) ) {
  197. $wrap_id = $args->menu_id;
  198. } else {
  199. $wrap_id = 'menu-' . $menu->slug;
  200. while ( in_array( $wrap_id, $menu_id_slugs ) ) {
  201. if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) ) {
  202. $wrap_id = preg_replace( '#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
  203. } else {
  204. $wrap_id = $wrap_id . '-1';
  205. }
  206. }
  207. }
  208. $menu_id_slugs[] = $wrap_id;
  209. $wrap_class = $args->menu_class ? $args->menu_class : '';
  210. /**
  211. * Filters the HTML list content for navigation menus.
  212. *
  213. * @since 3.0.0
  214. *
  215. * @see wp_nav_menu()
  216. *
  217. * @param string $items The HTML list content for the menu items.
  218. * @param stdClass $args An object containing wp_nav_menu() arguments.
  219. */
  220. $items = apply_filters( 'wp_nav_menu_items', $items, $args );
  221. /**
  222. * Filters the HTML list content for a specific navigation menu.
  223. *
  224. * @since 3.0.0
  225. *
  226. * @see wp_nav_menu()
  227. *
  228. * @param string $items The HTML list content for the menu items.
  229. * @param stdClass $args An object containing wp_nav_menu() arguments.
  230. */
  231. $items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );
  232. // Don't print any markup if there are no items at this point.
  233. if ( empty( $items ) ) {
  234. return false;
  235. }
  236. $nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
  237. unset( $items );
  238. if ( $show_container ) {
  239. $nav_menu .= '</' . $args->container . '>';
  240. }
  241. /**
  242. * Filters the HTML content for navigation menus.
  243. *
  244. * @since 3.0.0
  245. *
  246. * @see wp_nav_menu()
  247. *
  248. * @param string $nav_menu The HTML content for the navigation menu.
  249. * @param stdClass $args An object containing wp_nav_menu() arguments.
  250. */
  251. $nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );
  252. if ( $args->echo ) {
  253. echo $nav_menu;
  254. } else {
  255. return $nav_menu;
  256. }
  257. }
  258. /**
  259. * Add the class property classes for the current context, if applicable.
  260. *
  261. * @access private
  262. * @since 3.0.0
  263. *
  264. * @global WP_Query $wp_query WordPress Query object.
  265. * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
  266. *
  267. * @param array $menu_items The current menu item objects to which to add the class property information.
  268. */
  269. function _wp_menu_item_classes_by_context( &$menu_items ) {
  270. global $wp_query, $wp_rewrite;
  271. $queried_object = $wp_query->get_queried_object();
  272. $queried_object_id = (int) $wp_query->queried_object_id;
  273. $active_object = '';
  274. $active_ancestor_item_ids = array();
  275. $active_parent_item_ids = array();
  276. $active_parent_object_ids = array();
  277. $possible_taxonomy_ancestors = array();
  278. $possible_object_parents = array();
  279. $home_page_id = (int) get_option( 'page_for_posts' );
  280. if ( $wp_query->is_singular && ! empty( $queried_object->post_type ) && ! is_post_type_hierarchical( $queried_object->post_type ) ) {
  281. foreach ( (array) get_object_taxonomies( $queried_object->post_type ) as $taxonomy ) {
  282. if ( is_taxonomy_hierarchical( $taxonomy ) ) {
  283. $term_hierarchy = _get_term_hierarchy( $taxonomy );
  284. $terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) );
  285. if ( is_array( $terms ) ) {
  286. $possible_object_parents = array_merge( $possible_object_parents, $terms );
  287. $term_to_ancestor = array();
  288. foreach ( (array) $term_hierarchy as $anc => $descs ) {
  289. foreach ( (array) $descs as $desc ) {
  290. $term_to_ancestor[ $desc ] = $anc;
  291. }
  292. }
  293. foreach ( $terms as $desc ) {
  294. do {
  295. $possible_taxonomy_ancestors[ $taxonomy ][] = $desc;
  296. if ( isset( $term_to_ancestor[ $desc ] ) ) {
  297. $_desc = $term_to_ancestor[ $desc ];
  298. unset( $term_to_ancestor[ $desc ] );
  299. $desc = $_desc;
  300. } else {
  301. $desc = 0;
  302. }
  303. } while ( ! empty( $desc ) );
  304. }
  305. }
  306. }
  307. }
  308. } elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
  309. $term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
  310. $term_to_ancestor = array();
  311. foreach ( (array) $term_hierarchy as $anc => $descs ) {
  312. foreach ( (array) $descs as $desc ) {
  313. $term_to_ancestor[ $desc ] = $anc;
  314. }
  315. }
  316. $desc = $queried_object->term_id;
  317. do {
  318. $possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc;
  319. if ( isset( $term_to_ancestor[ $desc ] ) ) {
  320. $_desc = $term_to_ancestor[ $desc ];
  321. unset( $term_to_ancestor[ $desc ] );
  322. $desc = $_desc;
  323. } else {
  324. $desc = 0;
  325. }
  326. } while ( ! empty( $desc ) );
  327. }
  328. $possible_object_parents = array_filter( $possible_object_parents );
  329. $front_page_url = home_url();
  330. $front_page_id = (int) get_option( 'page_on_front' );
  331. $privacy_policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
  332. foreach ( (array) $menu_items as $key => $menu_item ) {
  333. $menu_items[ $key ]->current = false;
  334. $classes = (array) $menu_item->classes;
  335. $classes[] = 'menu-item';
  336. $classes[] = 'menu-item-type-' . $menu_item->type;
  337. $classes[] = 'menu-item-object-' . $menu_item->object;
  338. // This menu item is set as the 'Front Page'.
  339. if ( 'post_type' === $menu_item->type && $front_page_id === (int) $menu_item->object_id ) {
  340. $classes[] = 'menu-item-home';
  341. }
  342. // This menu item is set as the 'Privacy Policy Page'.
  343. if ( 'post_type' === $menu_item->type && $privacy_policy_page_id === (int) $menu_item->object_id ) {
  344. $classes[] = 'menu-item-privacy-policy';
  345. }
  346. // if the menu item corresponds to a taxonomy term for the currently-queried non-hierarchical post object
  347. if ( $wp_query->is_singular && 'taxonomy' == $menu_item->type && in_array( $menu_item->object_id, $possible_object_parents ) ) {
  348. $active_parent_object_ids[] = (int) $menu_item->object_id;
  349. $active_parent_item_ids[] = (int) $menu_item->db_id;
  350. $active_object = $queried_object->post_type;
  351. // if the menu item corresponds to the currently-queried post or taxonomy object
  352. } elseif (
  353. $menu_item->object_id == $queried_object_id &&
  354. (
  355. ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && $wp_query->is_home && $home_page_id == $menu_item->object_id ) ||
  356. ( 'post_type' == $menu_item->type && $wp_query->is_singular ) ||
  357. ( 'taxonomy' == $menu_item->type && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) && $queried_object->taxonomy == $menu_item->object )
  358. )
  359. ) {
  360. $classes[] = 'current-menu-item';
  361. $menu_items[ $key ]->current = true;
  362. $_anc_id = (int) $menu_item->db_id;
  363. while (
  364. ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  365. ! in_array( $_anc_id, $active_ancestor_item_ids )
  366. ) {
  367. $active_ancestor_item_ids[] = $_anc_id;
  368. }
  369. if ( 'post_type' == $menu_item->type && 'page' == $menu_item->object ) {
  370. // Back compat classes for pages to match wp_page_menu()
  371. $classes[] = 'page_item';
  372. $classes[] = 'page-item-' . $menu_item->object_id;
  373. $classes[] = 'current_page_item';
  374. }
  375. $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  376. $active_parent_object_ids[] = (int) $menu_item->post_parent;
  377. $active_object = $menu_item->object;
  378. // if the menu item corresponds to the currently-queried post type archive
  379. } elseif (
  380. 'post_type_archive' == $menu_item->type &&
  381. is_post_type_archive( array( $menu_item->object ) )
  382. ) {
  383. $classes[] = 'current-menu-item';
  384. $menu_items[ $key ]->current = true;
  385. $_anc_id = (int) $menu_item->db_id;
  386. while (
  387. ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  388. ! in_array( $_anc_id, $active_ancestor_item_ids )
  389. ) {
  390. $active_ancestor_item_ids[] = $_anc_id;
  391. }
  392. $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  393. // if the menu item corresponds to the currently-requested URL
  394. } elseif ( 'custom' == $menu_item->object && isset( $_SERVER['HTTP_HOST'] ) ) {
  395. $_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
  396. //if it is the customize page then it will strips the query var off the url before entering the comparison block.
  397. if ( is_customize_preview() ) {
  398. $_root_relative_current = strtok( untrailingslashit( $_SERVER['REQUEST_URI'] ), '?' );
  399. }
  400. $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current );
  401. $raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
  402. $item_url = set_url_scheme( untrailingslashit( $raw_item_url ) );
  403. $_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) );
  404. $matches = array(
  405. $current_url,
  406. urldecode( $current_url ),
  407. $_indexless_current,
  408. urldecode( $_indexless_current ),
  409. $_root_relative_current,
  410. urldecode( $_root_relative_current ),
  411. );
  412. if ( $raw_item_url && in_array( $item_url, $matches ) ) {
  413. $classes[] = 'current-menu-item';
  414. $menu_items[ $key ]->current = true;
  415. $_anc_id = (int) $menu_item->db_id;
  416. while (
  417. ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  418. ! in_array( $_anc_id, $active_ancestor_item_ids )
  419. ) {
  420. $active_ancestor_item_ids[] = $_anc_id;
  421. }
  422. if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ) ) ) {
  423. // Back compat for home link to match wp_page_menu()
  424. $classes[] = 'current_page_item';
  425. }
  426. $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  427. $active_parent_object_ids[] = (int) $menu_item->post_parent;
  428. $active_object = $menu_item->object;
  429. // give front page item current-menu-item class when extra query arguments involved
  430. } elseif ( $item_url == $front_page_url && is_front_page() ) {
  431. $classes[] = 'current-menu-item';
  432. }
  433. if ( untrailingslashit( $item_url ) == home_url() ) {
  434. $classes[] = 'menu-item-home';
  435. }
  436. }
  437. // back-compat with wp_page_menu: add "current_page_parent" to static home page link for any non-page query
  438. if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id ) {
  439. $classes[] = 'current_page_parent';
  440. }
  441. $menu_items[ $key ]->classes = array_unique( $classes );
  442. }
  443. $active_ancestor_item_ids = array_filter( array_unique( $active_ancestor_item_ids ) );
  444. $active_parent_item_ids = array_filter( array_unique( $active_parent_item_ids ) );
  445. $active_parent_object_ids = array_filter( array_unique( $active_parent_object_ids ) );
  446. // set parent's class
  447. foreach ( (array) $menu_items as $key => $parent_item ) {
  448. $classes = (array) $parent_item->classes;
  449. $menu_items[ $key ]->current_item_ancestor = false;
  450. $menu_items[ $key ]->current_item_parent = false;
  451. if (
  452. isset( $parent_item->type ) &&
  453. (
  454. // ancestral post object
  455. (
  456. 'post_type' == $parent_item->type &&
  457. ! empty( $queried_object->post_type ) &&
  458. is_post_type_hierarchical( $queried_object->post_type ) &&
  459. in_array( $parent_item->object_id, $queried_object->ancestors ) &&
  460. $parent_item->object != $queried_object->ID
  461. ) ||
  462. // ancestral term
  463. (
  464. 'taxonomy' == $parent_item->type &&
  465. isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
  466. in_array( $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
  467. (
  468. ! isset( $queried_object->term_id ) ||
  469. $parent_item->object_id != $queried_object->term_id
  470. )
  471. )
  472. )
  473. ) {
  474. $classes[] = empty( $queried_object->taxonomy ) ? 'current-' . $queried_object->post_type . '-ancestor' : 'current-' . $queried_object->taxonomy . '-ancestor';
  475. }
  476. if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
  477. $classes[] = 'current-menu-ancestor';
  478. $menu_items[ $key ]->current_item_ancestor = true;
  479. }
  480. if ( in_array( $parent_item->db_id, $active_parent_item_ids ) ) {
  481. $classes[] = 'current-menu-parent';
  482. $menu_items[ $key ]->current_item_parent = true;
  483. }
  484. if ( in_array( $parent_item->object_id, $active_parent_object_ids ) ) {
  485. $classes[] = 'current-' . $active_object . '-parent';
  486. }
  487. if ( 'post_type' == $parent_item->type && 'page' == $parent_item->object ) {
  488. // Back compat classes for pages to match wp_page_menu()
  489. if ( in_array( 'current-menu-parent', $classes ) ) {
  490. $classes[] = 'current_page_parent';
  491. }
  492. if ( in_array( 'current-menu-ancestor', $classes ) ) {
  493. $classes[] = 'current_page_ancestor';
  494. }
  495. }
  496. $menu_items[ $key ]->classes = array_unique( $classes );
  497. }
  498. }
  499. /**
  500. * Retrieve the HTML list content for nav menu items.
  501. *
  502. * @uses Walker_Nav_Menu to create HTML list content.
  503. * @since 3.0.0
  504. *
  505. * @param array $items The menu items, sorted by each menu item's menu order.
  506. * @param int $depth Depth of the item in reference to parents.
  507. * @param stdClass $r An object containing wp_nav_menu() arguments.
  508. * @return string The HTML list content for the menu items.
  509. */
  510. function walk_nav_menu_tree( $items, $depth, $r ) {
  511. $walker = ( empty( $r->walker ) ) ? new Walker_Nav_Menu : $r->walker;
  512. return $walker->walk( $items, $depth, $r );
  513. }
  514. /**
  515. * Prevents a menu item ID from being used more than once.
  516. *
  517. * @since 3.0.1
  518. * @access private
  519. *
  520. * @staticvar array $used_ids
  521. * @param string $id
  522. * @param object $item
  523. * @return string
  524. */
  525. function _nav_menu_item_id_use_once( $id, $item ) {
  526. static $_used_ids = array();
  527. if ( in_array( $item->ID, $_used_ids ) ) {
  528. return '';
  529. }
  530. $_used_ids[] = $item->ID;
  531. return $id;
  532. }