196 _wp_menu_item_classes_by_context( $menu_items ); |
196 _wp_menu_item_classes_by_context( $menu_items ); |
197 |
197 |
198 $sorted_menu_items = array(); |
198 $sorted_menu_items = array(); |
199 $menu_items_with_children = array(); |
199 $menu_items_with_children = array(); |
200 foreach ( (array) $menu_items as $menu_item ) { |
200 foreach ( (array) $menu_items as $menu_item ) { |
|
201 /* |
|
202 * Fix invalid `menu_item_parent`. See: https://core.trac.wordpress.org/ticket/56926. |
|
203 * Compare as strings. Plugins may change the ID to a string. |
|
204 */ |
|
205 if ( (string) $menu_item->ID === (string) $menu_item->menu_item_parent ) { |
|
206 $menu_item->menu_item_parent = 0; |
|
207 } |
|
208 |
201 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item; |
209 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item; |
202 if ( $menu_item->menu_item_parent ) { |
210 if ( $menu_item->menu_item_parent ) { |
203 $menu_items_with_children[ $menu_item->menu_item_parent ] = true; |
211 $menu_items_with_children[ $menu_item->menu_item_parent ] = true; |
204 } |
212 } |
205 } |
213 } |
331 $term_hierarchy = _get_term_hierarchy( $taxonomy ); |
339 $term_hierarchy = _get_term_hierarchy( $taxonomy ); |
332 $terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) ); |
340 $terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) ); |
333 if ( is_array( $terms ) ) { |
341 if ( is_array( $terms ) ) { |
334 $possible_object_parents = array_merge( $possible_object_parents, $terms ); |
342 $possible_object_parents = array_merge( $possible_object_parents, $terms ); |
335 $term_to_ancestor = array(); |
343 $term_to_ancestor = array(); |
336 foreach ( (array) $term_hierarchy as $anc => $descs ) { |
344 foreach ( (array) $term_hierarchy as $ancestor => $descendents ) { |
337 foreach ( (array) $descs as $desc ) { |
345 foreach ( (array) $descendents as $desc ) { |
338 $term_to_ancestor[ $desc ] = $anc; |
346 $term_to_ancestor[ $desc ] = $ancestor; |
339 } |
347 } |
340 } |
348 } |
341 |
349 |
342 foreach ( $terms as $desc ) { |
350 foreach ( $terms as $desc ) { |
343 do { |
351 do { |
355 } |
363 } |
356 } |
364 } |
357 } elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) { |
365 } elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) { |
358 $term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy ); |
366 $term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy ); |
359 $term_to_ancestor = array(); |
367 $term_to_ancestor = array(); |
360 foreach ( (array) $term_hierarchy as $anc => $descs ) { |
368 foreach ( (array) $term_hierarchy as $ancestor => $descendents ) { |
361 foreach ( (array) $descs as $desc ) { |
369 foreach ( (array) $descendents as $desc ) { |
362 $term_to_ancestor[ $desc ] = $anc; |
370 $term_to_ancestor[ $desc ] = $ancestor; |
363 } |
371 } |
364 } |
372 } |
365 $desc = $queried_object->term_id; |
373 $desc = $queried_object->term_id; |
366 do { |
374 do { |
367 $possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc; |
375 $possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc; |
408 $active_parent_item_ids[] = (int) $menu_item->db_id; |
416 $active_parent_item_ids[] = (int) $menu_item->db_id; |
409 $active_object = $queried_object->post_type; |
417 $active_object = $queried_object->post_type; |
410 |
418 |
411 // If the menu item corresponds to the currently queried post or taxonomy object. |
419 // If the menu item corresponds to the currently queried post or taxonomy object. |
412 } elseif ( |
420 } elseif ( |
413 $menu_item->object_id == $queried_object_id |
421 (int) $menu_item->object_id === $queried_object_id |
414 && ( |
422 && ( |
415 ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type |
423 ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type |
416 && $wp_query->is_home && $home_page_id == $menu_item->object_id ) |
424 && $wp_query->is_home && $home_page_id === (int) $menu_item->object_id ) |
417 || ( 'post_type' === $menu_item->type && $wp_query->is_singular ) |
425 || ( 'post_type' === $menu_item->type && $wp_query->is_singular ) |
418 || ( 'taxonomy' === $menu_item->type |
426 || ( 'taxonomy' === $menu_item->type |
419 && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) |
427 && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) |
420 && $queried_object->taxonomy == $menu_item->object ) |
428 && $queried_object->taxonomy === $menu_item->object ) |
421 ) |
429 ) |
422 ) { |
430 ) { |
423 $classes[] = 'current-menu-item'; |
431 $classes[] = 'current-menu-item'; |
424 $menu_items[ $key ]->current = true; |
432 $menu_items[ $key ]->current = true; |
425 $_anc_id = (int) $menu_item->db_id; |
433 $ancestor_id = (int) $menu_item->db_id; |
426 |
434 |
427 while ( |
435 while ( |
428 ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) |
436 ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) ) |
429 && ! in_array( $_anc_id, $active_ancestor_item_ids, true ) |
437 && ! in_array( $ancestor_id, $active_ancestor_item_ids, true ) |
430 ) { |
438 ) { |
431 $active_ancestor_item_ids[] = $_anc_id; |
439 $active_ancestor_item_ids[] = $ancestor_id; |
432 } |
440 } |
433 |
441 |
434 if ( 'post_type' === $menu_item->type && 'page' === $menu_item->object ) { |
442 if ( 'post_type' === $menu_item->type && 'page' === $menu_item->object ) { |
435 // Back compat classes for pages to match wp_page_menu(). |
443 // Back compat classes for pages to match wp_page_menu(). |
436 $classes[] = 'page_item'; |
444 $classes[] = 'page_item'; |
447 'post_type_archive' === $menu_item->type |
455 'post_type_archive' === $menu_item->type |
448 && is_post_type_archive( array( $menu_item->object ) ) |
456 && is_post_type_archive( array( $menu_item->object ) ) |
449 ) { |
457 ) { |
450 $classes[] = 'current-menu-item'; |
458 $classes[] = 'current-menu-item'; |
451 $menu_items[ $key ]->current = true; |
459 $menu_items[ $key ]->current = true; |
452 $_anc_id = (int) $menu_item->db_id; |
460 $ancestor_id = (int) $menu_item->db_id; |
453 |
461 |
454 while ( |
462 while ( |
455 ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) |
463 ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) ) |
456 && ! in_array( $_anc_id, $active_ancestor_item_ids, true ) |
464 && ! in_array( $ancestor_id, $active_ancestor_item_ids, true ) |
457 ) { |
465 ) { |
458 $active_ancestor_item_ids[] = $_anc_id; |
466 $active_ancestor_item_ids[] = $ancestor_id; |
459 } |
467 } |
460 |
468 |
461 $active_parent_item_ids[] = (int) $menu_item->menu_item_parent; |
469 $active_parent_item_ids[] = (int) $menu_item->menu_item_parent; |
462 |
470 |
463 // If the menu item corresponds to the currently requested URL. |
471 // If the menu item corresponds to the currently requested URL. |
484 ); |
492 ); |
485 |
493 |
486 if ( $raw_item_url && in_array( $item_url, $matches, true ) ) { |
494 if ( $raw_item_url && in_array( $item_url, $matches, true ) ) { |
487 $classes[] = 'current-menu-item'; |
495 $classes[] = 'current-menu-item'; |
488 $menu_items[ $key ]->current = true; |
496 $menu_items[ $key ]->current = true; |
489 $_anc_id = (int) $menu_item->db_id; |
497 $ancestor_id = (int) $menu_item->db_id; |
490 |
498 |
491 while ( |
499 while ( |
492 ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) |
500 ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) ) |
493 && ! in_array( $_anc_id, $active_ancestor_item_ids, true ) |
501 && ! in_array( $ancestor_id, $active_ancestor_item_ids, true ) |
494 ) { |
502 ) { |
495 $active_ancestor_item_ids[] = $_anc_id; |
503 $active_ancestor_item_ids[] = $ancestor_id; |
496 } |
504 } |
497 |
505 |
498 if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ), true ) ) { |
506 if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ), true ) ) { |
499 // Back compat for home link to match wp_page_menu(). |
507 // Back compat for home link to match wp_page_menu(). |
500 $classes[] = 'current_page_item'; |
508 $classes[] = 'current_page_item'; |
502 $active_parent_item_ids[] = (int) $menu_item->menu_item_parent; |
510 $active_parent_item_ids[] = (int) $menu_item->menu_item_parent; |
503 $active_parent_object_ids[] = (int) $menu_item->post_parent; |
511 $active_parent_object_ids[] = (int) $menu_item->post_parent; |
504 $active_object = $menu_item->object; |
512 $active_object = $menu_item->object; |
505 |
513 |
506 // Give front page item the 'current-menu-item' class when extra query arguments are involved. |
514 // Give front page item the 'current-menu-item' class when extra query arguments are involved. |
507 } elseif ( $item_url == $front_page_url && is_front_page() ) { |
515 } elseif ( $item_url === $front_page_url && is_front_page() ) { |
508 $classes[] = 'current-menu-item'; |
516 $classes[] = 'current-menu-item'; |
509 } |
517 } |
510 |
518 |
511 if ( untrailingslashit( $item_url ) == home_url() ) { |
519 if ( untrailingslashit( $item_url ) === home_url() ) { |
512 $classes[] = 'menu-item-home'; |
520 $classes[] = 'menu-item-home'; |
513 } |
521 } |
514 } |
522 } |
515 |
523 |
516 // Back-compat with wp_page_menu(): add "current_page_parent" to static home page link for any non-page query. |
524 // Back-compat with wp_page_menu(): add "current_page_parent" to static home page link for any non-page query. |
517 if ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type |
525 if ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type |
518 && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id |
526 && empty( $wp_query->is_page ) && $home_page_id === (int) $menu_item->object_id |
519 ) { |
527 ) { |
520 $classes[] = 'current_page_parent'; |
528 $classes[] = 'current_page_parent'; |
521 } |
529 } |
522 |
530 |
523 $menu_items[ $key ]->classes = array_unique( $classes ); |
531 $menu_items[ $key ]->classes = array_unique( $classes ); |
539 ( |
547 ( |
540 'post_type' === $parent_item->type |
548 'post_type' === $parent_item->type |
541 && ! empty( $queried_object->post_type ) |
549 && ! empty( $queried_object->post_type ) |
542 && is_post_type_hierarchical( $queried_object->post_type ) |
550 && is_post_type_hierarchical( $queried_object->post_type ) |
543 && in_array( (int) $parent_item->object_id, $queried_object->ancestors, true ) |
551 && in_array( (int) $parent_item->object_id, $queried_object->ancestors, true ) |
544 && $parent_item->object != $queried_object->ID |
552 && (int) $parent_item->object_id !== $queried_object->ID |
545 ) || |
553 ) || |
546 |
554 |
547 // Ancestral term. |
555 // Ancestral term. |
548 ( |
556 ( |
549 'taxonomy' === $parent_item->type |
557 'taxonomy' === $parent_item->type |
550 && isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) |
558 && isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) |
551 && in_array( (int) $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ], true ) |
559 && in_array( (int) $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ], true ) |
552 && ( |
560 && ( |
553 ! isset( $queried_object->term_id ) || |
561 ! isset( $queried_object->term_id ) || |
554 $parent_item->object_id != $queried_object->term_id |
562 (int) $parent_item->object_id !== $queried_object->term_id |
555 ) |
563 ) |
556 ) |
564 ) |
557 ) |
565 ) |
558 ) { |
566 ) { |
559 if ( ! empty( $queried_object->taxonomy ) ) { |
567 if ( ! empty( $queried_object->taxonomy ) ) { |
601 * @param int $depth Depth of the item in reference to parents. |
609 * @param int $depth Depth of the item in reference to parents. |
602 * @param stdClass $args An object containing wp_nav_menu() arguments. |
610 * @param stdClass $args An object containing wp_nav_menu() arguments. |
603 * @return string The HTML list content for the menu items. |
611 * @return string The HTML list content for the menu items. |
604 */ |
612 */ |
605 function walk_nav_menu_tree( $items, $depth, $args ) { |
613 function walk_nav_menu_tree( $items, $depth, $args ) { |
606 $walker = ( empty( $args->walker ) ) ? new Walker_Nav_Menu : $args->walker; |
614 $walker = ( empty( $args->walker ) ) ? new Walker_Nav_Menu() : $args->walker; |
607 |
615 |
608 return $walker->walk( $items, $depth, $args ); |
616 return $walker->walk( $items, $depth, $args ); |
609 } |
617 } |
610 |
618 |
611 /** |
619 /** |
627 |
635 |
628 $_used_ids[] = $item->ID; |
636 $_used_ids[] = $item->ID; |
629 |
637 |
630 return $id; |
638 return $id; |
631 } |
639 } |
|
640 |
|
641 /** |
|
642 * Remove the `menu-item-has-children` class from bottom level menu items. |
|
643 * |
|
644 * This runs on the {@see 'nav_menu_css_class'} filter. The $args and $depth |
|
645 * parameters were added after the filter was originally introduced in |
|
646 * WordPress 3.0.0 so this needs to allow for cases in which the filter is |
|
647 * called without them. |
|
648 * |
|
649 * @see https://core.trac.wordpress.org/ticket/56926 |
|
650 * |
|
651 * @since 6.2.0 |
|
652 * |
|
653 * @param string[] $classes Array of the CSS classes that are applied to the menu item's `<li>` element. |
|
654 * @param WP_Post $menu_item The current menu item object. |
|
655 * @param stdClass|false $args An object of wp_nav_menu() arguments. Default false ($args unspecified when filter is called). |
|
656 * @param int|false $depth Depth of menu item. Default false ($depth unspecified when filter is called). |
|
657 * @return string[] Modified nav menu classes. |
|
658 */ |
|
659 function wp_nav_menu_remove_menu_item_has_children_class( $classes, $menu_item, $args = false, $depth = false ) { |
|
660 /* |
|
661 * Account for the filter being called without the $args or $depth parameters. |
|
662 * |
|
663 * This occurs when a theme uses a custom walker calling the `nav_menu_css_class` |
|
664 * filter using the legacy formats prior to the introduction of the $args and |
|
665 * $depth parameters. |
|
666 * |
|
667 * As both of these parameters are required for this function to determine |
|
668 * both the current and maximum depth of the menu tree, the function does not |
|
669 * attempt to remove the `menu-item-has-children` class if these parameters |
|
670 * are not set. |
|
671 */ |
|
672 if ( false === $depth || false === $args ) { |
|
673 return $classes; |
|
674 } |
|
675 |
|
676 // Max-depth is 1-based. |
|
677 $max_depth = isset( $args->depth ) ? (int) $args->depth : 0; |
|
678 // Depth is 0-based so needs to be increased by one. |
|
679 $depth = $depth + 1; |
|
680 |
|
681 // Complete menu tree is displayed. |
|
682 if ( 0 === $max_depth ) { |
|
683 return $classes; |
|
684 } |
|
685 |
|
686 /* |
|
687 * Remove the `menu-item-has-children` class from bottom level menu items. |
|
688 * -1 is used to display all menu items in one level so the class should |
|
689 * be removed from all menu items. |
|
690 */ |
|
691 if ( -1 === $max_depth || $depth >= $max_depth ) { |
|
692 $classes = array_diff( $classes, array( 'menu-item-has-children' ) ); |
|
693 } |
|
694 |
|
695 return $classes; |
|
696 } |