/class.bcn_breadcrumb_trail.php

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