Instantly share code, notes, and snippets.
Created
November 28, 2014 06:59
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save mcaskill/d22334bfc954bc91a12b to your computer and use it in GitHub Desktop.
WordPress : Displays a location breadcrumb trail using WordPress navigation menus.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Displays a location breadcrumb trail. | |
* | |
* Uses WordPress nav menu items and theme locations to build | |
* a breadcrumb trail. Overall, this template functon replicates | |
* most of the procedures found in `wp_nav_menu()`. | |
* | |
* @param array $args { | |
* Optional. Array of nav menu arguments. | |
* | |
* @type string $menu Desired menu. Accepts (matching in order) id, slug, name. Default empty. | |
* @type string $theme_location Theme location to be used. Must be registered with register_nav_menu() | |
* in order to be selectable by the user. | |
* @type string $container Whether to wrap the ul, and what to wrap it with. Default 'nav'. | |
* @type string $container_class Class that is applied to the container. Default 'menu-{menu slug}-container'. | |
* @type string $container_id The ID that is applied to the container. Default empty. | |
* @type callback|bool $fallback_cb If the menu doesn't exists, a callback function will fire. | |
* Default is 'wp_page_menu'. Set to false for no fallback. | |
* @type string $title_home Anchor text for the front page. | |
* @type bool $include_home Add front page crumb. | |
* @type bool $include_single Add single post crumb. | |
* @type string $item_before Text before the link text. Default empty. | |
* @type string $item_after Text after the link text. Default empty. | |
* @type string $item_wrap How the item should be rendered. | |
* Uses printf() format with numbered placeholders. | |
* @type string $item_separator How the list items should be separated. Default is an arrow. | |
* @type string $link_before Text before the label. Default empty. | |
* @type string $link_after Text after the label. Default empty. | |
* @type string $link_wrap How the link should be rendered. Default is an anchor with an id, class, and href attribute. | |
* Uses printf() format with numbered placeholders. | |
* @type string $current_wrap How the current label should be rendered. | |
* Uses printf() format with numbered placeholders. | |
* Same numbered placeholders as $link_wrap. | |
* @type bool $echo Whether to echo the menu or return it. Default true. | |
* } | |
* @return mixed Menu output if $echo is false, false if there are no items or no menu was found. | |
*/ | |
function wp_nav_trail( $args = [] ) | |
{ | |
$defaults = [ | |
'echo' => true, | |
'menu' => '', | |
'theme_location' => '', | |
'container' => 'nav', | |
'container_class' => '', | |
'container_id' => '', | |
'fallback_cb' => '', | |
'title_home' => __('Home'), | |
'include_home' => true, | |
'include_single' => false, | |
'item_before' => '', | |
'item_after' => '', | |
'item_wrap' => '<span id="%1$s" class="%2$s" typeof="v:Breadcrumb">%3$s</span>', | |
'item_separator' => ' → ', | |
'link_before' => '', | |
'link_after' => '', | |
'link_wrap' => '<a href="%1$s" rel="v:url" property="v:title">%2$s</a>', | |
'current_wrap' => '<span property="v:title">%2$s</span>' | |
]; | |
$args = wp_parse_args( $args, $defaults ); | |
/** | |
* Filter the arguments used to display a breadcrumb trail. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu_args | |
* | |
* @param array $args Array of wp_nav_trail() arguments. | |
*/ | |
$args = apply_filters( 'wp_nav_trail_args', $args ); | |
$args = (object) $args; | |
/** | |
* Filter whether to short-circuit the wp_nav_trail() output. | |
* | |
* Returning a non-null value to the filter will short-circuit | |
* wp_nav_trail(), echoing that value if $args->echo is true, | |
* returning that value otherwise. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} pre_wp_nav_menu | |
* | |
* @param string|null $output Nav trail output to short-circuit with. Default null. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$nav_menu = apply_filters( 'pre_wp_nav_trail', null, $args ); | |
if ( null !== $nav_menu ) { | |
if ( $args->echo ) { | |
echo $nav_menu; | |
return; | |
} | |
return $nav_menu; | |
} | |
// Get the nav menu based on the requested menu | |
$menu = wp_get_nav_menu_object( $args->menu ); | |
// Get the nav menu based on the theme_location | |
if ( ! $menu && $args->theme_location && ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) ) { | |
$menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] ); | |
} | |
// get the first menu that has items if we still can't find a menu | |
if ( ! $menu && ! $args->theme_location ) { | |
$menus = wp_get_nav_menus([ 'orderby' => 'name' ]); | |
foreach ( $menus as $menu_maybe ) { | |
if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, [ 'update_post_term_cache' => false ] ) ) { | |
$menu = $menu_maybe; | |
break; | |
} | |
} | |
} | |
// If the menu exists, get its items. | |
if ( $menu && ! is_wp_error( $menu ) && ! isset( $menu_items ) ) | |
$menu_items = wp_get_nav_menu_items( $menu->term_id, [ 'update_post_term_cache' => false ] ); | |
/* | |
* If no menu was found: | |
* - Fall back (if one was specified), or bail. | |
* | |
* If no menu items were found: | |
* - Fall back, but only if no theme location was specified. | |
* - Otherwise, bail. | |
*/ | |
if ( ( ! $menu || is_wp_error( $menu ) || ( isset( $menu_items ) && empty( $menu_items ) && ! $args->theme_location ) ) | |
&& $args->fallback_cb && is_callable( $args->fallback_cb ) ) { | |
return call_user_func( $args->fallback_cb, (array) $args ); | |
} | |
if ( ! $menu || is_wp_error( $menu ) ) { | |
return false; | |
} | |
$nav_menu = $items = ''; | |
$show_container = false; | |
if ( $args->container ) { | |
/** | |
* Filter the list of HTML tags that are valid for use as breadcrumb containers. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu_container_allowedtags | |
* | |
* @param array $tags The acceptable HTML tags for use as breadcrumb containers. | |
* Default is array containing 'div' and 'nav'. | |
*/ | |
$allowed_tags = apply_filters( 'wp_nav_trail_container_allowedtags', [ 'div', 'nav' ] ); | |
if ( in_array( $args->container, $allowed_tags ) ) { | |
$show_container = true; | |
$atts = []; | |
$atts['id'] = ( $args->container_id ? esc_attr( $args->container_id ) : '' ); | |
$atts['class'] = ( $args->container_class ? esc_attr( $args->container_class ) : 'trail-' . $menu->slug . '-container' ); | |
$atts['prefix'] = 'v:http://rdf.data-vocabulary.org/#'; | |
/** | |
* Filter the HTML attributes applied to a trail's container. | |
* | |
* @see {method} WordPress\Nav_Menus\Walker_Nav_Menu::start_el() | |
* @see {filter} nav_menu_link_attributes | |
* | |
* @param array $atts { | |
* The HTML attributes applied to the trail's container, empty strings are ignored. | |
* | |
* @type string $id The ID attribute. | |
* @type string $class The class names attribute. | |
* @type string $prefix The prefix attribute. | |
* } | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
$atts = apply_filters( 'nav_trail_container_attributes', $atts, $args ); | |
$attributes = ''; | |
foreach ( $atts as $attr => $value ) { | |
if ( ! empty( $value ) ) { | |
$attributes .= ' ' . $attr . '="' . esc_attr( $value ) . '"'; | |
} | |
} | |
$nav_menu .= '<'. $args->container . $attributes . '>'; | |
} | |
} | |
// Set up the $menu_item variables | |
_wp_menu_item_classes_by_context( $menu_items ); | |
$sorted_menu_items = []; | |
foreach ( (array) $menu_items as $menu_item ) { | |
$sorted_menu_items[ $menu_item->menu_order ] = $menu_item; | |
} | |
unset( $menu_items, $menu_item ); | |
/** | |
* Filter the sorted list of breadcrumb objects before generating the trail's HTML. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu_objects | |
* | |
* @param array $sorted_menu_items The breadcrumnb items, sorted by each breadcrumb's menu order. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$sorted_menu_items = apply_filters( 'wp_nav_trail_objects', $sorted_menu_items, $args ); | |
$items = []; | |
if ( $args->include_home ) { | |
$show_on_front = get_option('show_on_front'); | |
$page_on_front = get_option('page_on_front'); | |
// $page_for_posts = get_option('page_for_posts'); | |
if ( ( $show_on_front === 'page' && is_front_page() ) || ( $show_on_front === 'posts' && is_home() ) ) { | |
// Do nothing | |
} | |
else { | |
$front_page = (object) [ | |
'ID' => $page_on_front, | |
'db_id' => $page_on_front, | |
'object_id' => 0, | |
'object' => 'page', | |
'post_parent' => 0, | |
'post_name' => 'home', | |
'menu_item_parent' => 0, | |
'type' => 'post_type', | |
'title' => $args->title_home, | |
'url' => home_url(), | |
'description' => '', | |
'attr_title' => '', | |
'classes' => [], | |
'target' => '', | |
'xfn' => '', | |
'current' => is_front_page(), | |
'current_item_parent' => false, | |
'current_item_ancestor' => true | |
]; | |
array_unshift( $sorted_menu_items, $front_page ); | |
} | |
} | |
/** @todo $args->include_single */ | |
if ( $args->include_single ) { | |
} | |
foreach ( $sorted_menu_items as $item ) { | |
if ( $item->current_item_ancestor || $item->current_item_parent || $item->current ) { | |
$classes = ( empty( $item->classes ) ? [] : (array) $item->classes ); | |
/** | |
* Filter the CSS class(es) applied to a breadcrumb's <a>. | |
* | |
* @see {method} WordPress\Nav_Menus\Walker_Nav_Menu::start_el() | |
* @see {filter} nav_menu_css_class | |
* | |
* @param array $classes The CSS classes that are applied to the breadcrumb's <a>. | |
* @param object $item The current breadcrumb. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
$class_names = implode( ' ', apply_filters( 'nav_trail_css_class', array_filter( $classes ), $item, $args ) ); | |
/** | |
* Filter the ID applied to a breadcrumb's <a>. | |
* | |
* @see {method} WordPress\Nav_Menus\Walker_Nav_Menu::start_el() | |
* @see {filter} nav_menu_item_id | |
* | |
* @param string $item_id The ID that is applied to the breadcrumb's <a>. | |
* @param object $item The current breadcrumb. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
$id = apply_filters( 'nav_trail_item_id', 'crumb-item-' . $item->ID, $item, $args ); | |
/** | |
* Filter the URL applied to a breadcrumb's <a>. | |
* | |
* @param string $item_id The URL that is applied to the breadcrumb's <a>. | |
* @param object $item The current breadcrumb. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
$url = apply_filters( 'nav_trail_item_url', $item->url, $item, $args ); | |
$title = $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; | |
$format = ( $item->current ? $args->current_wrap : $args->link_wrap ); | |
/** | |
* Filter a breadcrumb's output format. | |
* | |
* @param string $format The breadcrumb's HTML output. | |
* @param object $item Trail item data object. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$format = apply_filters( 'nav_trail_item_format', $format, $item, $args ); | |
$link_output = $args->item_before; | |
$link_output .= sprintf( $format, esc_url( $url ), $title ); | |
$link_output .= $args->item_after; | |
$item_output = sprintf( $args->item_wrap, esc_attr( $id ), esc_attr( $class_names ), $link_output ); | |
/** | |
* Filter a breadcrumb's output. | |
* | |
* @see {method} WordPress\Nav_Menus\Walker_Nav_Menu::start_el() | |
* @see {filter} walker_nav_menu_start_el | |
* | |
* @param string $item_output The breadcrumb's HTML output. | |
* @param object $item Trail item data object. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$items[] = apply_filters( 'nav_trail_item', $item_output, $items, $args ); | |
} | |
} | |
if ( function_exists('first') ) { | |
/** | |
* Filter the first item (most likely the root ancestor). | |
* | |
* @param string $item_output The breadcrumb's HTML output. | |
* @param object $item Trail item data object. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
apply_filters( 'nav_trail_start', first( $items ), reset( $sorted_menu_items ), $args ); | |
} | |
if ( function_exists('last') ) { | |
/** | |
* Filter the last item (most likely the active one). | |
* | |
* @param string $item_output The breadcrumb's HTML output. | |
* @param object $item Trail item data object. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
apply_filters( 'nav_trail_end', last( $items ), end( $sorted_menu_items ), $args ); | |
} | |
/** | |
* Filter the separator applied between each item. | |
* | |
* @param string $separator The URL that is applied to the breadcrumb's <a>. | |
* @param array $items The trail's HTML output. | |
* @param array $args An array of wp_nav_trail() arguments. | |
*/ | |
$separator = apply_filters( 'nav_trail_separator', $args->item_separator, $items, $args ); | |
$items = implode( $separator, $items ); | |
unset( $sorted_menu_items ); | |
/** | |
* Filter the HTML list content for navigation menus. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu_items | |
* | |
* @param string $items The HTML list content for the breadcrumbs. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$items = apply_filters( 'wp_nav_trail_items', $items, $args ); | |
/** | |
* Filter the HTML list content for a specific navigation menu. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu_{$menu->slug}_items | |
* | |
* @param string $items The HTML list content for the breadcrumbs. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$items = apply_filters( "wp_nav_trail_{$menu->slug}_items", $items, $args ); | |
// Don't print any markup if there are no items at this point. | |
if ( empty( $items ) ) { | |
return false; | |
} | |
$nav_menu .= $items; | |
unset( $items ); | |
if ( $show_container ) { | |
$nav_menu .= '</' . $args->container . '>'; | |
} | |
/** | |
* Filter the HTML content for navigation breadcrumb trail. | |
* | |
* @see {function} WordPress\Nav_Menus\wp_nav_menu() | |
* @see {filter} wp_nav_menu | |
* | |
* @param string $nav_menu The HTML content for the navigation breadcrumb trail. | |
* @param object $args An object containing wp_nav_trail() arguments. | |
*/ | |
$nav_menu = apply_filters( 'wp_nav_trail', $nav_menu, $args ); | |
if ( $args->echo ) { | |
echo $nav_menu; | |
} | |
else { | |
return $nav_menu; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment