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