[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Post Template Functions. 4 * 5 * Gets content for the current post in the loop. 6 * 7 * @package WordPress 8 * @subpackage Template 9 */ 10 11 /** 12 * Displays the ID of the current item in the WordPress Loop. 13 * 14 * @since 0.71 15 */ 16 function the_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 17 echo get_the_ID(); 18 } 19 20 /** 21 * Retrieves the ID of the current item in the WordPress Loop. 22 * 23 * @since 2.1.0 24 * 25 * @return int|false The ID of the current item in the WordPress Loop. False if $post is not set. 26 */ 27 function get_the_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 28 $post = get_post(); 29 return ! empty( $post ) ? $post->ID : false; 30 } 31 32 /** 33 * Displays or retrieves the current post title with optional markup. 34 * 35 * @since 0.71 36 * 37 * @param string $before Optional. Markup to prepend to the title. Default empty. 38 * @param string $after Optional. Markup to append to the title. Default empty. 39 * @param bool $echo Optional. Whether to echo or return the title. Default true for echo. 40 * @return void|string Void if `$echo` argument is true, current post title if `$echo` is false. 41 */ 42 function the_title( $before = '', $after = '', $echo = true ) { 43 $title = get_the_title(); 44 45 if ( strlen( $title ) == 0 ) { 46 return; 47 } 48 49 $title = $before . $title . $after; 50 51 if ( $echo ) { 52 echo $title; 53 } else { 54 return $title; 55 } 56 } 57 58 /** 59 * Sanitizes the current title when retrieving or displaying. 60 * 61 * Works like the_title(), except the parameters can be in a string or 62 * an array. See the function for what can be override in the $args parameter. 63 * 64 * The title before it is displayed will have the tags stripped and esc_attr() 65 * before it is passed to the user or displayed. The default as with the_title(), 66 * is to display the title. 67 * 68 * @since 2.3.0 69 * 70 * @param string|array $args { 71 * Title attribute arguments. Optional. 72 * 73 * @type string $before Markup to prepend to the title. Default empty. 74 * @type string $after Markup to append to the title. Default empty. 75 * @type bool $echo Whether to echo or return the title. Default true for echo. 76 * @type WP_Post $post Current post object to retrieve the title for. 77 * } 78 * @return void|string Void if 'echo' argument is true, the title attribute if 'echo' is false. 79 */ 80 function the_title_attribute( $args = '' ) { 81 $defaults = array( 82 'before' => '', 83 'after' => '', 84 'echo' => true, 85 'post' => get_post(), 86 ); 87 $parsed_args = wp_parse_args( $args, $defaults ); 88 89 $title = get_the_title( $parsed_args['post'] ); 90 91 if ( strlen( $title ) == 0 ) { 92 return; 93 } 94 95 $title = $parsed_args['before'] . $title . $parsed_args['after']; 96 $title = esc_attr( strip_tags( $title ) ); 97 98 if ( $parsed_args['echo'] ) { 99 echo $title; 100 } else { 101 return $title; 102 } 103 } 104 105 /** 106 * Retrieves the post title. 107 * 108 * If the post is protected and the visitor is not an admin, then "Protected" 109 * will be inserted before the post title. If the post is private, then 110 * "Private" will be inserted before the post title. 111 * 112 * @since 0.71 113 * 114 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 115 * @return string 116 */ 117 function get_the_title( $post = 0 ) { 118 $post = get_post( $post ); 119 120 $post_title = isset( $post->post_title ) ? $post->post_title : ''; 121 $post_id = isset( $post->ID ) ? $post->ID : 0; 122 123 if ( ! is_admin() ) { 124 if ( ! empty( $post->post_password ) ) { 125 126 /* translators: %s: Protected post title. */ 127 $prepend = __( 'Protected: %s' ); 128 129 /** 130 * Filters the text prepended to the post title for protected posts. 131 * 132 * The filter is only applied on the front end. 133 * 134 * @since 2.8.0 135 * 136 * @param string $prepend Text displayed before the post title. 137 * Default 'Protected: %s'. 138 * @param WP_Post $post Current post object. 139 */ 140 $protected_title_format = apply_filters( 'protected_title_format', $prepend, $post ); 141 142 $post_title = sprintf( $protected_title_format, $post_title ); 143 } elseif ( isset( $post->post_status ) && 'private' === $post->post_status ) { 144 145 /* translators: %s: Private post title. */ 146 $prepend = __( 'Private: %s' ); 147 148 /** 149 * Filters the text prepended to the post title of private posts. 150 * 151 * The filter is only applied on the front end. 152 * 153 * @since 2.8.0 154 * 155 * @param string $prepend Text displayed before the post title. 156 * Default 'Private: %s'. 157 * @param WP_Post $post Current post object. 158 */ 159 $private_title_format = apply_filters( 'private_title_format', $prepend, $post ); 160 161 $post_title = sprintf( $private_title_format, $post_title ); 162 } 163 } 164 165 /** 166 * Filters the post title. 167 * 168 * @since 0.71 169 * 170 * @param string $post_title The post title. 171 * @param int $post_id The post ID. 172 */ 173 return apply_filters( 'the_title', $post_title, $post_id ); 174 } 175 176 /** 177 * Displays the Post Global Unique Identifier (guid). 178 * 179 * The guid will appear to be a link, but should not be used as a link to the 180 * post. The reason you should not use it as a link, is because of moving the 181 * blog across domains. 182 * 183 * URL is escaped to make it XML-safe. 184 * 185 * @since 1.5.0 186 * 187 * @param int|WP_Post $post Optional. Post ID or post object. Default is global $post. 188 */ 189 function the_guid( $post = 0 ) { 190 $post = get_post( $post ); 191 192 $post_guid = isset( $post->guid ) ? get_the_guid( $post ) : ''; 193 $post_id = isset( $post->ID ) ? $post->ID : 0; 194 195 /** 196 * Filters the escaped Global Unique Identifier (guid) of the post. 197 * 198 * @since 4.2.0 199 * 200 * @see get_the_guid() 201 * 202 * @param string $post_guid Escaped Global Unique Identifier (guid) of the post. 203 * @param int $post_id The post ID. 204 */ 205 echo apply_filters( 'the_guid', $post_guid, $post_id ); 206 } 207 208 /** 209 * Retrieves the Post Global Unique Identifier (guid). 210 * 211 * The guid will appear to be a link, but should not be used as an link to the 212 * post. The reason you should not use it as a link, is because of moving the 213 * blog across domains. 214 * 215 * @since 1.5.0 216 * 217 * @param int|WP_Post $post Optional. Post ID or post object. Default is global $post. 218 * @return string 219 */ 220 function get_the_guid( $post = 0 ) { 221 $post = get_post( $post ); 222 223 $post_guid = isset( $post->guid ) ? $post->guid : ''; 224 $post_id = isset( $post->ID ) ? $post->ID : 0; 225 226 /** 227 * Filters the Global Unique Identifier (guid) of the post. 228 * 229 * @since 1.5.0 230 * 231 * @param string $post_guid Global Unique Identifier (guid) of the post. 232 * @param int $post_id The post ID. 233 */ 234 return apply_filters( 'get_the_guid', $post_guid, $post_id ); 235 } 236 237 /** 238 * Displays the post content. 239 * 240 * @since 0.71 241 * 242 * @param string $more_link_text Optional. Content for when there is more text. 243 * @param bool $strip_teaser Optional. Strip teaser content before the more text. Default false. 244 */ 245 function the_content( $more_link_text = null, $strip_teaser = false ) { 246 $content = get_the_content( $more_link_text, $strip_teaser ); 247 248 /** 249 * Filters the post content. 250 * 251 * @since 0.71 252 * 253 * @param string $content Content of the current post. 254 */ 255 $content = apply_filters( 'the_content', $content ); 256 $content = str_replace( ']]>', ']]>', $content ); 257 echo $content; 258 } 259 260 /** 261 * Retrieves the post content. 262 * 263 * @since 0.71 264 * @since 5.2.0 Added the `$post` parameter. 265 * 266 * @global int $page Page number of a single post/page. 267 * @global int $more Boolean indicator for whether single post/page is being viewed. 268 * @global bool $preview Whether post/page is in preview mode. 269 * @global array $pages Array of all pages in post/page. Each array element contains 270 * part of the content separated by the `<!--nextpage-->` tag. 271 * @global int $multipage Boolean indicator for whether multiple pages are in play. 272 * 273 * @param string $more_link_text Optional. Content for when there is more text. 274 * @param bool $strip_teaser Optional. Strip teaser content before the more text. Default false. 275 * @param WP_Post|object|int $post Optional. WP_Post instance or Post ID/object. Default null. 276 * @return string 277 */ 278 function get_the_content( $more_link_text = null, $strip_teaser = false, $post = null ) { 279 global $page, $more, $preview, $pages, $multipage; 280 281 $_post = get_post( $post ); 282 283 if ( ! ( $_post instanceof WP_Post ) ) { 284 return ''; 285 } 286 287 // Use the globals if the $post parameter was not specified, 288 // but only after they have been set up in setup_postdata(). 289 if ( null === $post && did_action( 'the_post' ) ) { 290 $elements = compact( 'page', 'more', 'preview', 'pages', 'multipage' ); 291 } else { 292 $elements = generate_postdata( $_post ); 293 } 294 295 if ( null === $more_link_text ) { 296 $more_link_text = sprintf( 297 '<span aria-label="%1$s">%2$s</span>', 298 sprintf( 299 /* translators: %s: Post title. */ 300 __( 'Continue reading %s' ), 301 the_title_attribute( 302 array( 303 'echo' => false, 304 'post' => $_post, 305 ) 306 ) 307 ), 308 __( '(more…)' ) 309 ); 310 } 311 312 $output = ''; 313 $has_teaser = false; 314 315 // If post password required and it doesn't match the cookie. 316 if ( post_password_required( $_post ) ) { 317 return get_the_password_form( $_post ); 318 } 319 320 // If the requested page doesn't exist. 321 if ( $elements['page'] > count( $elements['pages'] ) ) { 322 // Give them the highest numbered page that DOES exist. 323 $elements['page'] = count( $elements['pages'] ); 324 } 325 326 $page_no = $elements['page']; 327 $content = $elements['pages'][ $page_no - 1 ]; 328 if ( preg_match( '/<!--more(.*?)?-->/', $content, $matches ) ) { 329 if ( has_block( 'more', $content ) ) { 330 // Remove the core/more block delimiters. They will be left over after $content is split up. 331 $content = preg_replace( '/<!-- \/?wp:more(.*?) -->/', '', $content ); 332 } 333 334 $content = explode( $matches[0], $content, 2 ); 335 336 if ( ! empty( $matches[1] ) && ! empty( $more_link_text ) ) { 337 $more_link_text = strip_tags( wp_kses_no_null( trim( $matches[1] ) ) ); 338 } 339 340 $has_teaser = true; 341 } else { 342 $content = array( $content ); 343 } 344 345 if ( false !== strpos( $_post->post_content, '<!--noteaser-->' ) && ( ! $elements['multipage'] || 1 == $elements['page'] ) ) { 346 $strip_teaser = true; 347 } 348 349 $teaser = $content[0]; 350 351 if ( $elements['more'] && $strip_teaser && $has_teaser ) { 352 $teaser = ''; 353 } 354 355 $output .= $teaser; 356 357 if ( count( $content ) > 1 ) { 358 if ( $elements['more'] ) { 359 $output .= '<span id="more-' . $_post->ID . '"></span>' . $content[1]; 360 } else { 361 if ( ! empty( $more_link_text ) ) { 362 363 /** 364 * Filters the Read More link text. 365 * 366 * @since 2.8.0 367 * 368 * @param string $more_link_element Read More link element. 369 * @param string $more_link_text Read More text. 370 */ 371 $output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink( $_post ) . "#more-{$_post->ID}\" class=\"more-link\">$more_link_text</a>", $more_link_text ); 372 } 373 $output = force_balance_tags( $output ); 374 } 375 } 376 377 return $output; 378 } 379 380 /** 381 * Displays the post excerpt. 382 * 383 * @since 0.71 384 */ 385 function the_excerpt() { 386 387 /** 388 * Filters the displayed post excerpt. 389 * 390 * @since 0.71 391 * 392 * @see get_the_excerpt() 393 * 394 * @param string $post_excerpt The post excerpt. 395 */ 396 echo apply_filters( 'the_excerpt', get_the_excerpt() ); 397 } 398 399 /** 400 * Retrieves the post excerpt. 401 * 402 * @since 0.71 403 * @since 4.5.0 Introduced the `$post` parameter. 404 * 405 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 406 * @return string Post excerpt. 407 */ 408 function get_the_excerpt( $post = null ) { 409 if ( is_bool( $post ) ) { 410 _deprecated_argument( __FUNCTION__, '2.3.0' ); 411 } 412 413 $post = get_post( $post ); 414 if ( empty( $post ) ) { 415 return ''; 416 } 417 418 if ( post_password_required( $post ) ) { 419 return __( 'There is no excerpt because this is a protected post.' ); 420 } 421 422 /** 423 * Filters the retrieved post excerpt. 424 * 425 * @since 1.2.0 426 * @since 4.5.0 Introduced the `$post` parameter. 427 * 428 * @param string $post_excerpt The post excerpt. 429 * @param WP_Post $post Post object. 430 */ 431 return apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ); 432 } 433 434 /** 435 * Determines whether the post has a custom excerpt. 436 * 437 * For more information on this and similar theme functions, check out 438 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 439 * Conditional Tags} article in the Theme Developer Handbook. 440 * 441 * @since 2.3.0 442 * 443 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 444 * @return bool True if the post has a custom excerpt, false otherwise. 445 */ 446 function has_excerpt( $post = 0 ) { 447 $post = get_post( $post ); 448 return ( ! empty( $post->post_excerpt ) ); 449 } 450 451 /** 452 * Displays the classes for the post container element. 453 * 454 * @since 2.7.0 455 * 456 * @param string|string[] $class One or more classes to add to the class list. 457 * @param int|WP_Post $post Optional. Post ID or post object. Defaults to the global `$post`. 458 */ 459 function post_class( $class = '', $post = null ) { 460 // Separates classes with a single space, collates classes for post DIV. 461 echo 'class="' . esc_attr( implode( ' ', get_post_class( $class, $post ) ) ) . '"'; 462 } 463 464 /** 465 * Retrieves an array of the class names for the post container element. 466 * 467 * The class names are many. If the post is a sticky, then the 'sticky' 468 * class name. The class 'hentry' is always added to each post. If the post has a 469 * post thumbnail, 'has-post-thumbnail' is added as a class. For each taxonomy that 470 * the post belongs to, a class will be added of the format '{$taxonomy}-{$slug}' - 471 * eg 'category-foo' or 'my_custom_taxonomy-bar'. 472 * 473 * The 'post_tag' taxonomy is a special 474 * case; the class has the 'tag-' prefix instead of 'post_tag-'. All class names are 475 * passed through the filter, {@see 'post_class'}, with the list of class names, followed by 476 * $class parameter value, with the post ID as the last parameter. 477 * 478 * @since 2.7.0 479 * @since 4.2.0 Custom taxonomy class names were added. 480 * 481 * @param string|string[] $class Space-separated string or array of class names to add to the class list. 482 * @param int|WP_Post $post Optional. Post ID or post object. 483 * @return string[] Array of class names. 484 */ 485 function get_post_class( $class = '', $post = null ) { 486 $post = get_post( $post ); 487 488 $classes = array(); 489 490 if ( $class ) { 491 if ( ! is_array( $class ) ) { 492 $class = preg_split( '#\s+#', $class ); 493 } 494 $classes = array_map( 'esc_attr', $class ); 495 } else { 496 // Ensure that we always coerce class to being an array. 497 $class = array(); 498 } 499 500 if ( ! $post ) { 501 return $classes; 502 } 503 504 $classes[] = 'post-' . $post->ID; 505 if ( ! is_admin() ) { 506 $classes[] = $post->post_type; 507 } 508 $classes[] = 'type-' . $post->post_type; 509 $classes[] = 'status-' . $post->post_status; 510 511 // Post Format. 512 if ( post_type_supports( $post->post_type, 'post-formats' ) ) { 513 $post_format = get_post_format( $post->ID ); 514 515 if ( $post_format && ! is_wp_error( $post_format ) ) { 516 $classes[] = 'format-' . sanitize_html_class( $post_format ); 517 } else { 518 $classes[] = 'format-standard'; 519 } 520 } 521 522 $post_password_required = post_password_required( $post->ID ); 523 524 // Post requires password. 525 if ( $post_password_required ) { 526 $classes[] = 'post-password-required'; 527 } elseif ( ! empty( $post->post_password ) ) { 528 $classes[] = 'post-password-protected'; 529 } 530 531 // Post thumbnails. 532 if ( current_theme_supports( 'post-thumbnails' ) && has_post_thumbnail( $post->ID ) && ! is_attachment( $post ) && ! $post_password_required ) { 533 $classes[] = 'has-post-thumbnail'; 534 } 535 536 // Sticky for Sticky Posts. 537 if ( is_sticky( $post->ID ) ) { 538 if ( is_home() && ! is_paged() ) { 539 $classes[] = 'sticky'; 540 } elseif ( is_admin() ) { 541 $classes[] = 'status-sticky'; 542 } 543 } 544 545 // hentry for hAtom compliance. 546 $classes[] = 'hentry'; 547 548 // All public taxonomies. 549 $taxonomies = get_taxonomies( array( 'public' => true ) ); 550 foreach ( (array) $taxonomies as $taxonomy ) { 551 if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) ) { 552 foreach ( (array) get_the_terms( $post->ID, $taxonomy ) as $term ) { 553 if ( empty( $term->slug ) ) { 554 continue; 555 } 556 557 $term_class = sanitize_html_class( $term->slug, $term->term_id ); 558 if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) { 559 $term_class = $term->term_id; 560 } 561 562 // 'post_tag' uses the 'tag' prefix for backward compatibility. 563 if ( 'post_tag' === $taxonomy ) { 564 $classes[] = 'tag-' . $term_class; 565 } else { 566 $classes[] = sanitize_html_class( $taxonomy . '-' . $term_class, $taxonomy . '-' . $term->term_id ); 567 } 568 } 569 } 570 } 571 572 $classes = array_map( 'esc_attr', $classes ); 573 574 /** 575 * Filters the list of CSS class names for the current post. 576 * 577 * @since 2.7.0 578 * 579 * @param string[] $classes An array of post class names. 580 * @param string[] $class An array of additional class names added to the post. 581 * @param int $post_id The post ID. 582 */ 583 $classes = apply_filters( 'post_class', $classes, $class, $post->ID ); 584 585 return array_unique( $classes ); 586 } 587 588 /** 589 * Displays the class names for the body element. 590 * 591 * @since 2.8.0 592 * 593 * @param string|string[] $class Space-separated string or array of class names to add to the class list. 594 */ 595 function body_class( $class = '' ) { 596 // Separates class names with a single space, collates class names for body element. 597 echo 'class="' . esc_attr( implode( ' ', get_body_class( $class ) ) ) . '"'; 598 } 599 600 /** 601 * Retrieves an array of the class names for the body element. 602 * 603 * @since 2.8.0 604 * 605 * @global WP_Query $wp_query WordPress Query object. 606 * 607 * @param string|string[] $class Space-separated string or array of class names to add to the class list. 608 * @return string[] Array of class names. 609 */ 610 function get_body_class( $class = '' ) { 611 global $wp_query; 612 613 $classes = array(); 614 615 if ( is_rtl() ) { 616 $classes[] = 'rtl'; 617 } 618 619 if ( is_front_page() ) { 620 $classes[] = 'home'; 621 } 622 if ( is_home() ) { 623 $classes[] = 'blog'; 624 } 625 if ( is_privacy_policy() ) { 626 $classes[] = 'privacy-policy'; 627 } 628 if ( is_archive() ) { 629 $classes[] = 'archive'; 630 } 631 if ( is_date() ) { 632 $classes[] = 'date'; 633 } 634 if ( is_search() ) { 635 $classes[] = 'search'; 636 $classes[] = $wp_query->posts ? 'search-results' : 'search-no-results'; 637 } 638 if ( is_paged() ) { 639 $classes[] = 'paged'; 640 } 641 if ( is_attachment() ) { 642 $classes[] = 'attachment'; 643 } 644 if ( is_404() ) { 645 $classes[] = 'error404'; 646 } 647 648 if ( is_singular() ) { 649 $post_id = $wp_query->get_queried_object_id(); 650 $post = $wp_query->get_queried_object(); 651 $post_type = $post->post_type; 652 653 if ( is_page_template() ) { 654 $classes[] = "{$post_type}-template"; 655 656 $template_slug = get_page_template_slug( $post_id ); 657 $template_parts = explode( '/', $template_slug ); 658 659 foreach ( $template_parts as $part ) { 660 $classes[] = "{$post_type}-template-" . sanitize_html_class( str_replace( array( '.', '/' ), '-', basename( $part, '.php' ) ) ); 661 } 662 $classes[] = "{$post_type}-template-" . sanitize_html_class( str_replace( '.', '-', $template_slug ) ); 663 } else { 664 $classes[] = "{$post_type}-template-default"; 665 } 666 667 if ( is_single() ) { 668 $classes[] = 'single'; 669 if ( isset( $post->post_type ) ) { 670 $classes[] = 'single-' . sanitize_html_class( $post->post_type, $post_id ); 671 $classes[] = 'postid-' . $post_id; 672 673 // Post Format. 674 if ( post_type_supports( $post->post_type, 'post-formats' ) ) { 675 $post_format = get_post_format( $post->ID ); 676 677 if ( $post_format && ! is_wp_error( $post_format ) ) { 678 $classes[] = 'single-format-' . sanitize_html_class( $post_format ); 679 } else { 680 $classes[] = 'single-format-standard'; 681 } 682 } 683 } 684 } 685 686 if ( is_attachment() ) { 687 $mime_type = get_post_mime_type( $post_id ); 688 $mime_prefix = array( 'application/', 'image/', 'text/', 'audio/', 'video/', 'music/' ); 689 $classes[] = 'attachmentid-' . $post_id; 690 $classes[] = 'attachment-' . str_replace( $mime_prefix, '', $mime_type ); 691 } elseif ( is_page() ) { 692 $classes[] = 'page'; 693 694 $page_id = $wp_query->get_queried_object_id(); 695 696 $post = get_post( $page_id ); 697 698 $classes[] = 'page-id-' . $page_id; 699 700 if ( get_pages( 701 array( 702 'parent' => $page_id, 703 'number' => 1, 704 ) 705 ) ) { 706 $classes[] = 'page-parent'; 707 } 708 709 if ( $post->post_parent ) { 710 $classes[] = 'page-child'; 711 $classes[] = 'parent-pageid-' . $post->post_parent; 712 } 713 } 714 } elseif ( is_archive() ) { 715 if ( is_post_type_archive() ) { 716 $classes[] = 'post-type-archive'; 717 $post_type = get_query_var( 'post_type' ); 718 if ( is_array( $post_type ) ) { 719 $post_type = reset( $post_type ); 720 } 721 $classes[] = 'post-type-archive-' . sanitize_html_class( $post_type ); 722 } elseif ( is_author() ) { 723 $author = $wp_query->get_queried_object(); 724 $classes[] = 'author'; 725 if ( isset( $author->user_nicename ) ) { 726 $classes[] = 'author-' . sanitize_html_class( $author->user_nicename, $author->ID ); 727 $classes[] = 'author-' . $author->ID; 728 } 729 } elseif ( is_category() ) { 730 $cat = $wp_query->get_queried_object(); 731 $classes[] = 'category'; 732 if ( isset( $cat->term_id ) ) { 733 $cat_class = sanitize_html_class( $cat->slug, $cat->term_id ); 734 if ( is_numeric( $cat_class ) || ! trim( $cat_class, '-' ) ) { 735 $cat_class = $cat->term_id; 736 } 737 738 $classes[] = 'category-' . $cat_class; 739 $classes[] = 'category-' . $cat->term_id; 740 } 741 } elseif ( is_tag() ) { 742 $tag = $wp_query->get_queried_object(); 743 $classes[] = 'tag'; 744 if ( isset( $tag->term_id ) ) { 745 $tag_class = sanitize_html_class( $tag->slug, $tag->term_id ); 746 if ( is_numeric( $tag_class ) || ! trim( $tag_class, '-' ) ) { 747 $tag_class = $tag->term_id; 748 } 749 750 $classes[] = 'tag-' . $tag_class; 751 $classes[] = 'tag-' . $tag->term_id; 752 } 753 } elseif ( is_tax() ) { 754 $term = $wp_query->get_queried_object(); 755 if ( isset( $term->term_id ) ) { 756 $term_class = sanitize_html_class( $term->slug, $term->term_id ); 757 if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) { 758 $term_class = $term->term_id; 759 } 760 761 $classes[] = 'tax-' . sanitize_html_class( $term->taxonomy ); 762 $classes[] = 'term-' . $term_class; 763 $classes[] = 'term-' . $term->term_id; 764 } 765 } 766 } 767 768 if ( is_user_logged_in() ) { 769 $classes[] = 'logged-in'; 770 } 771 772 if ( is_admin_bar_showing() ) { 773 $classes[] = 'admin-bar'; 774 $classes[] = 'no-customize-support'; 775 } 776 777 if ( current_theme_supports( 'custom-background' ) 778 && ( get_background_color() !== get_theme_support( 'custom-background', 'default-color' ) || get_background_image() ) ) { 779 $classes[] = 'custom-background'; 780 } 781 782 if ( has_custom_logo() ) { 783 $classes[] = 'wp-custom-logo'; 784 } 785 786 if ( current_theme_supports( 'responsive-embeds' ) ) { 787 $classes[] = 'wp-embed-responsive'; 788 } 789 790 $page = $wp_query->get( 'page' ); 791 792 if ( ! $page || $page < 2 ) { 793 $page = $wp_query->get( 'paged' ); 794 } 795 796 if ( $page && $page > 1 && ! is_404() ) { 797 $classes[] = 'paged-' . $page; 798 799 if ( is_single() ) { 800 $classes[] = 'single-paged-' . $page; 801 } elseif ( is_page() ) { 802 $classes[] = 'page-paged-' . $page; 803 } elseif ( is_category() ) { 804 $classes[] = 'category-paged-' . $page; 805 } elseif ( is_tag() ) { 806 $classes[] = 'tag-paged-' . $page; 807 } elseif ( is_date() ) { 808 $classes[] = 'date-paged-' . $page; 809 } elseif ( is_author() ) { 810 $classes[] = 'author-paged-' . $page; 811 } elseif ( is_search() ) { 812 $classes[] = 'search-paged-' . $page; 813 } elseif ( is_post_type_archive() ) { 814 $classes[] = 'post-type-paged-' . $page; 815 } 816 } 817 818 if ( ! empty( $class ) ) { 819 if ( ! is_array( $class ) ) { 820 $class = preg_split( '#\s+#', $class ); 821 } 822 $classes = array_merge( $classes, $class ); 823 } else { 824 // Ensure that we always coerce class to being an array. 825 $class = array(); 826 } 827 828 $classes = array_map( 'esc_attr', $classes ); 829 830 /** 831 * Filters the list of CSS body class names for the current post or page. 832 * 833 * @since 2.8.0 834 * 835 * @param string[] $classes An array of body class names. 836 * @param string[] $class An array of additional class names added to the body. 837 */ 838 $classes = apply_filters( 'body_class', $classes, $class ); 839 840 return array_unique( $classes ); 841 } 842 843 /** 844 * Determines whether the post requires password and whether a correct password has been provided. 845 * 846 * @since 2.7.0 847 * 848 * @param int|WP_Post|null $post An optional post. Global $post used if not provided. 849 * @return bool false if a password is not required or the correct password cookie is present, true otherwise. 850 */ 851 function post_password_required( $post = null ) { 852 $post = get_post( $post ); 853 854 if ( empty( $post->post_password ) ) { 855 /** This filter is documented in wp-includes/post-template.php */ 856 return apply_filters( 'post_password_required', false, $post ); 857 } 858 859 if ( ! isset( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ) ) { 860 /** This filter is documented in wp-includes/post-template.php */ 861 return apply_filters( 'post_password_required', true, $post ); 862 } 863 864 require_once ABSPATH . WPINC . '/class-phpass.php'; 865 $hasher = new PasswordHash( 8, true ); 866 867 $hash = wp_unslash( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ); 868 if ( 0 !== strpos( $hash, '$P$B' ) ) { 869 $required = true; 870 } else { 871 $required = ! $hasher->CheckPassword( $post->post_password, $hash ); 872 } 873 874 /** 875 * Filters whether a post requires the user to supply a password. 876 * 877 * @since 4.7.0 878 * 879 * @param bool $required Whether the user needs to supply a password. True if password has not been 880 * provided or is incorrect, false if password has been supplied or is not required. 881 * @param WP_Post $post Post object. 882 */ 883 return apply_filters( 'post_password_required', $required, $post ); 884 } 885 886 // 887 // Page Template Functions for usage in Themes. 888 // 889 890 /** 891 * The formatted output of a list of pages. 892 * 893 * Displays page links for paginated posts (i.e. including the `<!--nextpage-->` 894 * Quicktag one or more times). This tag must be within The Loop. 895 * 896 * @since 1.2.0 897 * @since 5.1.0 Added the `aria_current` argument. 898 * 899 * @global int $page 900 * @global int $numpages 901 * @global int $multipage 902 * @global int $more 903 * 904 * @param string|array $args { 905 * Optional. Array or string of default arguments. 906 * 907 * @type string $before HTML or text to prepend to each link. Default is `<p> Pages:`. 908 * @type string $after HTML or text to append to each link. Default is `</p>`. 909 * @type string $link_before HTML or text to prepend to each link, inside the `<a>` tag. 910 * Also prepended to the current item, which is not linked. Default empty. 911 * @type string $link_after HTML or text to append to each Pages link inside the `<a>` tag. 912 * Also appended to the current item, which is not linked. Default empty. 913 * @type string $aria_current The value for the aria-current attribute. Possible values are 'page', 914 * 'step', 'location', 'date', 'time', 'true', 'false'. Default is 'page'. 915 * @type string $next_or_number Indicates whether page numbers should be used. Valid values are number 916 * and next. Default is 'number'. 917 * @type string $separator Text between pagination links. Default is ' '. 918 * @type string $nextpagelink Link text for the next page link, if available. Default is 'Next Page'. 919 * @type string $previouspagelink Link text for the previous page link, if available. Default is 'Previous Page'. 920 * @type string $pagelink Format string for page numbers. The % in the parameter string will be 921 * replaced with the page number, so 'Page %' generates "Page 1", "Page 2", etc. 922 * Defaults to '%', just the page number. 923 * @type int|bool $echo Whether to echo or not. Accepts 1|true or 0|false. Default 1|true. 924 * } 925 * @return string Formatted output in HTML. 926 */ 927 function wp_link_pages( $args = '' ) { 928 global $page, $numpages, $multipage, $more; 929 930 $defaults = array( 931 'before' => '<p class="post-nav-links">' . __( 'Pages:' ), 932 'after' => '</p>', 933 'link_before' => '', 934 'link_after' => '', 935 'aria_current' => 'page', 936 'next_or_number' => 'number', 937 'separator' => ' ', 938 'nextpagelink' => __( 'Next page' ), 939 'previouspagelink' => __( 'Previous page' ), 940 'pagelink' => '%', 941 'echo' => 1, 942 ); 943 944 $parsed_args = wp_parse_args( $args, $defaults ); 945 946 /** 947 * Filters the arguments used in retrieving page links for paginated posts. 948 * 949 * @since 3.0.0 950 * 951 * @param array $parsed_args An array of page link arguments. See wp_link_pages() 952 * for information on accepted arguments. 953 */ 954 $parsed_args = apply_filters( 'wp_link_pages_args', $parsed_args ); 955 956 $output = ''; 957 if ( $multipage ) { 958 if ( 'number' === $parsed_args['next_or_number'] ) { 959 $output .= $parsed_args['before']; 960 for ( $i = 1; $i <= $numpages; $i++ ) { 961 $link = $parsed_args['link_before'] . str_replace( '%', $i, $parsed_args['pagelink'] ) . $parsed_args['link_after']; 962 if ( $i != $page || ! $more && 1 == $page ) { 963 $link = _wp_link_page( $i ) . $link . '</a>'; 964 } elseif ( $i === $page ) { 965 $link = '<span class="post-page-numbers current" aria-current="' . esc_attr( $parsed_args['aria_current'] ) . '">' . $link . '</span>'; 966 } 967 /** 968 * Filters the HTML output of individual page number links. 969 * 970 * @since 3.6.0 971 * 972 * @param string $link The page number HTML output. 973 * @param int $i Page number for paginated posts' page links. 974 */ 975 $link = apply_filters( 'wp_link_pages_link', $link, $i ); 976 977 // Use the custom links separator beginning with the second link. 978 $output .= ( 1 === $i ) ? ' ' : $parsed_args['separator']; 979 $output .= $link; 980 } 981 $output .= $parsed_args['after']; 982 } elseif ( $more ) { 983 $output .= $parsed_args['before']; 984 $prev = $page - 1; 985 if ( $prev > 0 ) { 986 $link = _wp_link_page( $prev ) . $parsed_args['link_before'] . $parsed_args['previouspagelink'] . $parsed_args['link_after'] . '</a>'; 987 988 /** This filter is documented in wp-includes/post-template.php */ 989 $output .= apply_filters( 'wp_link_pages_link', $link, $prev ); 990 } 991 $next = $page + 1; 992 if ( $next <= $numpages ) { 993 if ( $prev ) { 994 $output .= $parsed_args['separator']; 995 } 996 $link = _wp_link_page( $next ) . $parsed_args['link_before'] . $parsed_args['nextpagelink'] . $parsed_args['link_after'] . '</a>'; 997 998 /** This filter is documented in wp-includes/post-template.php */ 999 $output .= apply_filters( 'wp_link_pages_link', $link, $next ); 1000 } 1001 $output .= $parsed_args['after']; 1002 } 1003 } 1004 1005 /** 1006 * Filters the HTML output of page links for paginated posts. 1007 * 1008 * @since 3.6.0 1009 * 1010 * @param string $output HTML output of paginated posts' page links. 1011 * @param array|string $args An array or query string of arguments. See wp_link_pages() 1012 * for information on accepted arguments. 1013 */ 1014 $html = apply_filters( 'wp_link_pages', $output, $args ); 1015 1016 if ( $parsed_args['echo'] ) { 1017 echo $html; 1018 } 1019 return $html; 1020 } 1021 1022 /** 1023 * Helper function for wp_link_pages(). 1024 * 1025 * @since 3.1.0 1026 * @access private 1027 * 1028 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. 1029 * 1030 * @param int $i Page number. 1031 * @return string Link. 1032 */ 1033 function _wp_link_page( $i ) { 1034 global $wp_rewrite; 1035 $post = get_post(); 1036 $query_args = array(); 1037 1038 if ( 1 == $i ) { 1039 $url = get_permalink(); 1040 } else { 1041 if ( ! get_option( 'permalink_structure' ) || in_array( $post->post_status, array( 'draft', 'pending' ), true ) ) { 1042 $url = add_query_arg( 'page', $i, get_permalink() ); 1043 } elseif ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) == $post->ID ) { 1044 $url = trailingslashit( get_permalink() ) . user_trailingslashit( "$wp_rewrite->pagination_base/" . $i, 'single_paged' ); 1045 } else { 1046 $url = trailingslashit( get_permalink() ) . user_trailingslashit( $i, 'single_paged' ); 1047 } 1048 } 1049 1050 if ( is_preview() ) { 1051 1052 if ( ( 'draft' !== $post->post_status ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) { 1053 $query_args['preview_id'] = wp_unslash( $_GET['preview_id'] ); 1054 $query_args['preview_nonce'] = wp_unslash( $_GET['preview_nonce'] ); 1055 } 1056 1057 $url = get_preview_post_link( $post, $query_args, $url ); 1058 } 1059 1060 return '<a href="' . esc_url( $url ) . '" class="post-page-numbers">'; 1061 } 1062 1063 // 1064 // Post-meta: Custom per-post fields. 1065 // 1066 1067 /** 1068 * Retrieves post custom meta data field. 1069 * 1070 * @since 1.5.0 1071 * 1072 * @param string $key Meta data key name. 1073 * @return array|string|false Array of values, or single value if only one element exists. 1074 * False if the key does not exist. 1075 */ 1076 function post_custom( $key = '' ) { 1077 $custom = get_post_custom(); 1078 1079 if ( ! isset( $custom[ $key ] ) ) { 1080 return false; 1081 } elseif ( 1 === count( $custom[ $key ] ) ) { 1082 return $custom[ $key ][0]; 1083 } else { 1084 return $custom[ $key ]; 1085 } 1086 } 1087 1088 /** 1089 * Displays a list of post custom fields. 1090 * 1091 * @since 1.2.0 1092 * 1093 * @internal This will probably change at some point... 1094 */ 1095 function the_meta() { 1096 $keys = get_post_custom_keys(); 1097 if ( $keys ) { 1098 $li_html = ''; 1099 foreach ( (array) $keys as $key ) { 1100 $keyt = trim( $key ); 1101 if ( is_protected_meta( $keyt, 'post' ) ) { 1102 continue; 1103 } 1104 1105 $values = array_map( 'trim', get_post_custom_values( $key ) ); 1106 $value = implode( ', ', $values ); 1107 1108 $html = sprintf( 1109 "<li><span class='post-meta-key'>%s</span> %s</li>\n", 1110 /* translators: %s: Post custom field name. */ 1111 sprintf( _x( '%s:', 'Post custom field name' ), $key ), 1112 $value 1113 ); 1114 1115 /** 1116 * Filters the HTML output of the li element in the post custom fields list. 1117 * 1118 * @since 2.2.0 1119 * 1120 * @param string $html The HTML output for the li element. 1121 * @param string $key Meta key. 1122 * @param string $value Meta value. 1123 */ 1124 $li_html .= apply_filters( 'the_meta_key', $html, $key, $value ); 1125 } 1126 1127 if ( $li_html ) { 1128 echo "<ul class='post-meta'>\n{$li_html}</ul>\n"; 1129 } 1130 } 1131 } 1132 1133 // 1134 // Pages. 1135 // 1136 1137 /** 1138 * Retrieves or displays a list of pages as a dropdown (select list). 1139 * 1140 * @since 2.1.0 1141 * @since 4.2.0 The `$value_field` argument was added. 1142 * @since 4.3.0 The `$class` argument was added. 1143 * 1144 * @see get_pages() 1145 * 1146 * @param array|string $args { 1147 * Optional. Array or string of arguments to generate a page dropdown. See get_pages() for additional arguments. 1148 * 1149 * @type int $depth Maximum depth. Default 0. 1150 * @type int $child_of Page ID to retrieve child pages of. Default 0. 1151 * @type int|string $selected Value of the option that should be selected. Default 0. 1152 * @type bool|int $echo Whether to echo or return the generated markup. Accepts 0, 1, 1153 * or their bool equivalents. Default 1. 1154 * @type string $name Value for the 'name' attribute of the select element. 1155 * Default 'page_id'. 1156 * @type string $id Value for the 'id' attribute of the select element. 1157 * @type string $class Value for the 'class' attribute of the select element. Default: none. 1158 * Defaults to the value of `$name`. 1159 * @type string $show_option_none Text to display for showing no pages. Default empty (does not display). 1160 * @type string $show_option_no_change Text to display for "no change" option. Default empty (does not display). 1161 * @type string $option_none_value Value to use when no page is selected. Default empty. 1162 * @type string $value_field Post field used to populate the 'value' attribute of the option 1163 * elements. Accepts any valid post field. Default 'ID'. 1164 * } 1165 * @return string HTML dropdown list of pages. 1166 */ 1167 function wp_dropdown_pages( $args = '' ) { 1168 $defaults = array( 1169 'depth' => 0, 1170 'child_of' => 0, 1171 'selected' => 0, 1172 'echo' => 1, 1173 'name' => 'page_id', 1174 'id' => '', 1175 'class' => '', 1176 'show_option_none' => '', 1177 'show_option_no_change' => '', 1178 'option_none_value' => '', 1179 'value_field' => 'ID', 1180 ); 1181 1182 $parsed_args = wp_parse_args( $args, $defaults ); 1183 1184 $pages = get_pages( $parsed_args ); 1185 $output = ''; 1186 // Back-compat with old system where both id and name were based on $name argument. 1187 if ( empty( $parsed_args['id'] ) ) { 1188 $parsed_args['id'] = $parsed_args['name']; 1189 } 1190 1191 if ( ! empty( $pages ) ) { 1192 $class = ''; 1193 if ( ! empty( $parsed_args['class'] ) ) { 1194 $class = " class='" . esc_attr( $parsed_args['class'] ) . "'"; 1195 } 1196 1197 $output = "<select name='" . esc_attr( $parsed_args['name'] ) . "'" . $class . " id='" . esc_attr( $parsed_args['id'] ) . "'>\n"; 1198 if ( $parsed_args['show_option_no_change'] ) { 1199 $output .= "\t<option value=\"-1\">" . $parsed_args['show_option_no_change'] . "</option>\n"; 1200 } 1201 if ( $parsed_args['show_option_none'] ) { 1202 $output .= "\t<option value=\"" . esc_attr( $parsed_args['option_none_value'] ) . '">' . $parsed_args['show_option_none'] . "</option>\n"; 1203 } 1204 $output .= walk_page_dropdown_tree( $pages, $parsed_args['depth'], $parsed_args ); 1205 $output .= "</select>\n"; 1206 } 1207 1208 /** 1209 * Filters the HTML output of a list of pages as a dropdown. 1210 * 1211 * @since 2.1.0 1212 * @since 4.4.0 `$parsed_args` and `$pages` added as arguments. 1213 * 1214 * @param string $output HTML output for dropdown list of pages. 1215 * @param array $parsed_args The parsed arguments array. See wp_dropdown_pages() 1216 * for information on accepted arguments. 1217 * @param WP_Post[] $pages Array of the page objects. 1218 */ 1219 $html = apply_filters( 'wp_dropdown_pages', $output, $parsed_args, $pages ); 1220 1221 if ( $parsed_args['echo'] ) { 1222 echo $html; 1223 } 1224 1225 return $html; 1226 } 1227 1228 /** 1229 * Retrieves or displays a list of pages (or hierarchical post type items) in list (li) format. 1230 * 1231 * @since 1.5.0 1232 * @since 4.7.0 Added the `item_spacing` argument. 1233 * 1234 * @see get_pages() 1235 * 1236 * @global WP_Query $wp_query WordPress Query object. 1237 * 1238 * @param array|string $args { 1239 * Optional. Array or string of arguments to generate a list of pages. See get_pages() for additional arguments. 1240 * 1241 * @type int $child_of Display only the sub-pages of a single page by ID. Default 0 (all pages). 1242 * @type string $authors Comma-separated list of author IDs. Default empty (all authors). 1243 * @type string $date_format PHP date format to use for the listed pages. Relies on the 'show_date' parameter. 1244 * Default is the value of 'date_format' option. 1245 * @type int $depth Number of levels in the hierarchy of pages to include in the generated list. 1246 * Accepts -1 (any depth), 0 (all pages), 1 (top-level pages only), and n (pages to 1247 * the given n depth). Default 0. 1248 * @type bool $echo Whether or not to echo the list of pages. Default true. 1249 * @type string $exclude Comma-separated list of page IDs to exclude. Default empty. 1250 * @type array $include Comma-separated list of page IDs to include. Default empty. 1251 * @type string $link_after Text or HTML to follow the page link label. Default null. 1252 * @type string $link_before Text or HTML to precede the page link label. Default null. 1253 * @type string $post_type Post type to query for. Default 'page'. 1254 * @type string|array $post_status Comma-separated list or array of post statuses to include. Default 'publish'. 1255 * @type string $show_date Whether to display the page publish or modified date for each page. Accepts 1256 * 'modified' or any other value. An empty value hides the date. Default empty. 1257 * @type string $sort_column Comma-separated list of column names to sort the pages by. Accepts 'post_author', 1258 * 'post_date', 'post_title', 'post_name', 'post_modified', 'post_modified_gmt', 1259 * 'menu_order', 'post_parent', 'ID', 'rand', or 'comment_count'. Default 'post_title'. 1260 * @type string $title_li List heading. Passing a null or empty value will result in no heading, and the list 1261 * will not be wrapped with unordered list `<ul>` tags. Default 'Pages'. 1262 * @type string $item_spacing Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' or 'discard'. 1263 * Default 'preserve'. 1264 * @type Walker $walker Walker instance to use for listing pages. Default empty which results in a 1265 * Walker_Page instance being used. 1266 * } 1267 * @return void|string Void if 'echo' argument is true, HTML list of pages if 'echo' is false. 1268 */ 1269 function wp_list_pages( $args = '' ) { 1270 $defaults = array( 1271 'depth' => 0, 1272 'show_date' => '', 1273 'date_format' => get_option( 'date_format' ), 1274 'child_of' => 0, 1275 'exclude' => '', 1276 'title_li' => __( 'Pages' ), 1277 'echo' => 1, 1278 'authors' => '', 1279 'sort_column' => 'menu_order, post_title', 1280 'link_before' => '', 1281 'link_after' => '', 1282 'item_spacing' => 'preserve', 1283 'walker' => '', 1284 ); 1285 1286 $parsed_args = wp_parse_args( $args, $defaults ); 1287 1288 if ( ! in_array( $parsed_args['item_spacing'], array( 'preserve', 'discard' ), true ) ) { 1289 // Invalid value, fall back to default. 1290 $parsed_args['item_spacing'] = $defaults['item_spacing']; 1291 } 1292 1293 $output = ''; 1294 $current_page = 0; 1295 1296 // Sanitize, mostly to keep spaces out. 1297 $parsed_args['exclude'] = preg_replace( '/[^0-9,]/', '', $parsed_args['exclude'] ); 1298 1299 // Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array). 1300 $exclude_array = ( $parsed_args['exclude'] ) ? explode( ',', $parsed_args['exclude'] ) : array(); 1301 1302 /** 1303 * Filters the array of pages to exclude from the pages list. 1304 * 1305 * @since 2.1.0 1306 * 1307 * @param string[] $exclude_array An array of page IDs to exclude. 1308 */ 1309 $parsed_args['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) ); 1310 1311 $parsed_args['hierarchical'] = 0; 1312 1313 // Query pages. 1314 $pages = get_pages( $parsed_args ); 1315 1316 if ( ! empty( $pages ) ) { 1317 if ( $parsed_args['title_li'] ) { 1318 $output .= '<li class="pagenav">' . $parsed_args['title_li'] . '<ul>'; 1319 } 1320 global $wp_query; 1321 if ( is_page() || is_attachment() || $wp_query->is_posts_page ) { 1322 $current_page = get_queried_object_id(); 1323 } elseif ( is_singular() ) { 1324 $queried_object = get_queried_object(); 1325 if ( is_post_type_hierarchical( $queried_object->post_type ) ) { 1326 $current_page = $queried_object->ID; 1327 } 1328 } 1329 1330 $output .= walk_page_tree( $pages, $parsed_args['depth'], $current_page, $parsed_args ); 1331 1332 if ( $parsed_args['title_li'] ) { 1333 $output .= '</ul></li>'; 1334 } 1335 } 1336 1337 /** 1338 * Filters the HTML output of the pages to list. 1339 * 1340 * @since 1.5.1 1341 * @since 4.4.0 `$pages` added as arguments. 1342 * 1343 * @see wp_list_pages() 1344 * 1345 * @param string $output HTML output of the pages list. 1346 * @param array $parsed_args An array of page-listing arguments. See wp_list_pages() 1347 * for information on accepted arguments. 1348 * @param WP_Post[] $pages Array of the page objects. 1349 */ 1350 $html = apply_filters( 'wp_list_pages', $output, $parsed_args, $pages ); 1351 1352 if ( $parsed_args['echo'] ) { 1353 echo $html; 1354 } else { 1355 return $html; 1356 } 1357 } 1358 1359 /** 1360 * Displays or retrieves a list of pages with an optional home link. 1361 * 1362 * The arguments are listed below and part of the arguments are for wp_list_pages() function. 1363 * Check that function for more info on those arguments. 1364 * 1365 * @since 2.7.0 1366 * @since 4.4.0 Added `menu_id`, `container`, `before`, `after`, and `walker` arguments. 1367 * @since 4.7.0 Added the `item_spacing` argument. 1368 * 1369 * @param array|string $args { 1370 * Optional. Array or string of arguments to generate a page menu. See wp_list_pages() for additional arguments. 1371 * 1372 * @type string $sort_column How to sort the list of pages. Accepts post column names. 1373 * Default 'menu_order, post_title'. 1374 * @type string $menu_id ID for the div containing the page list. Default is empty string. 1375 * @type string $menu_class Class to use for the element containing the page list. Default 'menu'. 1376 * @type string $container Element to use for the element containing the page list. Default 'div'. 1377 * @type bool $echo Whether to echo the list or return it. Accepts true (echo) or false (return). 1378 * Default true. 1379 * @type int|bool|string $show_home Whether to display the link to the home page. Can just enter the text 1380 * you'd like shown for the home link. 1|true defaults to 'Home'. 1381 * @type string $link_before The HTML or text to prepend to $show_home text. Default empty. 1382 * @type string $link_after The HTML or text to append to $show_home text. Default empty. 1383 * @type string $before The HTML or text to prepend to the menu. Default is '<ul>'. 1384 * @type string $after The HTML or text to append to the menu. Default is '</ul>'. 1385 * @type string $item_spacing Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' 1386 * or 'discard'. Default 'discard'. 1387 * @type Walker $walker Walker instance to use for listing pages. Default empty which results in a 1388 * Walker_Page instance being used. 1389 * } 1390 * @return void|string Void if 'echo' argument is true, HTML menu if 'echo' is false. 1391 */ 1392 function wp_page_menu( $args = array() ) { 1393 $defaults = array( 1394 'sort_column' => 'menu_order, post_title', 1395 'menu_id' => '', 1396 'menu_class' => 'menu', 1397 'container' => 'div', 1398 'echo' => true, 1399 'link_before' => '', 1400 'link_after' => '', 1401 'before' => '<ul>', 1402 'after' => '</ul>', 1403 'item_spacing' => 'discard', 1404 'walker' => '', 1405 ); 1406 $args = wp_parse_args( $args, $defaults ); 1407 1408 if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ), true ) ) { 1409 // Invalid value, fall back to default. 1410 $args['item_spacing'] = $defaults['item_spacing']; 1411 } 1412 1413 if ( 'preserve' === $args['item_spacing'] ) { 1414 $t = "\t"; 1415 $n = "\n"; 1416 } else { 1417 $t = ''; 1418 $n = ''; 1419 } 1420 1421 /** 1422 * Filters the arguments used to generate a page-based menu. 1423 * 1424 * @since 2.7.0 1425 * 1426 * @see wp_page_menu() 1427 * 1428 * @param array $args An array of page menu arguments. See wp_page_menu() 1429 * for information on accepted arguments. 1430 */ 1431 $args = apply_filters( 'wp_page_menu_args', $args ); 1432 1433 $menu = ''; 1434 1435 $list_args = $args; 1436 1437 // Show Home in the menu. 1438 if ( ! empty( $args['show_home'] ) ) { 1439 if ( true === $args['show_home'] || '1' === $args['show_home'] || 1 === $args['show_home'] ) { 1440 $text = __( 'Home' ); 1441 } else { 1442 $text = $args['show_home']; 1443 } 1444 $class = ''; 1445 if ( is_front_page() && ! is_paged() ) { 1446 $class = 'class="current_page_item"'; 1447 } 1448 $menu .= '<li ' . $class . '><a href="' . esc_url( home_url( '/' ) ) . '">' . $args['link_before'] . $text . $args['link_after'] . '</a></li>'; 1449 // If the front page is a page, add it to the exclude list. 1450 if ( 'page' === get_option( 'show_on_front' ) ) { 1451 if ( ! empty( $list_args['exclude'] ) ) { 1452 $list_args['exclude'] .= ','; 1453 } else { 1454 $list_args['exclude'] = ''; 1455 } 1456 $list_args['exclude'] .= get_option( 'page_on_front' ); 1457 } 1458 } 1459 1460 $list_args['echo'] = false; 1461 $list_args['title_li'] = ''; 1462 $menu .= wp_list_pages( $list_args ); 1463 1464 $container = sanitize_text_field( $args['container'] ); 1465 1466 // Fallback in case `wp_nav_menu()` was called without a container. 1467 if ( empty( $container ) ) { 1468 $container = 'div'; 1469 } 1470 1471 if ( $menu ) { 1472 1473 // wp_nav_menu() doesn't set before and after. 1474 if ( isset( $args['fallback_cb'] ) && 1475 'wp_page_menu' === $args['fallback_cb'] && 1476 'ul' !== $container ) { 1477 $args['before'] = "<ul>{$n}"; 1478 $args['after'] = '</ul>'; 1479 } 1480 1481 $menu = $args['before'] . $menu . $args['after']; 1482 } 1483 1484 $attrs = ''; 1485 if ( ! empty( $args['menu_id'] ) ) { 1486 $attrs .= ' id="' . esc_attr( $args['menu_id'] ) . '"'; 1487 } 1488 1489 if ( ! empty( $args['menu_class'] ) ) { 1490 $attrs .= ' class="' . esc_attr( $args['menu_class'] ) . '"'; 1491 } 1492 1493 $menu = "<{$container}{$attrs}>" . $menu . "</{$container}>{$n}"; 1494 1495 /** 1496 * Filters the HTML output of a page-based menu. 1497 * 1498 * @since 2.7.0 1499 * 1500 * @see wp_page_menu() 1501 * 1502 * @param string $menu The HTML output. 1503 * @param array $args An array of arguments. See wp_page_menu() 1504 * for information on accepted arguments. 1505 */ 1506 $menu = apply_filters( 'wp_page_menu', $menu, $args ); 1507 1508 if ( $args['echo'] ) { 1509 echo $menu; 1510 } else { 1511 return $menu; 1512 } 1513 } 1514 1515 // 1516 // Page helpers. 1517 // 1518 1519 /** 1520 * Retrieves HTML list content for page list. 1521 * 1522 * @uses Walker_Page to create HTML list content. 1523 * @since 2.1.0 1524 * 1525 * @param array $pages 1526 * @param int $depth 1527 * @param int $current_page 1528 * @param array $args 1529 * @return string 1530 */ 1531 function walk_page_tree( $pages, $depth, $current_page, $args ) { 1532 if ( empty( $args['walker'] ) ) { 1533 $walker = new Walker_Page; 1534 } else { 1535 /** 1536 * @var Walker $walker 1537 */ 1538 $walker = $args['walker']; 1539 } 1540 1541 foreach ( (array) $pages as $page ) { 1542 if ( $page->post_parent ) { 1543 $args['pages_with_children'][ $page->post_parent ] = true; 1544 } 1545 } 1546 1547 return $walker->walk( $pages, $depth, $args, $current_page ); 1548 } 1549 1550 /** 1551 * Retrieves HTML dropdown (select) content for page list. 1552 * 1553 * @since 2.1.0 1554 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 1555 * to the function signature. 1556 * 1557 * @uses Walker_PageDropdown to create HTML dropdown content. 1558 * @see Walker_PageDropdown::walk() for parameters and return description. 1559 * 1560 * @param mixed ...$args Elements array, maximum hierarchical depth and optional additional arguments. 1561 * @return string 1562 */ 1563 function walk_page_dropdown_tree( ...$args ) { 1564 if ( empty( $args[2]['walker'] ) ) { // The user's options are the third parameter. 1565 $walker = new Walker_PageDropdown; 1566 } else { 1567 /** 1568 * @var Walker $walker 1569 */ 1570 $walker = $args[2]['walker']; 1571 } 1572 1573 return $walker->walk( ...$args ); 1574 } 1575 1576 // 1577 // Attachments. 1578 // 1579 1580 /** 1581 * Displays an attachment page link using an image or icon. 1582 * 1583 * @since 2.0.0 1584 * 1585 * @param int|WP_Post $post Optional. Post ID or post object. 1586 * @param bool $fullsize Optional. Whether to use full size. Default false. 1587 * @param bool $deprecated Deprecated. Not used. 1588 * @param bool $permalink Optional. Whether to include permalink. Default false. 1589 */ 1590 function the_attachment_link( $post = 0, $fullsize = false, $deprecated = false, $permalink = false ) { 1591 if ( ! empty( $deprecated ) ) { 1592 _deprecated_argument( __FUNCTION__, '2.5.0' ); 1593 } 1594 1595 if ( $fullsize ) { 1596 echo wp_get_attachment_link( $post, 'full', $permalink ); 1597 } else { 1598 echo wp_get_attachment_link( $post, 'thumbnail', $permalink ); 1599 } 1600 } 1601 1602 /** 1603 * Retrieves an attachment page link using an image or icon, if possible. 1604 * 1605 * @since 2.5.0 1606 * @since 4.4.0 The `$post` parameter can now accept either a post ID or `WP_Post` object. 1607 * 1608 * @param int|WP_Post $post Optional. Post ID or post object. 1609 * @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array 1610 * of width and height values in pixels (in that order). Default 'thumbnail'. 1611 * @param bool $permalink Optional. Whether to add permalink to image. Default false. 1612 * @param bool $icon Optional. Whether the attachment is an icon. Default false. 1613 * @param string|false $text Optional. Link text to use. Activated by passing a string, false otherwise. 1614 * Default false. 1615 * @param array|string $attr Optional. Array or string of attributes. Default empty. 1616 * @return string HTML content. 1617 */ 1618 function wp_get_attachment_link( $post = 0, $size = 'thumbnail', $permalink = false, $icon = false, $text = false, $attr = '' ) { 1619 $_post = get_post( $post ); 1620 1621 if ( empty( $_post ) || ( 'attachment' !== $_post->post_type ) || ! wp_get_attachment_url( $_post->ID ) ) { 1622 return __( 'Missing Attachment' ); 1623 } 1624 1625 $url = wp_get_attachment_url( $_post->ID ); 1626 1627 if ( $permalink ) { 1628 $url = get_attachment_link( $_post->ID ); 1629 } 1630 1631 if ( $text ) { 1632 $link_text = $text; 1633 } elseif ( $size && 'none' !== $size ) { 1634 $link_text = wp_get_attachment_image( $_post->ID, $size, $icon, $attr ); 1635 } else { 1636 $link_text = ''; 1637 } 1638 1639 if ( '' === trim( $link_text ) ) { 1640 $link_text = $_post->post_title; 1641 } 1642 1643 if ( '' === trim( $link_text ) ) { 1644 $link_text = esc_html( pathinfo( get_attached_file( $_post->ID ), PATHINFO_FILENAME ) ); 1645 } 1646 1647 $link_html = "<a href='" . esc_url( $url ) . "'>$link_text</a>"; 1648 1649 /** 1650 * Filters a retrieved attachment page link. 1651 * 1652 * @since 2.7.0 1653 * @since 5.1.0 Added the `$attr` parameter. 1654 * 1655 * @param string $link_html The page link HTML output. 1656 * @param int|WP_Post $post Post ID or object. Can be 0 for the current global post. 1657 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1658 * an array of width and height values in pixels (in that order). 1659 * @param bool $permalink Whether to add permalink to image. Default false. 1660 * @param bool $icon Whether to include an icon. 1661 * @param string|false $text If string, will be link text. 1662 * @param array|string $attr Array or string of attributes. 1663 */ 1664 return apply_filters( 'wp_get_attachment_link', $link_html, $post, $size, $permalink, $icon, $text, $attr ); 1665 } 1666 1667 /** 1668 * Wraps attachment in paragraph tag before content. 1669 * 1670 * @since 2.0.0 1671 * 1672 * @param string $content 1673 * @return string 1674 */ 1675 function prepend_attachment( $content ) { 1676 $post = get_post(); 1677 1678 if ( empty( $post->post_type ) || 'attachment' !== $post->post_type ) { 1679 return $content; 1680 } 1681 1682 if ( wp_attachment_is( 'video', $post ) ) { 1683 $meta = wp_get_attachment_metadata( get_the_ID() ); 1684 $atts = array( 'src' => wp_get_attachment_url() ); 1685 if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) { 1686 $atts['width'] = (int) $meta['width']; 1687 $atts['height'] = (int) $meta['height']; 1688 } 1689 if ( has_post_thumbnail() ) { 1690 $atts['poster'] = wp_get_attachment_url( get_post_thumbnail_id() ); 1691 } 1692 $p = wp_video_shortcode( $atts ); 1693 } elseif ( wp_attachment_is( 'audio', $post ) ) { 1694 $p = wp_audio_shortcode( array( 'src' => wp_get_attachment_url() ) ); 1695 } else { 1696 $p = '<p class="attachment">'; 1697 // Show the medium sized image representation of the attachment if available, and link to the raw file. 1698 $p .= wp_get_attachment_link( 0, 'medium', false ); 1699 $p .= '</p>'; 1700 } 1701 1702 /** 1703 * Filters the attachment markup to be prepended to the post content. 1704 * 1705 * @since 2.0.0 1706 * 1707 * @see prepend_attachment() 1708 * 1709 * @param string $p The attachment HTML output. 1710 */ 1711 $p = apply_filters( 'prepend_attachment', $p ); 1712 1713 return "$p\n$content"; 1714 } 1715 1716 // 1717 // Misc. 1718 // 1719 1720 /** 1721 * Retrieves protected post password form content. 1722 * 1723 * @since 1.0.0 1724 * 1725 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 1726 * @return string HTML content for password form for password protected post. 1727 */ 1728 function get_the_password_form( $post = 0 ) { 1729 $post = get_post( $post ); 1730 $label = 'pwbox-' . ( empty( $post->ID ) ? rand() : $post->ID ); 1731 $output = '<form action="' . esc_url( site_url( 'wp-login.php?action=postpass', 'login_post' ) ) . '" class="post-password-form" method="post"> 1732 <p>' . __( 'This content is password protected. To view it please enter your password below:' ) . '</p> 1733 <p><label for="' . $label . '">' . __( 'Password:' ) . ' <input name="post_password" id="' . $label . '" type="password" size="20" /></label> <input type="submit" name="Submit" value="' . esc_attr_x( 'Enter', 'post password form' ) . '" /></p></form> 1734 '; 1735 1736 /** 1737 * Filters the HTML output for the protected post password form. 1738 * 1739 * If modifying the password field, please note that the core database schema 1740 * limits the password field to 20 characters regardless of the value of the 1741 * size attribute in the form input. 1742 * 1743 * @since 2.7.0 1744 * @since 5.8.0 Added the `$post` parameter. 1745 * 1746 * @param string $output The password form HTML output. 1747 * @param WP_Post $post Post object. 1748 */ 1749 return apply_filters( 'the_password_form', $output, $post ); 1750 } 1751 1752 /** 1753 * Determines whether the current post uses a page template. 1754 * 1755 * This template tag allows you to determine if you are in a page template. 1756 * You can optionally provide a template filename or array of template filenames 1757 * and then the check will be specific to that template. 1758 * 1759 * For more information on this and similar theme functions, check out 1760 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 1761 * Conditional Tags} article in the Theme Developer Handbook. 1762 * 1763 * @since 2.5.0 1764 * @since 4.2.0 The `$template` parameter was changed to also accept an array of page templates. 1765 * @since 4.7.0 Now works with any post type, not just pages. 1766 * 1767 * @param string|string[] $template The specific template filename or array of templates to match. 1768 * @return bool True on success, false on failure. 1769 */ 1770 function is_page_template( $template = '' ) { 1771 if ( ! is_singular() ) { 1772 return false; 1773 } 1774 1775 $page_template = get_page_template_slug( get_queried_object_id() ); 1776 1777 if ( empty( $template ) ) { 1778 return (bool) $page_template; 1779 } 1780 1781 if ( $template == $page_template ) { 1782 return true; 1783 } 1784 1785 if ( is_array( $template ) ) { 1786 if ( ( in_array( 'default', $template, true ) && ! $page_template ) 1787 || in_array( $page_template, $template, true ) 1788 ) { 1789 return true; 1790 } 1791 } 1792 1793 return ( 'default' === $template && ! $page_template ); 1794 } 1795 1796 /** 1797 * Gets the specific template filename for a given post. 1798 * 1799 * @since 3.4.0 1800 * @since 4.7.0 Now works with any post type, not just pages. 1801 * 1802 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 1803 * @return string|false Page template filename. Returns an empty string when the default page template 1804 * is in use. Returns false if the post does not exist. 1805 */ 1806 function get_page_template_slug( $post = null ) { 1807 $post = get_post( $post ); 1808 1809 if ( ! $post ) { 1810 return false; 1811 } 1812 1813 $template = get_post_meta( $post->ID, '_wp_page_template', true ); 1814 1815 if ( ! $template || 'default' === $template ) { 1816 return ''; 1817 } 1818 1819 return $template; 1820 } 1821 1822 /** 1823 * Retrieves formatted date timestamp of a revision (linked to that revisions's page). 1824 * 1825 * @since 2.6.0 1826 * 1827 * @param int|object $revision Revision ID or revision object. 1828 * @param bool $link Optional. Whether to link to revision's page. Default true. 1829 * @return string|false i18n formatted datetimestamp or localized 'Current Revision'. 1830 */ 1831 function wp_post_revision_title( $revision, $link = true ) { 1832 $revision = get_post( $revision ); 1833 1834 if ( ! $revision ) { 1835 return $revision; 1836 } 1837 1838 if ( ! in_array( $revision->post_type, array( 'post', 'page', 'revision' ), true ) ) { 1839 return false; 1840 } 1841 1842 /* translators: Revision date format, see https://www.php.net/manual/datetime.format.php */ 1843 $datef = _x( 'F j, Y @ H:i:s', 'revision date format' ); 1844 /* translators: %s: Revision date. */ 1845 $autosavef = __( '%s [Autosave]' ); 1846 /* translators: %s: Revision date. */ 1847 $currentf = __( '%s [Current Revision]' ); 1848 1849 $date = date_i18n( $datef, strtotime( $revision->post_modified ) ); 1850 $edit_link = get_edit_post_link( $revision->ID ); 1851 if ( $link && current_user_can( 'edit_post', $revision->ID ) && $edit_link ) { 1852 $date = "<a href='$edit_link'>$date</a>"; 1853 } 1854 1855 if ( ! wp_is_post_revision( $revision ) ) { 1856 $date = sprintf( $currentf, $date ); 1857 } elseif ( wp_is_post_autosave( $revision ) ) { 1858 $date = sprintf( $autosavef, $date ); 1859 } 1860 1861 return $date; 1862 } 1863 1864 /** 1865 * Retrieves formatted date timestamp of a revision (linked to that revisions's page). 1866 * 1867 * @since 3.6.0 1868 * 1869 * @param int|object $revision Revision ID or revision object. 1870 * @param bool $link Optional. Whether to link to revision's page. Default true. 1871 * @return string|false gravatar, user, i18n formatted datetimestamp or localized 'Current Revision'. 1872 */ 1873 function wp_post_revision_title_expanded( $revision, $link = true ) { 1874 $revision = get_post( $revision ); 1875 1876 if ( ! $revision ) { 1877 return $revision; 1878 } 1879 1880 if ( ! in_array( $revision->post_type, array( 'post', 'page', 'revision' ), true ) ) { 1881 return false; 1882 } 1883 1884 $author = get_the_author_meta( 'display_name', $revision->post_author ); 1885 /* translators: Revision date format, see https://www.php.net/manual/datetime.format.php */ 1886 $datef = _x( 'F j, Y @ H:i:s', 'revision date format' ); 1887 1888 $gravatar = get_avatar( $revision->post_author, 24 ); 1889 1890 $date = date_i18n( $datef, strtotime( $revision->post_modified ) ); 1891 $edit_link = get_edit_post_link( $revision->ID ); 1892 if ( $link && current_user_can( 'edit_post', $revision->ID ) && $edit_link ) { 1893 $date = "<a href='$edit_link'>$date</a>"; 1894 } 1895 1896 $revision_date_author = sprintf( 1897 /* translators: Post revision title. 1: Author avatar, 2: Author name, 3: Time ago, 4: Date. */ 1898 __( '%1$s %2$s, %3$s ago (%4$s)' ), 1899 $gravatar, 1900 $author, 1901 human_time_diff( strtotime( $revision->post_modified_gmt ) ), 1902 $date 1903 ); 1904 1905 /* translators: %s: Revision date with author avatar. */ 1906 $autosavef = __( '%s [Autosave]' ); 1907 /* translators: %s: Revision date with author avatar. */ 1908 $currentf = __( '%s [Current Revision]' ); 1909 1910 if ( ! wp_is_post_revision( $revision ) ) { 1911 $revision_date_author = sprintf( $currentf, $revision_date_author ); 1912 } elseif ( wp_is_post_autosave( $revision ) ) { 1913 $revision_date_author = sprintf( $autosavef, $revision_date_author ); 1914 } 1915 1916 /** 1917 * Filters the formatted author and date for a revision. 1918 * 1919 * @since 4.4.0 1920 * 1921 * @param string $revision_date_author The formatted string. 1922 * @param WP_Post $revision The revision object. 1923 * @param bool $link Whether to link to the revisions page, as passed into 1924 * wp_post_revision_title_expanded(). 1925 */ 1926 return apply_filters( 'wp_post_revision_title_expanded', $revision_date_author, $revision, $link ); 1927 } 1928 1929 /** 1930 * Displays a list of a post's revisions. 1931 * 1932 * Can output either a UL with edit links or a TABLE with diff interface, and 1933 * restore action links. 1934 * 1935 * @since 2.6.0 1936 * 1937 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 1938 * @param string $type 'all' (default), 'revision' or 'autosave' 1939 */ 1940 function wp_list_post_revisions( $post = 0, $type = 'all' ) { 1941 $post = get_post( $post ); 1942 1943 if ( ! $post ) { 1944 return; 1945 } 1946 1947 // $args array with (parent, format, right, left, type) deprecated since 3.6. 1948 if ( is_array( $type ) ) { 1949 $type = ! empty( $type['type'] ) ? $type['type'] : $type; 1950 _deprecated_argument( __FUNCTION__, '3.6.0' ); 1951 } 1952 1953 $revisions = wp_get_post_revisions( $post->ID ); 1954 1955 if ( ! $revisions ) { 1956 return; 1957 } 1958 1959 $rows = ''; 1960 foreach ( $revisions as $revision ) { 1961 if ( ! current_user_can( 'read_post', $revision->ID ) ) { 1962 continue; 1963 } 1964 1965 $is_autosave = wp_is_post_autosave( $revision ); 1966 if ( ( 'revision' === $type && $is_autosave ) || ( 'autosave' === $type && ! $is_autosave ) ) { 1967 continue; 1968 } 1969 1970 $rows .= "\t<li>" . wp_post_revision_title_expanded( $revision ) . "</li>\n"; 1971 } 1972 1973 echo "<div class='hide-if-js'><p>" . __( 'JavaScript must be enabled to use this feature.' ) . "</p></div>\n"; 1974 1975 echo "<ul class='post-revisions hide-if-no-js'>\n"; 1976 echo $rows; 1977 echo '</ul>'; 1978 } 1979 1980 /** 1981 * Retrieves the parent post object for the given post. 1982 * 1983 * @since 5.7.0 1984 * 1985 * @param int|WP_Post|null $post Optional. Post ID or WP_Post object. Default is global $post. 1986 * @return WP_Post|null Parent post object, or null if there isn't one. 1987 */ 1988 function get_post_parent( $post = null ) { 1989 $wp_post = get_post( $post ); 1990 return ! empty( $wp_post->post_parent ) ? get_post( $wp_post->post_parent ) : null; 1991 } 1992 1993 /** 1994 * Returns whether the given post has a parent post. 1995 * 1996 * @since 5.7.0 1997 * 1998 * @param int|WP_Post|null $post Optional. Post ID or WP_Post object. Default is global $post. 1999 * @return bool Whether the post has a parent post. 2000 */ 2001 function has_post_parent( $post = null ) { 2002 return (bool) get_post_parent( $post ); 2003 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Aug 19 08:20:02 2022 | Cross-referenced by PHPXref |