1<?php 2/** 3 * Taxonomy API: Core category-specific template tags 4 * 5 * @package WordPress 6 * @subpackage Template 7 * @since 1.2.0 8 */ 9 10/** 11 * Retrieves category link URL. 12 * 13 * @since 1.0.0 14 * 15 * @see get_term_link() 16 * 17 * @param int|object $category Category ID or object. 18 * @return string Link on success, empty string if category does not exist. 19 */ 20function get_category_link( $category ) { 21 if ( ! is_object( $category ) ) { 22 $category = (int) $category; 23 } 24 25 $category = get_term_link( $category ); 26 27 if ( is_wp_error( $category ) ) { 28 return ''; 29 } 30 31 return $category; 32} 33 34/** 35 * Retrieves category parents with separator. 36 * 37 * @since 1.2.0 38 * @since 4.8.0 The `$visited` parameter was deprecated and renamed to `$deprecated`. 39 * 40 * @param int $category_id Category ID. 41 * @param bool $link Optional. Whether to format with link. Default false. 42 * @param string $separator Optional. How to separate categories. Default '/'. 43 * @param bool $nicename Optional. Whether to use nice name for display. Default false. 44 * @param array $deprecated Not used. 45 * @return string|WP_Error A list of category parents on success, WP_Error on failure. 46 */ 47function get_category_parents( $category_id, $link = false, $separator = '/', $nicename = false, $deprecated = array() ) { 48 49 if ( ! empty( $deprecated ) ) { 50 _deprecated_argument( __FUNCTION__, '4.8.0' ); 51 } 52 53 $format = $nicename ? 'slug' : 'name'; 54 55 $args = array( 56 'separator' => $separator, 57 'link' => $link, 58 'format' => $format, 59 ); 60 61 return get_term_parents_list( $category_id, 'category', $args ); 62} 63 64/** 65 * Retrieves post categories. 66 * 67 * This tag may be used outside The Loop by passing a post ID as the parameter. 68 * 69 * Note: This function only returns results from the default "category" taxonomy. 70 * For custom taxonomies use get_the_terms(). 71 * 72 * @since 0.71 73 * 74 * @param int $post_id Optional. The post ID. Defaults to current post ID. 75 * @return WP_Term[] Array of WP_Term objects, one for each category assigned to the post. 76 */ 77function get_the_category( $post_id = false ) { 78 $categories = get_the_terms( $post_id, 'category' ); 79 if ( ! $categories || is_wp_error( $categories ) ) { 80 $categories = array(); 81 } 82 83 $categories = array_values( $categories ); 84 85 foreach ( array_keys( $categories ) as $key ) { 86 _make_cat_compat( $categories[ $key ] ); 87 } 88 89 /** 90 * Filters the array of categories to return for a post. 91 * 92 * @since 3.1.0 93 * @since 4.4.0 Added `$post_id` parameter. 94 * 95 * @param WP_Term[] $categories An array of categories to return for the post. 96 * @param int|false $post_id ID of the post. 97 */ 98 return apply_filters( 'get_the_categories', $categories, $post_id ); 99} 100 101/** 102 * Retrieves category name based on category ID. 103 * 104 * @since 0.71 105 * 106 * @param int $cat_ID Category ID. 107 * @return string|WP_Error Category name on success, WP_Error on failure. 108 */ 109function get_the_category_by_ID( $cat_ID ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 110 $cat_ID = (int) $cat_ID; 111 $category = get_term( $cat_ID ); 112 113 if ( is_wp_error( $category ) ) { 114 return $category; 115 } 116 117 return ( $category ) ? $category->name : ''; 118} 119 120/** 121 * Retrieves category list for a post in either HTML list or custom format. 122 * 123 * Generally used for quick, delimited (e.g. comma-separated) lists of categories, 124 * as part of a post entry meta. 125 * 126 * For a more powerful, list-based function, see wp_list_categories(). 127 * 128 * @since 1.5.1 129 * 130 * @see wp_list_categories() 131 * 132 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. 133 * 134 * @param string $separator Optional. Separator between the categories. By default, the links are placed 135 * in an unordered list. An empty string will result in the default behavior. 136 * @param string $parents Optional. How to display the parents. 137 * @param int $post_id Optional. Post ID to retrieve categories. 138 * @return string Category list for a post. 139 */ 140function get_the_category_list( $separator = '', $parents = '', $post_id = false ) { 141 global $wp_rewrite; 142 143 if ( ! is_object_in_taxonomy( get_post_type( $post_id ), 'category' ) ) { 144 /** This filter is documented in wp-includes/category-template.php */ 145 return apply_filters( 'the_category', '', $separator, $parents ); 146 } 147 148 /** 149 * Filters the categories before building the category list. 150 * 151 * @since 4.4.0 152 * 153 * @param WP_Term[] $categories An array of the post's categories. 154 * @param int|bool $post_id ID of the post we're retrieving categories for. 155 * When `false`, we assume the current post in the loop. 156 */ 157 $categories = apply_filters( 'the_category_list', get_the_category( $post_id ), $post_id ); 158 159 if ( empty( $categories ) ) { 160 /** This filter is documented in wp-includes/category-template.php */ 161 return apply_filters( 'the_category', __( 'Uncategorized' ), $separator, $parents ); 162 } 163 164 $rel = ( is_object( $wp_rewrite ) && $wp_rewrite->using_permalinks() ) ? 'rel="category tag"' : 'rel="category"'; 165 166 $thelist = ''; 167 if ( '' === $separator ) { 168 $thelist .= '<ul class="post-categories">'; 169 foreach ( $categories as $category ) { 170 $thelist .= "\n\t<li>"; 171 switch ( strtolower( $parents ) ) { 172 case 'multiple': 173 if ( $category->parent ) { 174 $thelist .= get_category_parents( $category->parent, true, $separator ); 175 } 176 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a></li>'; 177 break; 178 case 'single': 179 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>'; 180 if ( $category->parent ) { 181 $thelist .= get_category_parents( $category->parent, false, $separator ); 182 } 183 $thelist .= $category->name . '</a></li>'; 184 break; 185 case '': 186 default: 187 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a></li>'; 188 } 189 } 190 $thelist .= '</ul>'; 191 } else { 192 $i = 0; 193 foreach ( $categories as $category ) { 194 if ( 0 < $i ) { 195 $thelist .= $separator; 196 } 197 switch ( strtolower( $parents ) ) { 198 case 'multiple': 199 if ( $category->parent ) { 200 $thelist .= get_category_parents( $category->parent, true, $separator ); 201 } 202 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a>'; 203 break; 204 case 'single': 205 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>'; 206 if ( $category->parent ) { 207 $thelist .= get_category_parents( $category->parent, false, $separator ); 208 } 209 $thelist .= "$category->name</a>"; 210 break; 211 case '': 212 default: 213 $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a>'; 214 } 215 ++$i; 216 } 217 } 218 219 /** 220 * Filters the category or list of categories. 221 * 222 * @since 1.2.0 223 * 224 * @param string $thelist List of categories for the current post. 225 * @param string $separator Separator used between the categories. 226 * @param string $parents How to display the category parents. Accepts 'multiple', 227 * 'single', or empty. 228 */ 229 return apply_filters( 'the_category', $thelist, $separator, $parents ); 230} 231 232/** 233 * Checks if the current post is within any of the given categories. 234 * 235 * The given categories are checked against the post's categories' term_ids, names and slugs. 236 * Categories given as integers will only be checked against the post's categories' term_ids. 237 * 238 * Prior to v2.5 of WordPress, category names were not supported. 239 * Prior to v2.7, category slugs were not supported. 240 * Prior to v2.7, only one category could be compared: in_category( $single_category ). 241 * Prior to v2.7, this function could only be used in the WordPress Loop. 242 * As of 2.7, the function can be used anywhere if it is provided a post ID or post object. 243 * 244 * For more information on this and similar theme functions, check out 245 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 246 * Conditional Tags} article in the Theme Developer Handbook. 247 * 248 * @since 1.2.0 249 * @since 2.7.0 The `$post` parameter was added. 250 * 251 * @param int|string|int[]|string[] $category Category ID, name, slug, or array of such 252 * to check against. 253 * @param int|object $post Optional. Post to check instead of the current post. 254 * @return bool True if the current post is in any of the given categories. 255 */ 256function in_category( $category, $post = null ) { 257 if ( empty( $category ) ) { 258 return false; 259 } 260 261 return has_category( $category, $post ); 262} 263 264/** 265 * Displays category list for a post in either HTML list or custom format. 266 * 267 * @since 0.71 268 * 269 * @param string $separator Optional. Separator between the categories. By default, the links are placed 270 * in an unordered list. An empty string will result in the default behavior. 271 * @param string $parents Optional. How to display the parents. 272 * @param int $post_id Optional. Post ID to retrieve categories. 273 */ 274function the_category( $separator = '', $parents = '', $post_id = false ) { 275 echo get_the_category_list( $separator, $parents, $post_id ); 276} 277 278/** 279 * Retrieves category description. 280 * 281 * @since 1.0.0 282 * 283 * @param int $category Optional. Category ID. Defaults to the current category ID. 284 * @return string Category description, if available. 285 */ 286function category_description( $category = 0 ) { 287 return term_description( $category ); 288} 289 290/** 291 * Displays or retrieves the HTML dropdown list of categories. 292 * 293 * The 'hierarchical' argument, which is disabled by default, will override the 294 * depth argument, unless it is true. When the argument is false, it will 295 * display all of the categories. When it is enabled it will use the value in 296 * the 'depth' argument. 297 * 298 * @since 2.1.0 299 * @since 4.2.0 Introduced the `value_field` argument. 300 * @since 4.6.0 Introduced the `required` argument. 301 * 302 * @param array|string $args { 303 * Optional. Array or string of arguments to generate a categories drop-down element. See WP_Term_Query::__construct() 304 * for information on additional accepted arguments. 305 * 306 * @type string $show_option_all Text to display for showing all categories. Default empty. 307 * @type string $show_option_none Text to display for showing no categories. Default empty. 308 * @type string $option_none_value Value to use when no category is selected. Default empty. 309 * @type string $orderby Which column to use for ordering categories. See get_terms() for a list 310 * of accepted values. Default 'id' (term_id). 311 * @type bool $pad_counts See get_terms() for an argument description. Default false. 312 * @type bool|int $show_count Whether to include post counts. Accepts 0, 1, or their bool equivalents. 313 * Default 0. 314 * @type bool|int $echo Whether to echo or return the generated markup. Accepts 0, 1, or their 315 * bool equivalents. Default 1. 316 * @type bool|int $hierarchical Whether to traverse the taxonomy hierarchy. Accepts 0, 1, or their bool 317 * equivalents. Default 0. 318 * @type int $depth Maximum depth. Default 0. 319 * @type int $tab_index Tab index for the select element. Default 0 (no tabindex). 320 * @type string $name Value for the 'name' attribute of the select element. Default 'cat'. 321 * @type string $id Value for the 'id' attribute of the select element. Defaults to the value 322 * of `$name`. 323 * @type string $class Value for the 'class' attribute of the select element. Default 'postform'. 324 * @type int|string $selected Value of the option that should be selected. Default 0. 325 * @type string $value_field Term field that should be used to populate the 'value' attribute 326 * of the option elements. Accepts any valid term field: 'term_id', 'name', 327 * 'slug', 'term_group', 'term_taxonomy_id', 'taxonomy', 'description', 328 * 'parent', 'count'. Default 'term_id'. 329 * @type string|array $taxonomy Name of the taxonomy or taxonomies to retrieve. Default 'category'. 330 * @type bool $hide_if_empty True to skip generating markup if no categories are found. 331 * Default false (create select element even if no categories are found). 332 * @type bool $required Whether the `<select>` element should have the HTML5 'required' attribute. 333 * Default false. 334 * } 335 * @return string HTML dropdown list of categories. 336 */ 337function wp_dropdown_categories( $args = '' ) { 338 $defaults = array( 339 'show_option_all' => '', 340 'show_option_none' => '', 341 'orderby' => 'id', 342 'order' => 'ASC', 343 'show_count' => 0, 344 'hide_empty' => 1, 345 'child_of' => 0, 346 'exclude' => '', 347 'echo' => 1, 348 'selected' => 0, 349 'hierarchical' => 0, 350 'name' => 'cat', 351 'id' => '', 352 'class' => 'postform', 353 'depth' => 0, 354 'tab_index' => 0, 355 'taxonomy' => 'category', 356 'hide_if_empty' => false, 357 'option_none_value' => -1, 358 'value_field' => 'term_id', 359 'required' => false, 360 ); 361 362 $defaults['selected'] = ( is_category() ) ? get_query_var( 'cat' ) : 0; 363 364 // Back compat. 365 if ( isset( $args['type'] ) && 'link' === $args['type'] ) { 366 _deprecated_argument( 367 __FUNCTION__, 368 '3.0.0', 369 sprintf( 370 /* translators: 1: "type => link", 2: "taxonomy => link_category" */ 371 __( '%1$s is deprecated. Use %2$s instead.' ), 372 '<code>type => link</code>', 373 '<code>taxonomy => link_category</code>' 374 ) 375 ); 376 $args['taxonomy'] = 'link_category'; 377 } 378 379 // Parse incoming $args into an array and merge it with $defaults. 380 $parsed_args = wp_parse_args( $args, $defaults ); 381 382 $option_none_value = $parsed_args['option_none_value']; 383 384 if ( ! isset( $parsed_args['pad_counts'] ) && $parsed_args['show_count'] && $parsed_args['hierarchical'] ) { 385 $parsed_args['pad_counts'] = true; 386 } 387 388 $tab_index = $parsed_args['tab_index']; 389 390 $tab_index_attribute = ''; 391 if ( (int) $tab_index > 0 ) { 392 $tab_index_attribute = " tabindex=\"$tab_index\""; 393 } 394 395 // Avoid clashes with the 'name' param of get_terms(). 396 $get_terms_args = $parsed_args; 397 unset( $get_terms_args['name'] ); 398 $categories = get_terms( $get_terms_args ); 399 400 $name = esc_attr( $parsed_args['name'] ); 401 $class = esc_attr( $parsed_args['class'] ); 402 $id = $parsed_args['id'] ? esc_attr( $parsed_args['id'] ) : $name; 403 $required = $parsed_args['required'] ? 'required' : ''; 404 405 if ( ! $parsed_args['hide_if_empty'] || ! empty( $categories ) ) { 406 $output = "<select $required name='$name' id='$id' class='$class' $tab_index_attribute>\n"; 407 } else { 408 $output = ''; 409 } 410 if ( empty( $categories ) && ! $parsed_args['hide_if_empty'] && ! empty( $parsed_args['show_option_none'] ) ) { 411 412 /** 413 * Filters a taxonomy drop-down display element. 414 * 415 * A variety of taxonomy drop-down display elements can be modified 416 * just prior to display via this filter. Filterable arguments include 417 * 'show_option_none', 'show_option_all', and various forms of the 418 * term name. 419 * 420 * @since 1.2.0 421 * 422 * @see wp_dropdown_categories() 423 * 424 * @param string $element Category name. 425 * @param WP_Term|null $category The category object, or null if there's no corresponding category. 426 */ 427 $show_option_none = apply_filters( 'list_cats', $parsed_args['show_option_none'], null ); 428 $output .= "\t<option value='" . esc_attr( $option_none_value ) . "' selected='selected'>$show_option_none</option>\n"; 429 } 430 431 if ( ! empty( $categories ) ) { 432 433 if ( $parsed_args['show_option_all'] ) { 434 435 /** This filter is documented in wp-includes/category-template.php */ 436 $show_option_all = apply_filters( 'list_cats', $parsed_args['show_option_all'], null ); 437 $selected = ( '0' === (string) $parsed_args['selected'] ) ? " selected='selected'" : ''; 438 $output .= "\t<option value='0'$selected>$show_option_all</option>\n"; 439 } 440 441 if ( $parsed_args['show_option_none'] ) { 442 443 /** This filter is documented in wp-includes/category-template.php */ 444 $show_option_none = apply_filters( 'list_cats', $parsed_args['show_option_none'], null ); 445 $selected = selected( $option_none_value, $parsed_args['selected'], false ); 446 $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$selected>$show_option_none</option>\n"; 447 } 448 449 if ( $parsed_args['hierarchical'] ) { 450 $depth = $parsed_args['depth']; // Walk the full depth. 451 } else { 452 $depth = -1; // Flat. 453 } 454 $output .= walk_category_dropdown_tree( $categories, $depth, $parsed_args ); 455 } 456 457 if ( ! $parsed_args['hide_if_empty'] || ! empty( $categories ) ) { 458 $output .= "</select>\n"; 459 } 460 461 /** 462 * Filters the taxonomy drop-down output. 463 * 464 * @since 2.1.0 465 * 466 * @param string $output HTML output. 467 * @param array $parsed_args Arguments used to build the drop-down. 468 */ 469 $output = apply_filters( 'wp_dropdown_cats', $output, $parsed_args ); 470 471 if ( $parsed_args['echo'] ) { 472 echo $output; 473 } 474 475 return $output; 476} 477 478/** 479 * Displays or retrieves the HTML list of categories. 480 * 481 * @since 2.1.0 482 * @since 4.4.0 Introduced the `hide_title_if_empty` and `separator` arguments. 483 * @since 4.4.0 The `current_category` argument was modified to optionally accept an array of values. 484 * 485 * @param array|string $args { 486 * Array of optional arguments. See get_categories(), get_terms(), and WP_Term_Query::__construct() 487 * for information on additional accepted arguments. 488 * 489 * @type int|int[] $current_category ID of category, or array of IDs of categories, that should get the 490 * 'current-cat' class. Default 0. 491 * @type int $depth Category depth. Used for tab indentation. Default 0. 492 * @type bool|int $echo Whether to echo or return the generated markup. Accepts 0, 1, or their 493 * bool equivalents. Default 1. 494 * @type int[]|string $exclude Array or comma/space-separated string of term IDs to exclude. 495 * If `$hierarchical` is true, descendants of `$exclude` terms will also 496 * be excluded; see `$exclude_tree`. See get_terms(). 497 * Default empty string. 498 * @type int[]|string $exclude_tree Array or comma/space-separated string of term IDs to exclude, along 499 * with their descendants. See get_terms(). Default empty string. 500 * @type string $feed Text to use for the feed link. Default 'Feed for all posts filed 501 * under [cat name]'. 502 * @type string $feed_image URL of an image to use for the feed link. Default empty string. 503 * @type string $feed_type Feed type. Used to build feed link. See get_term_feed_link(). 504 * Default empty string (default feed). 505 * @type bool $hide_title_if_empty Whether to hide the `$title_li` element if there are no terms in 506 * the list. Default false (title will always be shown). 507 * @type string $separator Separator between links. Default '<br />'. 508 * @type bool|int $show_count Whether to include post counts. Accepts 0, 1, or their bool equivalents. 509 * Default 0. 510 * @type string $show_option_all Text to display for showing all categories. Default empty string. 511 * @type string $show_option_none Text to display for the 'no categories' option. 512 * Default 'No categories'. 513 * @type string $style The style used to display the categories list. If 'list', categories 514 * will be output as an unordered list. If left empty or another value, 515 * categories will be output separated by `<br>` tags. Default 'list'. 516 * @type string $taxonomy Name of the taxonomy to retrieve. Default 'category'. 517 * @type string $title_li Text to use for the list title `<li>` element. Pass an empty string 518 * to disable. Default 'Categories'. 519 * @type bool|int $use_desc_for_title Whether to use the category description as the title attribute. 520 * Accepts 0, 1, or their bool equivalents. Default 1. 521 * } 522 * @return void|string|false Void if 'echo' argument is true, HTML list of categories if 'echo' is false. 523 * False if the taxonomy does not exist. 524 */ 525function wp_list_categories( $args = '' ) { 526 $defaults = array( 527 'child_of' => 0, 528 'current_category' => 0, 529 'depth' => 0, 530 'echo' => 1, 531 'exclude' => '', 532 'exclude_tree' => '', 533 'feed' => '', 534 'feed_image' => '', 535 'feed_type' => '', 536 'hide_empty' => 1, 537 'hide_title_if_empty' => false, 538 'hierarchical' => true, 539 'order' => 'ASC', 540 'orderby' => 'name', 541 'separator' => '<br />', 542 'show_count' => 0, 543 'show_option_all' => '', 544 'show_option_none' => __( 'No categories' ), 545 'style' => 'list', 546 'taxonomy' => 'category', 547 'title_li' => __( 'Categories' ), 548 'use_desc_for_title' => 1, 549 ); 550 551 $parsed_args = wp_parse_args( $args, $defaults ); 552 553 if ( ! isset( $parsed_args['pad_counts'] ) && $parsed_args['show_count'] && $parsed_args['hierarchical'] ) { 554 $parsed_args['pad_counts'] = true; 555 } 556 557 // Descendants of exclusions should be excluded too. 558 if ( true == $parsed_args['hierarchical'] ) { 559 $exclude_tree = array(); 560 561 if ( $parsed_args['exclude_tree'] ) { 562 $exclude_tree = array_merge( $exclude_tree, wp_parse_id_list( $parsed_args['exclude_tree'] ) ); 563 } 564 565 if ( $parsed_args['exclude'] ) { 566 $exclude_tree = array_merge( $exclude_tree, wp_parse_id_list( $parsed_args['exclude'] ) ); 567 } 568 569 $parsed_args['exclude_tree'] = $exclude_tree; 570 $parsed_args['exclude'] = ''; 571 } 572 573 if ( ! isset( $parsed_args['class'] ) ) { 574 $parsed_args['class'] = ( 'category' === $parsed_args['taxonomy'] ) ? 'categories' : $parsed_args['taxonomy']; 575 } 576 577 if ( ! taxonomy_exists( $parsed_args['taxonomy'] ) ) { 578 return false; 579 } 580 581 $show_option_all = $parsed_args['show_option_all']; 582 $show_option_none = $parsed_args['show_option_none']; 583 584 $categories = get_categories( $parsed_args ); 585 586 $output = ''; 587 588 if ( $parsed_args['title_li'] && 'list' === $parsed_args['style'] 589 && ( ! empty( $categories ) || ! $parsed_args['hide_title_if_empty'] ) 590 ) { 591 $output = '<li class="' . esc_attr( $parsed_args['class'] ) . '">' . $parsed_args['title_li'] . '<ul>'; 592 } 593 594 if ( empty( $categories ) ) { 595 if ( ! empty( $show_option_none ) ) { 596 if ( 'list' === $parsed_args['style'] ) { 597 $output .= '<li class="cat-item-none">' . $show_option_none . '</li>'; 598 } else { 599 $output .= $show_option_none; 600 } 601 } 602 } else { 603 if ( ! empty( $show_option_all ) ) { 604 605 $posts_page = ''; 606 607 // For taxonomies that belong only to custom post types, point to a valid archive. 608 $taxonomy_object = get_taxonomy( $parsed_args['taxonomy'] ); 609 if ( ! in_array( 'post', $taxonomy_object->object_type, true ) && ! in_array( 'page', $taxonomy_object->object_type, true ) ) { 610 foreach ( $taxonomy_object->object_type as $object_type ) { 611 $_object_type = get_post_type_object( $object_type ); 612 613 // Grab the first one. 614 if ( ! empty( $_object_type->has_archive ) ) { 615 $posts_page = get_post_type_archive_link( $object_type ); 616 break; 617 } 618 } 619 } 620 621 // Fallback for the 'All' link is the posts page. 622 if ( ! $posts_page ) { 623 if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) ) { 624 $posts_page = get_permalink( get_option( 'page_for_posts' ) ); 625 } else { 626 $posts_page = home_url( '/' ); 627 } 628 } 629 630 $posts_page = esc_url( $posts_page ); 631 if ( 'list' === $parsed_args['style'] ) { 632 $output .= "<li class='cat-item-all'><a href='$posts_page'>$show_option_all</a></li>"; 633 } else { 634 $output .= "<a href='$posts_page'>$show_option_all</a>"; 635 } 636 } 637 638 if ( empty( $parsed_args['current_category'] ) && ( is_category() || is_tax() || is_tag() ) ) { 639 $current_term_object = get_queried_object(); 640 if ( $current_term_object && $parsed_args['taxonomy'] === $current_term_object->taxonomy ) { 641 $parsed_args['current_category'] = get_queried_object_id(); 642 } 643 } 644 645 if ( $parsed_args['hierarchical'] ) { 646 $depth = $parsed_args['depth']; 647 } else { 648 $depth = -1; // Flat. 649 } 650 $output .= walk_category_tree( $categories, $depth, $parsed_args ); 651 } 652 653 if ( $parsed_args['title_li'] && 'list' === $parsed_args['style'] 654 && ( ! empty( $categories ) || ! $parsed_args['hide_title_if_empty'] ) 655 ) { 656 $output .= '</ul></li>'; 657 } 658 659 /** 660 * Filters the HTML output of a taxonomy list. 661 * 662 * @since 2.1.0 663 * 664 * @param string $output HTML output. 665 * @param array $args An array of taxonomy-listing arguments. See wp_list_categories() 666 * for information on accepted arguments. 667 */ 668 $html = apply_filters( 'wp_list_categories', $output, $args ); 669 670 if ( $parsed_args['echo'] ) { 671 echo $html; 672 } else { 673 return $html; 674 } 675} 676 677/** 678 * Displays a tag cloud. 679 * 680 * Outputs a list of tags in what is called a 'tag cloud', where the size of each tag 681 * is determined by how many times that particular tag has been assigned to posts. 682 * 683 * @since 2.3.0 684 * @since 2.8.0 Added the `taxonomy` argument. 685 * @since 4.8.0 Added the `show_count` argument. 686 * 687 * @param array|string $args { 688 * Optional. Array or string of arguments for displaying a tag cloud. See wp_generate_tag_cloud() 689 * and get_terms() for the full lists of arguments that can be passed in `$args`. 690 * 691 * @type int $number The number of tags to display. Accepts any positive integer 692 * or zero to return all. Default 45. 693 * @type string $link Whether to display term editing links or term permalinks. 694 * Accepts 'edit' and 'view'. Default 'view'. 695 * @type string $post_type The post type. Used to highlight the proper post type menu 696 * on the linked edit page. Defaults to the first post type 697 * associated with the taxonomy. 698 * @type bool $echo Whether or not to echo the return value. Default true. 699 * } 700 * @return void|string|string[] Void if 'echo' argument is true, or on failure. Otherwise, tag cloud 701 * as a string or an array, depending on 'format' argument. 702 */ 703function wp_tag_cloud( $args = '' ) { 704 $defaults = array( 705 'smallest' => 8, 706 'largest' => 22, 707 'unit' => 'pt', 708 'number' => 45, 709 'format' => 'flat', 710 'separator' => "\n", 711 'orderby' => 'name', 712 'order' => 'ASC', 713 'exclude' => '', 714 'include' => '', 715 'link' => 'view', 716 'taxonomy' => 'post_tag', 717 'post_type' => '', 718 'echo' => true, 719 'show_count' => 0, 720 ); 721 722 $args = wp_parse_args( $args, $defaults ); 723 724 $tags = get_terms( 725 array_merge( 726 $args, 727 array( 728 'orderby' => 'count', 729 'order' => 'DESC', 730 ) 731 ) 732 ); // Always query top tags. 733 734 if ( empty( $tags ) || is_wp_error( $tags ) ) { 735 return; 736 } 737 738 foreach ( $tags as $key => $tag ) { 739 if ( 'edit' === $args['link'] ) { 740 $link = get_edit_term_link( $tag->term_id, $tag->taxonomy, $args['post_type'] ); 741 } else { 742 $link = get_term_link( (int) $tag->term_id, $tag->taxonomy ); 743 } 744 745 if ( is_wp_error( $link ) ) { 746 return; 747 } 748 749 $tags[ $key ]->link = $link; 750 $tags[ $key ]->id = $tag->term_id; 751 } 752 753 // Here's where those top tags get sorted according to $args. 754 $return = wp_generate_tag_cloud( $tags, $args ); 755 756 /** 757 * Filters the tag cloud output. 758 * 759 * @since 2.3.0 760 * 761 * @param string|string[] $return Tag cloud as a string or an array, depending on 'format' argument. 762 * @param array $args An array of tag cloud arguments. See wp_tag_cloud() 763 * for information on accepted arguments. 764 */ 765 $return = apply_filters( 'wp_tag_cloud', $return, $args ); 766 767 if ( 'array' === $args['format'] || empty( $args['echo'] ) ) { 768 return $return; 769 } 770 771 echo $return; 772} 773 774/** 775 * Default topic count scaling for tag links. 776 * 777 * @since 2.9.0 778 * 779 * @param int $count Number of posts with that tag. 780 * @return int Scaled count. 781 */ 782function default_topic_count_scale( $count ) { 783 return round( log10( $count + 1 ) * 100 ); 784} 785 786/** 787 * Generates a tag cloud (heatmap) from provided data. 788 * 789 * @todo Complete functionality. 790 * @since 2.3.0 791 * @since 4.8.0 Added the `show_count` argument. 792 * 793 * @param WP_Term[] $tags Array of WP_Term objects to generate the tag cloud for. 794 * @param string|array $args { 795 * Optional. Array or string of arguments for generating a tag cloud. 796 * 797 * @type int $smallest Smallest font size used to display tags. Paired 798 * with the value of `$unit`, to determine CSS text 799 * size unit. Default 8 (pt). 800 * @type int $largest Largest font size used to display tags. Paired 801 * with the value of `$unit`, to determine CSS text 802 * size unit. Default 22 (pt). 803 * @type string $unit CSS text size unit to use with the `$smallest` 804 * and `$largest` values. Accepts any valid CSS text 805 * size unit. Default 'pt'. 806 * @type int $number The number of tags to return. Accepts any 807 * positive integer or zero to return all. 808 * Default 0. 809 * @type string $format Format to display the tag cloud in. Accepts 'flat' 810 * (tags separated with spaces), 'list' (tags displayed 811 * in an unordered list), or 'array' (returns an array). 812 * Default 'flat'. 813 * @type string $separator HTML or text to separate the tags. Default "\n" (newline). 814 * @type string $orderby Value to order tags by. Accepts 'name' or 'count'. 815 * Default 'name'. The {@see 'tag_cloud_sort'} filter 816 * can also affect how tags are sorted. 817 * @type string $order How to order the tags. Accepts 'ASC' (ascending), 818 * 'DESC' (descending), or 'RAND' (random). Default 'ASC'. 819 * @type int|bool $filter Whether to enable filtering of the final output 820 * via {@see 'wp_generate_tag_cloud'}. Default 1. 821 * @type string $topic_count_text Nooped plural text from _n_noop() to supply to 822 * tag counts. Default null. 823 * @type callable $topic_count_text_callback Callback used to generate nooped plural text for 824 * tag counts based on the count. Default null. 825 * @type callable $topic_count_scale_callback Callback used to determine the tag count scaling 826 * value. Default default_topic_count_scale(). 827 * @type bool|int $show_count Whether to display the tag counts. Default 0. Accepts 828 * 0, 1, or their bool equivalents. 829 * } 830 * @return string|string[] Tag cloud as a string or an array, depending on 'format' argument. 831 */ 832function wp_generate_tag_cloud( $tags, $args = '' ) { 833 $defaults = array( 834 'smallest' => 8, 835 'largest' => 22, 836 'unit' => 'pt', 837 'number' => 0, 838 'format' => 'flat', 839 'separator' => "\n", 840 'orderby' => 'name', 841 'order' => 'ASC', 842 'topic_count_text' => null, 843 'topic_count_text_callback' => null, 844 'topic_count_scale_callback' => 'default_topic_count_scale', 845 'filter' => 1, 846 'show_count' => 0, 847 ); 848 849 $args = wp_parse_args( $args, $defaults ); 850 851 $return = ( 'array' === $args['format'] ) ? array() : ''; 852 853 if ( empty( $tags ) ) { 854 return $return; 855 } 856 857 // Juggle topic counts. 858 if ( isset( $args['topic_count_text'] ) ) { 859 // First look for nooped plural support via topic_count_text. 860 $translate_nooped_plural = $args['topic_count_text']; 861 } elseif ( ! empty( $args['topic_count_text_callback'] ) ) { 862 // Look for the alternative callback style. Ignore the previous default. 863 if ( 'default_topic_count_text' === $args['topic_count_text_callback'] ) { 864 /* translators: %s: Number of items (tags). */ 865 $translate_nooped_plural = _n_noop( '%s item', '%s items' ); 866 } else { 867 $translate_nooped_plural = false; 868 } 869 } elseif ( isset( $args['single_text'] ) && isset( $args['multiple_text'] ) ) { 870 // If no callback exists, look for the old-style single_text and multiple_text arguments. 871 // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingle,WordPress.WP.I18n.NonSingularStringLiteralPlural 872 $translate_nooped_plural = _n_noop( $args['single_text'], $args['multiple_text'] ); 873 } else { 874 // This is the default for when no callback, plural, or argument is passed in. 875 /* translators: %s: Number of items (tags). */ 876 $translate_nooped_plural = _n_noop( '%s item', '%s items' ); 877 } 878 879 /** 880 * Filters how the items in a tag cloud are sorted. 881 * 882 * @since 2.8.0 883 * 884 * @param WP_Term[] $tags Ordered array of terms. 885 * @param array $args An array of tag cloud arguments. 886 */ 887 $tags_sorted = apply_filters( 'tag_cloud_sort', $tags, $args ); 888 if ( empty( $tags_sorted ) ) { 889 return $return; 890 } 891 892 if ( $tags_sorted !== $tags ) { 893 $tags = $tags_sorted; 894 unset( $tags_sorted ); 895 } else { 896 if ( 'RAND' === $args['order'] ) { 897 shuffle( $tags ); 898 } else { 899 // SQL cannot save you; this is a second (potentially different) sort on a subset of data. 900 if ( 'name' === $args['orderby'] ) { 901 uasort( $tags, '_wp_object_name_sort_cb' ); 902 } else { 903 uasort( $tags, '_wp_object_count_sort_cb' ); 904 } 905 906 if ( 'DESC' === $args['order'] ) { 907 $tags = array_reverse( $tags, true ); 908 } 909 } 910 } 911 912 if ( $args['number'] > 0 ) { 913 $tags = array_slice( $tags, 0, $args['number'] ); 914 } 915 916 $counts = array(); 917 $real_counts = array(); // For the alt tag. 918 foreach ( (array) $tags as $key => $tag ) { 919 $real_counts[ $key ] = $tag->count; 920 $counts[ $key ] = call_user_func( $args['topic_count_scale_callback'], $tag->count ); 921 } 922 923 $min_count = min( $counts ); 924 $spread = max( $counts ) - $min_count; 925 if ( $spread <= 0 ) { 926 $spread = 1; 927 } 928 $font_spread = $args['largest'] - $args['smallest']; 929 if ( $font_spread < 0 ) { 930 $font_spread = 1; 931 } 932 $font_step = $font_spread / $spread; 933 934 $aria_label = false; 935 /* 936 * Determine whether to output an 'aria-label' attribute with the tag name and count. 937 * When tags have a different font size, they visually convey an important information 938 * that should be available to assistive technologies too. On the other hand, sometimes 939 * themes set up the Tag Cloud to display all tags with the same font size (setting 940 * the 'smallest' and 'largest' arguments to the same value). 941 * In order to always serve the same content to all users, the 'aria-label' gets printed out: 942 * - when tags have a different size 943 * - when the tag count is displayed (for example when users check the checkbox in the 944 * Tag Cloud widget), regardless of the tags font size 945 */ 946 if ( $args['show_count'] || 0 !== $font_spread ) { 947 $aria_label = true; 948 } 949 950 // Assemble the data that will be used to generate the tag cloud markup. 951 $tags_data = array(); 952 foreach ( $tags as $key => $tag ) { 953 $tag_id = isset( $tag->id ) ? $tag->id : $key; 954 955 $count = $counts[ $key ]; 956 $real_count = $real_counts[ $key ]; 957 958 if ( $translate_nooped_plural ) { 959 $formatted_count = sprintf( translate_nooped_plural( $translate_nooped_plural, $real_count ), number_format_i18n( $real_count ) ); 960 } else { 961 $formatted_count = call_user_func( $args['topic_count_text_callback'], $real_count, $tag, $args ); 962 } 963 964 $tags_data[] = array( 965 'id' => $tag_id, 966 'url' => ( '#' !== $tag->link ) ? $tag->link : '#', 967 'role' => ( '#' !== $tag->link ) ? '' : ' role="button"', 968 'name' => $tag->name, 969 'formatted_count' => $formatted_count, 970 'slug' => $tag->slug, 971 'real_count' => $real_count, 972 'class' => 'tag-cloud-link tag-link-' . $tag_id, 973 'font_size' => $args['smallest'] + ( $count - $min_count ) * $font_step, 974 'aria_label' => $aria_label ? sprintf( ' aria-label="%1$s (%2$s)"', esc_attr( $tag->name ), esc_attr( $formatted_count ) ) : '', 975 'show_count' => $args['show_count'] ? '<span class="tag-link-count"> (' . $real_count . ')</span>' : '', 976 ); 977 } 978 979 /** 980 * Filters the data used to generate the tag cloud. 981 * 982 * @since 4.3.0 983 * 984 * @param array[] $tags_data An array of term data arrays for terms used to generate the tag cloud. 985 */ 986 $tags_data = apply_filters( 'wp_generate_tag_cloud_data', $tags_data ); 987 988 $a = array(); 989 990 // Generate the output links array. 991 foreach ( $tags_data as $key => $tag_data ) { 992 $class = $tag_data['class'] . ' tag-link-position-' . ( $key + 1 ); 993 $a[] = sprintf( 994 '<a href="%1$s"%2$s class="%3$s" style="font-size: %4$s;"%5$s>%6$s%7$s</a>', 995 esc_url( $tag_data['url'] ), 996 $tag_data['role'], 997 esc_attr( $class ), 998 esc_attr( str_replace( ',', '.', $tag_data['font_size'] ) . $args['unit'] ), 999 $tag_data['aria_label'], 1000 esc_html( $tag_data['name'] ), 1001 $tag_data['show_count'] 1002 ); 1003 } 1004 1005 switch ( $args['format'] ) { 1006 case 'array': 1007 $return =& $a; 1008 break; 1009 case 'list': 1010 /* 1011 * Force role="list", as some browsers (sic: Safari 10) don't expose to assistive 1012 * technologies the default role when the list is styled with `list-style: none`. 1013 * Note: this is redundant but doesn't harm. 1014 */ 1015 $return = "<ul class='wp-tag-cloud' role='list'>\n\t<li>"; 1016 $return .= implode( "</li>\n\t<li>", $a ); 1017 $return .= "</li>\n</ul>\n"; 1018 break; 1019 default: 1020 $return = implode( $args['separator'], $a ); 1021 break; 1022 } 1023 1024 if ( $args['filter'] ) { 1025 /** 1026 * Filters the generated output of a tag cloud. 1027 * 1028 * The filter is only evaluated if a true value is passed 1029 * to the $filter argument in wp_generate_tag_cloud(). 1030 * 1031 * @since 2.3.0 1032 * 1033 * @see wp_generate_tag_cloud() 1034 * 1035 * @param string[]|string $return String containing the generated HTML tag cloud output 1036 * or an array of tag links if the 'format' argument 1037 * equals 'array'. 1038 * @param WP_Term[] $tags An array of terms used in the tag cloud. 1039 * @param array $args An array of wp_generate_tag_cloud() arguments. 1040 */ 1041 return apply_filters( 'wp_generate_tag_cloud', $return, $tags, $args ); 1042 } else { 1043 return $return; 1044 } 1045} 1046 1047/** 1048 * Serves as a callback for comparing objects based on name. 1049 * 1050 * Used with `uasort()`. 1051 * 1052 * @since 3.1.0 1053 * @access private 1054 * 1055 * @param object $a The first object to compare. 1056 * @param object $b The second object to compare. 1057 * @return int Negative number if `$a->name` is less than `$b->name`, zero if they are equal, 1058 * or greater than zero if `$a->name` is greater than `$b->name`. 1059 */ 1060function _wp_object_name_sort_cb( $a, $b ) { 1061 return strnatcasecmp( $a->name, $b->name ); 1062} 1063 1064/** 1065 * Serves as a callback for comparing objects based on count. 1066 * 1067 * Used with `uasort()`. 1068 * 1069 * @since 3.1.0 1070 * @access private 1071 * 1072 * @param object $a The first object to compare. 1073 * @param object $b The second object to compare. 1074 * @return bool Whether the count value for `$a` is greater than the count value for `$b`. 1075 */ 1076function _wp_object_count_sort_cb( $a, $b ) { 1077 return ( $a->count > $b->count ); 1078} 1079 1080// 1081// Helper functions. 1082// 1083 1084/** 1085 * Retrieves HTML list content for category list. 1086 * 1087 * @since 2.1.0 1088 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 1089 * to the function signature. 1090 * 1091 * @uses Walker_Category to create HTML list content. 1092 * @see Walker::walk() for parameters and return description. 1093 * 1094 * @param mixed ...$args Elements array, maximum hierarchical depth and optional additional arguments. 1095 * @return string 1096 */ 1097function walk_category_tree( ...$args ) { 1098 // The user's options are the third parameter. 1099 if ( empty( $args[2]['walker'] ) || ! ( $args[2]['walker'] instanceof Walker ) ) { 1100 $walker = new Walker_Category; 1101 } else { 1102 /** 1103 * @var Walker $walker 1104 */ 1105 $walker = $args[2]['walker']; 1106 } 1107 return $walker->walk( ...$args ); 1108} 1109 1110/** 1111 * Retrieves HTML dropdown (select) content for category list. 1112 * 1113 * @since 2.1.0 1114 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 1115 * to the function signature. 1116 * 1117 * @uses Walker_CategoryDropdown to create HTML dropdown content. 1118 * @see Walker::walk() for parameters and return description. 1119 * 1120 * @param mixed ...$args Elements array, maximum hierarchical depth and optional additional arguments. 1121 * @return string 1122 */ 1123function walk_category_dropdown_tree( ...$args ) { 1124 // The user's options are the third parameter. 1125 if ( empty( $args[2]['walker'] ) || ! ( $args[2]['walker'] instanceof Walker ) ) { 1126 $walker = new Walker_CategoryDropdown; 1127 } else { 1128 /** 1129 * @var Walker $walker 1130 */ 1131 $walker = $args[2]['walker']; 1132 } 1133 return $walker->walk( ...$args ); 1134} 1135 1136// 1137// Tags. 1138// 1139 1140/** 1141 * Retrieves the link to the tag. 1142 * 1143 * @since 2.3.0 1144 * 1145 * @see get_term_link() 1146 * 1147 * @param int|object $tag Tag ID or object. 1148 * @return string Link on success, empty string if tag does not exist. 1149 */ 1150function get_tag_link( $tag ) { 1151 return get_category_link( $tag ); 1152} 1153 1154/** 1155 * Retrieves the tags for a post. 1156 * 1157 * @since 2.3.0 1158 * 1159 * @param int|WP_Post $post_id Post ID or object. 1160 * @return WP_Term[]|false|WP_Error Array of WP_Term objects on success, false if there are no terms 1161 * or the post does not exist, WP_Error on failure. 1162 */ 1163function get_the_tags( $post_id = 0 ) { 1164 $terms = get_the_terms( $post_id, 'post_tag' ); 1165 1166 /** 1167 * Filters the array of tags for the given post. 1168 * 1169 * @since 2.3.0 1170 * 1171 * @see get_the_terms() 1172 * 1173 * @param WP_Term[]|false|WP_Error $terms Array of WP_Term objects on success, false if there are no terms 1174 * or the post does not exist, WP_Error on failure. 1175 */ 1176 return apply_filters( 'get_the_tags', $terms ); 1177} 1178 1179/** 1180 * Retrieves the tags for a post formatted as a string. 1181 * 1182 * @since 2.3.0 1183 * 1184 * @param string $before Optional. String to use before the tags. Default empty. 1185 * @param string $sep Optional. String to use between the tags. Default empty. 1186 * @param string $after Optional. String to use after the tags. Default empty. 1187 * @param int $post_id Optional. Post ID. Defaults to the current post ID. 1188 * @return string|false|WP_Error A list of tags on success, false if there are no terms, 1189 * WP_Error on failure. 1190 */ 1191function get_the_tag_list( $before = '', $sep = '', $after = '', $post_id = 0 ) { 1192 $tag_list = get_the_term_list( $post_id, 'post_tag', $before, $sep, $after ); 1193 1194 /** 1195 * Filters the tags list for a given post. 1196 * 1197 * @since 2.3.0 1198 * 1199 * @param string $tag_list List of tags. 1200 * @param string $before String to use before the tags. 1201 * @param string $sep String to use between the tags. 1202 * @param string $after String to use after the tags. 1203 * @param int $post_id Post ID. 1204 */ 1205 return apply_filters( 'the_tags', $tag_list, $before, $sep, $after, $post_id ); 1206} 1207 1208/** 1209 * Displays the tags for a post. 1210 * 1211 * @since 2.3.0 1212 * 1213 * @param string $before Optional. String to use before the tags. Defaults to 'Tags:'. 1214 * @param string $sep Optional. String to use between the tags. Default ', '. 1215 * @param string $after Optional. String to use after the tags. Default empty. 1216 */ 1217function the_tags( $before = null, $sep = ', ', $after = '' ) { 1218 if ( null === $before ) { 1219 $before = __( 'Tags: ' ); 1220 } 1221 1222 $the_tags = get_the_tag_list( $before, $sep, $after ); 1223 1224 if ( ! is_wp_error( $the_tags ) ) { 1225 echo $the_tags; 1226 } 1227} 1228 1229/** 1230 * Retrieves tag description. 1231 * 1232 * @since 2.8.0 1233 * 1234 * @param int $tag Optional. Tag ID. Defaults to the current tag ID. 1235 * @return string Tag description, if available. 1236 */ 1237function tag_description( $tag = 0 ) { 1238 return term_description( $tag ); 1239} 1240 1241/** 1242 * Retrieves term description. 1243 * 1244 * @since 2.8.0 1245 * @since 4.9.2 The `$taxonomy` parameter was deprecated. 1246 * 1247 * @param int $term Optional. Term ID. Defaults to the current term ID. 1248 * @param null $deprecated Deprecated. Not used. 1249 * @return string Term description, if available. 1250 */ 1251function term_description( $term = 0, $deprecated = null ) { 1252 if ( ! $term && ( is_tax() || is_tag() || is_category() ) ) { 1253 $term = get_queried_object(); 1254 if ( $term ) { 1255 $term = $term->term_id; 1256 } 1257 } 1258 1259 $description = get_term_field( 'description', $term ); 1260 1261 return is_wp_error( $description ) ? '' : $description; 1262} 1263 1264/** 1265 * Retrieves the terms of the taxonomy that are attached to the post. 1266 * 1267 * @since 2.5.0 1268 * 1269 * @param int|WP_Post $post Post ID or object. 1270 * @param string $taxonomy Taxonomy name. 1271 * @return WP_Term[]|false|WP_Error Array of WP_Term objects on success, false if there are no terms 1272 * or the post does not exist, WP_Error on failure. 1273 */ 1274function get_the_terms( $post, $taxonomy ) { 1275 $post = get_post( $post ); 1276 if ( ! $post ) { 1277 return false; 1278 } 1279 1280 $terms = get_object_term_cache( $post->ID, $taxonomy ); 1281 if ( false === $terms ) { 1282 $terms = wp_get_object_terms( $post->ID, $taxonomy ); 1283 if ( ! is_wp_error( $terms ) ) { 1284 $term_ids = wp_list_pluck( $terms, 'term_id' ); 1285 wp_cache_add( $post->ID, $term_ids, $taxonomy . '_relationships' ); 1286 } 1287 } 1288 1289 /** 1290 * Filters the list of terms attached to the given post. 1291 * 1292 * @since 3.1.0 1293 * 1294 * @param WP_Term[]|WP_Error $terms Array of attached terms, or WP_Error on failure. 1295 * @param int $post_id Post ID. 1296 * @param string $taxonomy Name of the taxonomy. 1297 */ 1298 $terms = apply_filters( 'get_the_terms', $terms, $post->ID, $taxonomy ); 1299 1300 if ( empty( $terms ) ) { 1301 return false; 1302 } 1303 1304 return $terms; 1305} 1306 1307/** 1308 * Retrieves a post's terms as a list with specified format. 1309 * 1310 * Terms are linked to their respective term listing pages. 1311 * 1312 * @since 2.5.0 1313 * 1314 * @param int $post_id Post ID. 1315 * @param string $taxonomy Taxonomy name. 1316 * @param string $before Optional. String to use before the terms. Default empty. 1317 * @param string $sep Optional. String to use between the terms. Default empty. 1318 * @param string $after Optional. String to use after the terms. Default empty. 1319 * @return string|false|WP_Error A list of terms on success, false if there are no terms, 1320 * WP_Error on failure. 1321 */ 1322function get_the_term_list( $post_id, $taxonomy, $before = '', $sep = '', $after = '' ) { 1323 $terms = get_the_terms( $post_id, $taxonomy ); 1324 1325 if ( is_wp_error( $terms ) ) { 1326 return $terms; 1327 } 1328 1329 if ( empty( $terms ) ) { 1330 return false; 1331 } 1332 1333 $links = array(); 1334 1335 foreach ( $terms as $term ) { 1336 $link = get_term_link( $term, $taxonomy ); 1337 if ( is_wp_error( $link ) ) { 1338 return $link; 1339 } 1340 $links[] = '<a href="' . esc_url( $link ) . '" rel="tag">' . $term->name . '</a>'; 1341 } 1342 1343 /** 1344 * Filters the term links for a given taxonomy. 1345 * 1346 * The dynamic portion of the filter name, `$taxonomy`, refers 1347 * to the taxonomy slug. 1348 * 1349 * @since 2.5.0 1350 * 1351 * @param string[] $links An array of term links. 1352 */ 1353 $term_links = apply_filters( "term_links-{$taxonomy}", $links ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores 1354 1355 return $before . implode( $sep, $term_links ) . $after; 1356} 1357 1358/** 1359 * Retrieves term parents with separator. 1360 * 1361 * @since 4.8.0 1362 * 1363 * @param int $term_id Term ID. 1364 * @param string $taxonomy Taxonomy name. 1365 * @param string|array $args { 1366 * Array of optional arguments. 1367 * 1368 * @type string $format Use term names or slugs for display. Accepts 'name' or 'slug'. 1369 * Default 'name'. 1370 * @type string $separator Separator for between the terms. Default '/'. 1371 * @type bool $link Whether to format as a link. Default true. 1372 * @type bool $inclusive Include the term to get the parents for. Default true. 1373 * } 1374 * @return string|WP_Error A list of term parents on success, WP_Error or empty string on failure. 1375 */ 1376function get_term_parents_list( $term_id, $taxonomy, $args = array() ) { 1377 $list = ''; 1378 $term = get_term( $term_id, $taxonomy ); 1379 1380 if ( is_wp_error( $term ) ) { 1381 return $term; 1382 } 1383 1384 if ( ! $term ) { 1385 return $list; 1386 } 1387 1388 $term_id = $term->term_id; 1389 1390 $defaults = array( 1391 'format' => 'name', 1392 'separator' => '/', 1393 'link' => true, 1394 'inclusive' => true, 1395 ); 1396 1397 $args = wp_parse_args( $args, $defaults ); 1398 1399 foreach ( array( 'link', 'inclusive' ) as $bool ) { 1400 $args[ $bool ] = wp_validate_boolean( $args[ $bool ] ); 1401 } 1402 1403 $parents = get_ancestors( $term_id, $taxonomy, 'taxonomy' ); 1404 1405 if ( $args['inclusive'] ) { 1406 array_unshift( $parents, $term_id ); 1407 } 1408 1409 foreach ( array_reverse( $parents ) as $term_id ) { 1410 $parent = get_term( $term_id, $taxonomy ); 1411 $name = ( 'slug' === $args['format'] ) ? $parent->slug : $parent->name; 1412 1413 if ( $args['link'] ) { 1414 $list .= '<a href="' . esc_url( get_term_link( $parent->term_id, $taxonomy ) ) . '">' . $name . '</a>' . $args['separator']; 1415 } else { 1416 $list .= $name . $args['separator']; 1417 } 1418 } 1419 1420 return $list; 1421} 1422 1423/** 1424 * Displays the terms for a post in a list. 1425 * 1426 * @since 2.5.0 1427 * 1428 * @param int $post_id Post ID. 1429 * @param string $taxonomy Taxonomy name. 1430 * @param string $before Optional. String to use before the terms. Default empty. 1431 * @param string $sep Optional. String to use between the terms. Default ', '. 1432 * @param string $after Optional. String to use after the terms. Default empty. 1433 * @return void|false Void on success, false on failure. 1434 */ 1435function the_terms( $post_id, $taxonomy, $before = '', $sep = ', ', $after = '' ) { 1436 $term_list = get_the_term_list( $post_id, $taxonomy, $before, $sep, $after ); 1437 1438 if ( is_wp_error( $term_list ) ) { 1439 return false; 1440 } 1441 1442 /** 1443 * Filters the list of terms to display. 1444 * 1445 * @since 2.9.0 1446 * 1447 * @param string $term_list List of terms to display. 1448 * @param string $taxonomy The taxonomy name. 1449 * @param string $before String to use before the terms. 1450 * @param string $sep String to use between the terms. 1451 * @param string $after String to use after the terms. 1452 */ 1453 echo apply_filters( 'the_terms', $term_list, $taxonomy, $before, $sep, $after ); 1454} 1455 1456/** 1457 * Checks if the current post has any of given category. 1458 * 1459 * The given categories are checked against the post's categories' term_ids, names and slugs. 1460 * Categories given as integers will only be checked against the post's categories' term_ids. 1461 * 1462 * If no categories are given, determines if post has any categories. 1463 * 1464 * @since 3.1.0 1465 * 1466 * @param string|int|array $category Optional. The category name/term_id/slug, 1467 * or an array of them to check for. Default empty. 1468 * @param int|object $post Optional. Post to check instead of the current post. 1469 * @return bool True if the current post has any of the given categories 1470 * (or any category, if no category specified). False otherwise. 1471 */ 1472function has_category( $category = '', $post = null ) { 1473 return has_term( $category, 'category', $post ); 1474} 1475 1476/** 1477 * Checks if the current post has any of given tags. 1478 * 1479 * The given tags are checked against the post's tags' term_ids, names and slugs. 1480 * Tags given as integers will only be checked against the post's tags' term_ids. 1481 * 1482 * If no tags are given, determines if post has any tags. 1483 * 1484 * For more information on this and similar theme functions, check out 1485 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 1486 * Conditional Tags} article in the Theme Developer Handbook. 1487 * 1488 * @since 2.6.0 1489 * @since 2.7.0 Tags given as integers are only checked against 1490 * the post's tags' term_ids, not names or slugs. 1491 * @since 2.7.0 Can be used outside of the WordPress Loop if `$post` is provided. 1492 * 1493 * @param string|int|array $tag Optional. The tag name/term_id/slug, 1494 * or an array of them to check for. Default empty. 1495 * @param int|object $post Optional. Post to check instead of the current post. 1496 * @return bool True if the current post has any of the given tags 1497 * (or any tag, if no tag specified). False otherwise. 1498 */ 1499function has_tag( $tag = '', $post = null ) { 1500 return has_term( $tag, 'post_tag', $post ); 1501} 1502 1503/** 1504 * Checks if the current post has any of given terms. 1505 * 1506 * The given terms are checked against the post's terms' term_ids, names and slugs. 1507 * Terms given as integers will only be checked against the post's terms' term_ids. 1508 * 1509 * If no terms are given, determines if post has any terms. 1510 * 1511 * @since 3.1.0 1512 * 1513 * @param string|int|array $term Optional. The term name/term_id/slug, 1514 * or an array of them to check for. Default empty. 1515 * @param string $taxonomy Optional. Taxonomy name. Default empty. 1516 * @param int|WP_Post $post Optional. Post to check instead of the current post. 1517 * @return bool True if the current post has any of the given terms 1518 * (or any term, if no term specified). False otherwise. 1519 */ 1520function has_term( $term = '', $taxonomy = '', $post = null ) { 1521 $post = get_post( $post ); 1522 1523 if ( ! $post ) { 1524 return false; 1525 } 1526 1527 $r = is_object_in_term( $post->ID, $taxonomy, $term ); 1528 if ( is_wp_error( $r ) ) { 1529 return false; 1530 } 1531 1532 return $r; 1533} 1534