| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * General template tags that can go anywhere in a template. 4 * 5 * @package WordPress 6 * @subpackage Template 7 */ 8 9 /** 10 * Loads header template. 11 * 12 * Includes the header template for a theme or if a name is specified then a 13 * specialized header will be included. 14 * 15 * For the parameter, if the file is called "header-special.php" then specify 16 * "special". 17 * 18 * @since 1.5.0 19 * @since 5.5.0 A return value was added. 20 * @since 5.5.0 The `$args` parameter was added. 21 * 22 * @param string|null $name The name of the specialized header. Default null. 23 * @param array $args Optional. Additional arguments passed to the header template. 24 * Default empty array. 25 * @return void|false Void on success, false if the template does not exist. 26 */ 27 function get_header( $name = null, $args = array() ) { 28 /** 29 * Fires before the header template file is loaded. 30 * 31 * @since 2.1.0 32 * @since 2.8.0 The `$name` parameter was added. 33 * @since 5.5.0 The `$args` parameter was added. 34 * 35 * @param string|null $name Name of the specific header file to use. Null for the default header. 36 * @param array $args Additional arguments passed to the header template. 37 */ 38 do_action( 'get_header', $name, $args ); 39 40 $templates = array(); 41 $name = (string) $name; 42 if ( '' !== $name ) { 43 $templates[] = "header-{$name}.php"; 44 } 45 46 $templates[] = 'header.php'; 47 48 if ( ! locate_template( $templates, true, true, $args ) ) { 49 return false; 50 } 51 } 52 53 /** 54 * Loads footer template. 55 * 56 * Includes the footer template for a theme or if a name is specified then a 57 * specialized footer will be included. 58 * 59 * For the parameter, if the file is called "footer-special.php" then specify 60 * "special". 61 * 62 * @since 1.5.0 63 * @since 5.5.0 A return value was added. 64 * @since 5.5.0 The `$args` parameter was added. 65 * 66 * @param string|null $name The name of the specialized footer. Default null. 67 * @param array $args Optional. Additional arguments passed to the footer template. 68 * Default empty array. 69 * @return void|false Void on success, false if the template does not exist. 70 */ 71 function get_footer( $name = null, $args = array() ) { 72 /** 73 * Fires before the footer template file is loaded. 74 * 75 * @since 2.1.0 76 * @since 2.8.0 The `$name` parameter was added. 77 * @since 5.5.0 The `$args` parameter was added. 78 * 79 * @param string|null $name Name of the specific footer file to use. Null for the default footer. 80 * @param array $args Additional arguments passed to the footer template. 81 */ 82 do_action( 'get_footer', $name, $args ); 83 84 $templates = array(); 85 $name = (string) $name; 86 if ( '' !== $name ) { 87 $templates[] = "footer-{$name}.php"; 88 } 89 90 $templates[] = 'footer.php'; 91 92 if ( ! locate_template( $templates, true, true, $args ) ) { 93 return false; 94 } 95 } 96 97 /** 98 * Loads sidebar template. 99 * 100 * Includes the sidebar template for a theme or if a name is specified then a 101 * specialized sidebar will be included. 102 * 103 * For the parameter, if the file is called "sidebar-special.php" then specify 104 * "special". 105 * 106 * @since 1.5.0 107 * @since 5.5.0 A return value was added. 108 * @since 5.5.0 The `$args` parameter was added. 109 * 110 * @param string|null $name The name of the specialized sidebar. Default null. 111 * @param array $args Optional. Additional arguments passed to the sidebar template. 112 * Default empty array. 113 * @return void|false Void on success, false if the template does not exist. 114 */ 115 function get_sidebar( $name = null, $args = array() ) { 116 /** 117 * Fires before the sidebar template file is loaded. 118 * 119 * @since 2.2.0 120 * @since 2.8.0 The `$name` parameter was added. 121 * @since 5.5.0 The `$args` parameter was added. 122 * 123 * @param string|null $name Name of the specific sidebar file to use. Null for the default sidebar. 124 * @param array $args Additional arguments passed to the sidebar template. 125 */ 126 do_action( 'get_sidebar', $name, $args ); 127 128 $templates = array(); 129 $name = (string) $name; 130 if ( '' !== $name ) { 131 $templates[] = "sidebar-{$name}.php"; 132 } 133 134 $templates[] = 'sidebar.php'; 135 136 if ( ! locate_template( $templates, true, true, $args ) ) { 137 return false; 138 } 139 } 140 141 /** 142 * Loads a template part into a template. 143 * 144 * Provides a simple mechanism for child themes to overload reusable sections of code 145 * in the theme. 146 * 147 * Includes the named template part for a theme or if a name is specified then a 148 * specialized part will be included. If the theme contains no {slug}.php file 149 * then no template will be included. 150 * 151 * The template is included using require, not require_once, so you may include the 152 * same template part multiple times. 153 * 154 * For the $name parameter, if the file is called "{slug}-special.php" then specify 155 * "special". 156 * 157 * @since 3.0.0 158 * @since 5.5.0 A return value was added. 159 * @since 5.5.0 The `$args` parameter was added. 160 * 161 * @param string $slug The slug name for the generic template. 162 * @param string|null $name Optional. The name of the specialized template. Default null. 163 * @param array $args Optional. Additional arguments passed to the template. 164 * Default empty array. 165 * @return void|false Void on success, false if the template does not exist. 166 */ 167 function get_template_part( $slug, $name = null, $args = array() ) { 168 /** 169 * Fires before the specified template part file is loaded. 170 * 171 * The dynamic portion of the hook name, `$slug`, refers to the slug name 172 * for the generic template part. 173 * 174 * @since 3.0.0 175 * @since 5.5.0 The `$args` parameter was added. 176 * 177 * @param string $slug The slug name for the generic template. 178 * @param string|null $name The name of the specialized template 179 * or null if there is none. 180 * @param array $args Additional arguments passed to the template. 181 */ 182 do_action( "get_template_part_{$slug}", $slug, $name, $args ); 183 184 $templates = array(); 185 $name = (string) $name; 186 if ( '' !== $name ) { 187 $templates[] = "{$slug}-{$name}.php"; 188 } 189 190 $templates[] = "{$slug}.php"; 191 192 /** 193 * Fires before an attempt is made to locate and load a template part. 194 * 195 * @since 5.2.0 196 * @since 5.5.0 The `$args` parameter was added. 197 * 198 * @param string $slug The slug name for the generic template. 199 * @param string $name The name of the specialized template 200 * or an empty string if there is none. 201 * @param string[] $templates Array of template files to search for, in order. 202 * @param array $args Additional arguments passed to the template. 203 */ 204 do_action( 'get_template_part', $slug, $name, $templates, $args ); 205 206 if ( ! locate_template( $templates, true, false, $args ) ) { 207 return false; 208 } 209 } 210 211 /** 212 * Displays search form. 213 * 214 * Will first attempt to locate the searchform.php file in either the child or 215 * the parent, then load it. If it doesn't exist, then the default search form 216 * will be displayed. The default search form is HTML, which will be displayed. 217 * There is a filter applied to the search form HTML in order to edit or replace 218 * it. The filter is {@see 'get_search_form'}. 219 * 220 * This function is primarily used by themes which want to hardcode the search 221 * form into the sidebar and also by the search widget in WordPress. 222 * 223 * There is also an action that is called whenever the function is run called, 224 * {@see 'pre_get_search_form'}. This can be useful for outputting JavaScript that the 225 * search relies on or various formatting that applies to the beginning of the 226 * search. To give a few examples of what it can be used for. 227 * 228 * @since 2.7.0 229 * @since 5.2.0 The `$args` array parameter was added in place of an `$echo` boolean flag. 230 * 231 * @param array $args { 232 * Optional. Array of display arguments. 233 * 234 * @type bool $echo Whether to echo or return the form. Default true. 235 * @type string $aria_label ARIA label for the search form. Useful to distinguish 236 * multiple search forms on the same page and improve 237 * accessibility. Default empty. 238 * } 239 * @return string|void Search form HTML if 'echo' is false, nothing otherwise. 240 * @phpstan-param array<string, mixed>|bool $args 241 * @phpstan-return ( 242 * $args is array{ echo: false|0|''|'0', ... } 243 * ? string 244 * : ( $args is false|0|''|'0' ? string : void ) 245 * ) 246 */ 247 function get_search_form( $args = array() ) { 248 /** 249 * Fires before the search form is retrieved, at the start of get_search_form(). 250 * 251 * @since 2.7.0 as 'get_search_form' action. 252 * @since 3.6.0 253 * @since 5.5.0 The `$args` parameter was added. 254 * 255 * @link https://core.trac.wordpress.org/ticket/19321 256 * 257 * @param array $args The array of arguments for building the search form. 258 * See get_search_form() for information on accepted arguments. 259 */ 260 do_action( 'pre_get_search_form', $args ); 261 262 $echo = true; 263 264 if ( ! is_array( $args ) ) { 265 /* 266 * Back compat: to ensure previous uses of get_search_form() continue to 267 * function as expected, we handle a value for the boolean $echo param removed 268 * in 5.2.0. Then we deal with the $args array and cast its defaults. 269 */ 270 $echo = (bool) $args; 271 272 // Set an empty array and allow default arguments to take over. 273 $args = array(); 274 } 275 276 // Defaults are to echo and to output no custom label on the form. 277 $defaults = array( 278 'echo' => $echo, 279 'aria_label' => '', 280 ); 281 282 $args = wp_parse_args( $args, $defaults ); 283 284 /** 285 * Filters the array of arguments used when generating the search form. 286 * 287 * @since 5.2.0 288 * 289 * @param array $args The array of arguments for building the search form. 290 * See get_search_form() for information on accepted arguments. 291 */ 292 $args = apply_filters( 'search_form_args', $args ); 293 294 // Ensure that the filtered arguments contain all required default values. 295 $args = array_merge( $defaults, $args ); 296 297 $format = current_theme_supports( 'html5', 'search-form' ) ? 'html5' : 'xhtml'; 298 299 /** 300 * Filters the HTML format of the search form. 301 * 302 * @since 3.6.0 303 * @since 5.5.0 The `$args` parameter was added. 304 * 305 * @param string $format The type of markup to use in the search form. 306 * Accepts 'html5', 'xhtml'. 307 * @param array $args The array of arguments for building the search form. 308 * See get_search_form() for information on accepted arguments. 309 */ 310 $format = apply_filters( 'search_form_format', $format, $args ); 311 312 $search_form_template = locate_template( 'searchform.php' ); 313 314 if ( '' !== $search_form_template ) { 315 ob_start(); 316 require $search_form_template; 317 $form = ob_get_clean(); 318 } else { 319 // Build a string containing an aria-label to use for the search form. 320 if ( $args['aria_label'] ) { 321 $aria_label = 'aria-label="' . esc_attr( $args['aria_label'] ) . '" '; 322 } else { 323 /* 324 * If there's no custom aria-label, we can set a default here. At the 325 * moment it's empty as there's uncertainty about what the default should be. 326 */ 327 $aria_label = ''; 328 } 329 330 if ( 'html5' === $format ) { 331 $form = '<form role="search" ' . $aria_label . 'method="get" class="search-form" action="' . esc_url( home_url( '/' ) ) . '"> 332 <label> 333 <span class="screen-reader-text">' . 334 /* translators: Hidden accessibility text. */ 335 _x( 'Search for:', 'label' ) . 336 '</span> 337 <input type="search" class="search-field" placeholder="' . esc_attr_x( 'Search …', 'placeholder' ) . '" value="' . get_search_query() . '" name="s" /> 338 </label> 339 <input type="submit" class="search-submit" value="' . esc_attr_x( 'Search', 'submit button' ) . '" /> 340 </form>'; 341 } else { 342 $form = '<form role="search" ' . $aria_label . 'method="get" id="searchform" class="searchform" action="' . esc_url( home_url( '/' ) ) . '"> 343 <div> 344 <label class="screen-reader-text" for="s">' . 345 /* translators: Hidden accessibility text. */ 346 _x( 'Search for:', 'label' ) . 347 '</label> 348 <input type="text" value="' . get_search_query() . '" name="s" id="s" /> 349 <input type="submit" id="searchsubmit" value="' . esc_attr_x( 'Search', 'submit button' ) . '" /> 350 </div> 351 </form>'; 352 } 353 } 354 355 /** 356 * Filters the HTML output of the search form. 357 * 358 * @since 2.7.0 359 * @since 5.5.0 The `$args` parameter was added. 360 * 361 * @param string $form The search form HTML output. 362 * @param array $args The array of arguments for building the search form. 363 * See get_search_form() for information on accepted arguments. 364 */ 365 $result = apply_filters( 'get_search_form', $form, $args ); 366 367 if ( null === $result ) { 368 $result = $form; 369 } 370 371 if ( $args['echo'] ) { 372 echo $result; 373 } else { 374 return $result; 375 } 376 } 377 378 379 /** 380 * Retrieves the markup for an accessible tooltip. 381 * 382 * Returns a button with an accessible name popover hint. 383 * 384 * @since 7.1.0 385 * 386 * @param string $content Plain-text tooltip content. An empty value returns an empty string. 387 * @param array $args { 388 * Optional. Arguments for building the tooltip. 389 * 390 * @type string $id Unique ID for the popover element. Default is a 391 * generated unique ID. 392 * @type string $button Existing `button` or `a` markup. Used instead of generated button. 393 * Default empty string. 394 * @type string $label Not used for tooltips. 395 * @type string $close_label Not used for tooltips. 396 * @type string $icon Dashicons icon class for the toggle button. 397 * Default 'dashicons-editor-help'. Should match the control's 398 * visible label. 399 * @type string $class Additional class(es) for the wrapping element. 400 * Default empty. 401 * } 402 * @return string Tooltip HTML markup, or an empty string when no content is provided. 403 */ 404 function wp_get_tooltip( $content, $args = array() ) { 405 $args['type'] = 'tooltip'; 406 return wp_get_tooltip_helper( $content, $args ); 407 } 408 409 /** 410 * Retrieves the markup for an accessible toggle tip. 411 * 412 * Returns a button and an action triggered toggle tip with `$content`. 413 * 414 * @since 7.1.0 415 * 416 * @param string $content Plain-text tooltip content. An empty value returns an empty string. 417 * @param array $args { 418 * Optional. Arguments for building the tooltip. 419 * 420 * @type string $id Unique ID for the popover element. Default is a 421 * generated unique ID. 422 * @type string $button Existing `button` markup. Used instead of generated button. 423 * Default empty string. 424 * @type string $label Accessible label for the toggle button. 425 * Default 'Help', matching the default icon. 426 * Ignored for tooltips. 427 * @type string $close_label Accessible label for the close button. Default 'Close'. 428 * @type string $icon Dashicons icon class for the toggle button. 429 * Default 'dashicons-editor-help'. Should match the control's 430 * visible label. 431 * @type string $class Additional class(es) for the wrapping element. 432 * Default empty. 433 * } 434 * @return string Toggletip HTML markup, or an empty string when no content is provided. 435 */ 436 function wp_get_toggletip( $content, $args = array() ) { 437 $args['type'] = 'toggletip'; 438 return wp_get_tooltip_helper( $content, $args ); 439 } 440 441 /** 442 * Retrieves the markup for an accessible tooltip or toggletip. 443 * 444 * Returns a button and either a hover/focus triggered tooltip popover or an action 445 * triggered toggle tip. Enqueue the `wp-tooltip` style and script where it is used. 446 * Tooltips are used to show the accessible name of a control. 447 * Toggletips are used for longer supporting text explaining context. 448 * 449 * @since 7.1.0 450 * 451 * @param string $content Plain-text tooltip content. An empty value returns an empty string. 452 * @param array $args { 453 * Optional. Arguments for building the tooltip. 454 * 455 * @type string $id Unique ID for the popover element. Default is a 456 * generated unique ID. 457 * @type string $button Existing `button` or `a` markup. Used instead of generated button. 458 * Default empty string. 459 * @type string $label Accessible label for the toggle button. 460 * Default 'Help', matching the default icon. 461 * Ignored for tooltips. 462 * @type string $close_label Accessible label for the close button. Default 'Close'. 463 * @type string $icon Dashicons icon class for the toggle button. 464 * Default 'dashicons-editor-help'. Should match the control's 465 * visible label. 466 * @type string $class Additional class(es) for the wrapping element. 467 * Default empty. 468 * @type string $type Type of tooltip: either `tooltip` or `toggletip`. 469 * Default 'tooltip'. 470 * } 471 * @return string Tooltip HTML markup, or an empty string when no content is provided. 472 */ 473 function wp_get_tooltip_helper( $content, $args = array() ) { 474 $content = trim( (string) $content ); 475 476 if ( '' === $content ) { 477 return ''; 478 } 479 480 $defaults = array( 481 'id' => wp_unique_id( 'wp-tooltip-' ), 482 'button' => '', 483 'label' => __( 'Help' ), 484 'close_label' => __( 'Close' ), 485 'icon' => 'dashicons-editor-help', 486 'class' => '', 487 'type' => 'tooltip', 488 ); 489 490 $args = wp_parse_args( $args, $defaults ); 491 492 $classes = ( 'tooltip' === $args['type'] ) ? 'wp-tooltip wp-is-tooltip' : 'wp-tooltip wp-is-toggletip'; 493 if ( '' !== $args['class'] ) { 494 $classes .= ' ' . $args['class']; 495 } 496 497 $icon = ( $args['icon'] ) ? trim( $args['icon'] ) : $defaults['icon']; 498 $id = ( $args['id'] ) ? $args['id'] : $defaults['id']; 499 500 // Tooltips use the content as the accessible name; toggletips use the label. 501 $label = ( 'tooltip' === $args['type'] ) ? wp_strip_all_tags( $content, true ) : $args['label']; 502 503 /* 504 * The generated button is a plain skeleton. Every dynamic attribute is 505 * added through the tag processor below, so caller-supplied markup is 506 * never scanned or substituted and a percent sign in custom markup, 507 * such as a percent-encoded URL, is never treated as a conversion 508 * specification. 509 */ 510 $default_button = '<button type="button"><span></span></button>'; 511 512 $is_default = ! $args['button']; 513 $button = ( $args['button'] ) ? $args['button'] : $default_button; 514 515 // The accepted root element is a `button`, or an `a` for tooltips. 516 $tag = false; 517 $processor = new WP_HTML_Tag_Processor( $button ); 518 if ( true === $processor->next_tag( 'button' ) ) { 519 $tag = 'button'; 520 } else { 521 $processor = new WP_HTML_Tag_Processor( $button ); 522 if ( 'tooltip' === $args['type'] && true === $processor->next_tag( 'a' ) ) { 523 $tag = 'a'; 524 } 525 } 526 527 if ( false === $tag ) { 528 // Button HTML passed was not valid. Reset to default. 529 $is_default = true; 530 $button = $default_button; 531 $processor = new WP_HTML_Tag_Processor( $button ); 532 $processor->next_tag( 'button' ); 533 $tag = 'button'; 534 } 535 536 /* 537 * Attributes that apply to every accepted button are added in one pass. 538 * Attributes that name the control are only added when the caller's 539 * markup did not already provide them. 540 */ 541 if ( null === $processor->get_attribute( 'aria-label' ) ) { 542 $processor->set_attribute( 'aria-label', $label ); 543 } 544 $processor->add_class( 'wp-tooltip__toggle' ); 545 if ( 'button' === $tag && 'tooltip' !== $args['type'] ) { 546 $processor->set_attribute( 'popovertarget', $id ); 547 $processor->set_attribute( 'aria-haspopup', 'dialog' ); 548 } 549 550 // The generated button also carries the dashicon on its inner span. 551 if ( $is_default && true === $processor->next_tag( 'span' ) ) { 552 $processor->set_attribute( 'class', 'dashicons ' . $icon ); 553 $processor->set_attribute( 'aria-hidden', 'true' ); 554 } 555 556 $button = $processor->get_updated_html(); 557 558 /* 559 * The markup only uses phrasing content so it is valid when nested 560 * in a phrasing context. Sectioning content (e.g. `div`, `dialog`) will 561 * cause the parser to close an open `p`, creating an empty and breaking 562 * the layout. See #65660. 563 */ 564 if ( 'tooltip' === $args['type'] ) { 565 $markup = sprintf( 566 '<span class="%1$s"> 567 %6$s 568 <span popover="hint" id="%2$s" class="wp-tooltip__bubble" role="tooltip">' . 569 '<span id="%2$s-text" class="wp-tooltip__text">%5$s</span>' . 570 '</span>' . 571 '</span>', 572 esc_attr( $classes ), 573 esc_attr( $id ), 574 esc_attr( $label ), 575 esc_attr( $icon ), 576 esc_html( $content ), 577 $button, 578 ); 579 } else { 580 /* 581 * A `span` with `role="dialog"` is used instead of a `dialog` element to keep the 582 * markup as phrasing content. The `aria-label`, `tabindex`, and `autofocus` 583 * attributes reproduce the accessible name and focus handling of the native element. 584 */ 585 $markup = sprintf( 586 '<span class="%1$s"> 587 %8$s 588 <span popover="hint" id="%2$s-hint" class="wp-tooltip__bubble wp-tooltip__hint" aria-hidden="true">' . 589 '<span class="wp-tooltip__text">%7$s</span>' . 590 '</span>' . 591 '<span popover="auto" id="%2$s" class="wp-tooltip__bubble" role="dialog" aria-label="%3$s" tabindex="-1" autofocus>' . 592 '<span id="%2$s-text" class="wp-tooltip__text">%5$s</span>' . 593 '<button type="button" class="wp-tooltip__close" popovertarget="%2$s" popovertargetaction="hide" aria-label="%6$s">' . 594 '<span class="dashicons dashicons-no-alt" aria-hidden="true"></span>' . 595 '</button>' . 596 '</span>' . 597 '</span>', 598 esc_attr( $classes ), 599 esc_attr( $id ), 600 esc_attr( $label ), 601 esc_attr( $icon ), 602 esc_html( $content ), 603 esc_attr( $args['close_label'] ), 604 esc_html( $args['label'] ), 605 $button, 606 ); 607 } 608 609 return $markup; 610 } 611 612 /** 613 * Displays the Log In/Out link. 614 * 615 * Displays a link, which allows users to navigate to the Log In page to log in 616 * or log out depending on whether they are currently logged in. 617 * 618 * @since 1.5.0 619 * 620 * @param string $redirect Optional path to redirect to on login/logout. 621 * @param bool $display Default to echo and not return the link. 622 * @return string|void Log in/out link if `$display` is false, nothing otherwise. 623 * @phpstan-return ( $display is true ? void : string ) 624 */ 625 function wp_loginout( $redirect = '', $display = true ) { 626 if ( ! is_user_logged_in() ) { 627 $link = '<a href="' . esc_url( wp_login_url( $redirect ) ) . '">' . __( 'Log in' ) . '</a>'; 628 } else { 629 $link = '<a href="' . esc_url( wp_logout_url( $redirect ) ) . '">' . __( 'Log out' ) . '</a>'; 630 } 631 632 if ( $display ) { 633 /** 634 * Filters the HTML output for the Log In/Log Out link. 635 * 636 * @since 1.5.0 637 * 638 * @param string $link The HTML link content. 639 */ 640 echo apply_filters( 'loginout', $link ); 641 } else { 642 /** This filter is documented in wp-includes/general-template.php */ 643 return apply_filters( 'loginout', $link ); 644 } 645 } 646 647 /** 648 * Retrieves the logout URL. 649 * 650 * Returns the URL that allows the user to log out of the site. 651 * 652 * @since 2.7.0 653 * 654 * @param string $redirect Path to redirect to on logout. 655 * @return string The logout URL. Note: HTML-encoded via esc_html() in wp_nonce_url(). 656 */ 657 function wp_logout_url( $redirect = '' ) { 658 $args = array(); 659 if ( ! empty( $redirect ) ) { 660 $args['redirect_to'] = urlencode( $redirect ); 661 } 662 663 $logout_url = add_query_arg( $args, site_url( 'wp-login.php?action=logout', 'login' ) ); 664 $logout_url = wp_nonce_url( $logout_url, 'log-out' ); 665 666 /** 667 * Filters the logout URL. 668 * 669 * @since 2.8.0 670 * 671 * @param string $logout_url The HTML-encoded logout URL. 672 * @param string $redirect Path to redirect to on logout. 673 */ 674 return apply_filters( 'logout_url', $logout_url, $redirect ); 675 } 676 677 /** 678 * Retrieves the login URL. 679 * 680 * @since 2.7.0 681 * 682 * @param string $redirect Path to redirect to on log in. 683 * @param bool $force_reauth Whether to force reauthorization, even if a cookie is present. 684 * Default false. 685 * @return string The login URL. Not HTML-encoded. 686 */ 687 function wp_login_url( $redirect = '', $force_reauth = false ) { 688 $login_url = site_url( 'wp-login.php', 'login' ); 689 690 if ( ! empty( $redirect ) ) { 691 $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url ); 692 } 693 694 if ( $force_reauth ) { 695 $login_url = add_query_arg( 'reauth', '1', $login_url ); 696 } 697 698 /** 699 * Filters the login URL. 700 * 701 * @since 2.8.0 702 * @since 4.2.0 The `$force_reauth` parameter was added. 703 * 704 * @param string $login_url The login URL. Not HTML-encoded. 705 * @param string $redirect The path to redirect to on login, if supplied. 706 * @param bool $force_reauth Whether to force reauthorization, even if a cookie is present. 707 */ 708 return apply_filters( 'login_url', $login_url, $redirect, $force_reauth ); 709 } 710 711 /** 712 * Returns the URL that allows the user to register on the site. 713 * 714 * @since 3.6.0 715 * 716 * @return string User registration URL. 717 */ 718 function wp_registration_url() { 719 /** 720 * Filters the user registration URL. 721 * 722 * @since 3.6.0 723 * 724 * @param string $register The user registration URL. 725 */ 726 return apply_filters( 'register_url', site_url( 'wp-login.php?action=register', 'login' ) ); 727 } 728 729 /** 730 * Provides a simple login form for use anywhere within WordPress. 731 * 732 * The login form HTML is echoed by default. Pass a false value for `$echo` to return it instead. 733 * 734 * @since 3.0.0 735 * @since 6.6.0 Added `required_username` and `required_password` arguments. 736 * 737 * @param array $args { 738 * Optional. Array of options to control the form output. Default empty array. 739 * 740 * @type bool $echo Whether to display the login form or return the form HTML code. 741 * Default true (echo). 742 * @type string $redirect URL to redirect to. Must be absolute, as in "https://example.com/mypage/". 743 * Default is to redirect back to the request URI. 744 * @type string $form_id ID attribute value for the form. Default 'loginform'. 745 * @type string $label_username Label for the username or email address field. Default 'Username or Email Address'. 746 * @type string $label_password Label for the password field. Default 'Password'. 747 * @type string $label_remember Label for the remember field. Default 'Remember Me'. 748 * @type string $label_log_in Label for the submit button. Default 'Log In'. 749 * @type string $id_username ID attribute value for the username field. Default 'user_login'. 750 * @type string $id_password ID attribute value for the password field. Default 'user_pass'. 751 * @type string $id_remember ID attribute value for the remember field. Default 'rememberme'. 752 * @type string $id_submit ID attribute value for the submit button. Default 'wp-submit'. 753 * @type bool $remember Whether to display the "rememberme" checkbox in the form. 754 * @type string $value_username Default value for the username field. Default empty. 755 * @type bool $value_remember Whether the "Remember Me" checkbox should be checked by default. 756 * Default false (unchecked). 757 * @type bool $required_username Whether the username field has the 'required' attribute. 758 * Default false. 759 * @type bool $required_password Whether the password field has the 'required' attribute. 760 * Default false. 761 * 762 * } 763 * @return string|void Login form HTML if 'echo' is false, nothing otherwise. 764 * @phpstan-return ( $args is array{ echo: false|0|''|'0', ... } ? string : void ) 765 */ 766 function wp_login_form( $args = array() ) { 767 $defaults = array( 768 'echo' => true, 769 // Default 'redirect' value takes the user back to the request URI. 770 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 771 'form_id' => 'loginform', 772 'label_username' => __( 'Username or Email Address' ), 773 'label_password' => __( 'Password' ), 774 'label_remember' => __( 'Remember Me' ), 775 'label_log_in' => __( 'Log In' ), 776 'id_username' => 'user_login', 777 'id_password' => 'user_pass', 778 'id_remember' => 'rememberme', 779 'id_submit' => 'wp-submit', 780 'remember' => true, 781 'value_username' => '', 782 // Set 'value_remember' to true to default the "Remember me" checkbox to checked. 783 'value_remember' => false, 784 // Set 'required_username' to true to add the required attribute to username field. 785 'required_username' => false, 786 // Set 'required_password' to true to add the required attribute to password field. 787 'required_password' => false, 788 ); 789 790 /** 791 * Filters the default login form output arguments. 792 * 793 * @since 3.0.0 794 * 795 * @see wp_login_form() 796 * 797 * @param array $defaults An array of default login form arguments. 798 */ 799 $args = wp_parse_args( $args, apply_filters( 'login_form_defaults', $defaults ) ); 800 801 /** 802 * Filters content to display at the top of the login form. 803 * 804 * The filter evaluates just following the opening form tag element. 805 * 806 * @since 3.0.0 807 * 808 * @param string $content Content to display. Default empty. 809 * @param array $args Array of login form arguments. 810 */ 811 $login_form_top = apply_filters( 'login_form_top', '', $args ); 812 813 /** 814 * Filters content to display in the middle of the login form. 815 * 816 * The filter evaluates just following the location where the 'login-password' 817 * field is displayed. 818 * 819 * @since 3.0.0 820 * 821 * @param string $content Content to display. Default empty. 822 * @param array $args Array of login form arguments. 823 */ 824 $login_form_middle = apply_filters( 'login_form_middle', '', $args ); 825 826 /** 827 * Filters content to display at the bottom of the login form. 828 * 829 * The filter evaluates just preceding the closing form tag element. 830 * 831 * @since 3.0.0 832 * 833 * @param string $content Content to display. Default empty. 834 * @param array $args Array of login form arguments. 835 */ 836 $login_form_bottom = apply_filters( 'login_form_bottom', '', $args ); 837 838 $direction_style = is_rtl() ? ' style="direction: ltr;"' : ''; 839 840 $form = 841 sprintf( 842 '<form name="%1$s" id="%1$s" action="%2$s" method="post">', 843 esc_attr( $args['form_id'] ), 844 esc_url( site_url( 'wp-login.php', 'login_post' ) ) 845 ) . 846 $login_form_top . 847 sprintf( 848 '<p class="login-username"> 849 <label for="%1$s">%2$s</label> 850 <input type="text" name="log" id="%1$s" autocomplete="username" class="input" value="%3$s" size="20"%4$s%5$s /> 851 </p>', 852 esc_attr( $args['id_username'] ), 853 esc_html( $args['label_username'] ), 854 esc_attr( $args['value_username'] ), 855 ( $args['required_username'] ? ' required="required"' : '' ), 856 $direction_style 857 ) . 858 sprintf( 859 '<p class="login-password"> 860 <label for="%1$s">%2$s</label> 861 <input type="password" name="pwd" id="%1$s" autocomplete="current-password" spellcheck="false" class="input" value="" size="20"%3$s%4$s /> 862 </p>', 863 esc_attr( $args['id_password'] ), 864 esc_html( $args['label_password'] ), 865 ( $args['required_password'] ? ' required="required"' : '' ), 866 $direction_style 867 ) . 868 $login_form_middle . 869 ( $args['remember'] ? 870 sprintf( 871 '<p class="login-remember"><label><input name="rememberme" type="checkbox" id="%1$s" value="forever"%2$s /> %3$s</label></p>', 872 esc_attr( $args['id_remember'] ), 873 ( $args['value_remember'] ? ' checked="checked"' : '' ), 874 esc_html( $args['label_remember'] ) 875 ) : '' 876 ) . 877 sprintf( 878 '<p class="login-submit"> 879 <input type="submit" name="wp-submit" id="%1$s" class="button button-primary" value="%2$s" /> 880 <input type="hidden" name="redirect_to" value="%3$s" /> 881 </p>', 882 esc_attr( $args['id_submit'] ), 883 esc_attr( $args['label_log_in'] ), 884 esc_url( $args['redirect'] ) 885 ) . 886 $login_form_bottom . 887 '</form>'; 888 889 if ( $args['echo'] ) { 890 echo $form; 891 } else { 892 return $form; 893 } 894 } 895 896 /** 897 * Returns the URL that allows the user to reset the lost password. 898 * 899 * @since 2.8.0 900 * 901 * @param string $redirect Path to redirect to on login. 902 * @return string Lost password URL. 903 */ 904 function wp_lostpassword_url( $redirect = '' ) { 905 $args = array( 906 'action' => 'lostpassword', 907 ); 908 909 if ( ! empty( $redirect ) ) { 910 $args['redirect_to'] = urlencode( $redirect ); 911 } 912 913 if ( is_multisite() ) { 914 $blog_details = get_site(); 915 $wp_login_path = $blog_details->path . 'wp-login.php'; 916 } else { 917 $wp_login_path = 'wp-login.php'; 918 } 919 920 $lostpassword_url = add_query_arg( $args, network_site_url( $wp_login_path, 'login' ) ); 921 922 /** 923 * Filters the Lost Password URL. 924 * 925 * @since 2.8.0 926 * 927 * @param string $lostpassword_url The lost password page URL. 928 * @param string $redirect The path to redirect to on login. 929 */ 930 return apply_filters( 'lostpassword_url', $lostpassword_url, $redirect ); 931 } 932 933 /** 934 * Displays the Registration or Admin link. 935 * 936 * Display a link which allows the user to navigate to the registration page if 937 * not logged in and registration is enabled or to the dashboard if logged in. 938 * 939 * @since 1.5.0 940 * 941 * @param string $before Text to output before the link. Default `<li>`. 942 * @param string $after Text to output after the link. Default `</li>`. 943 * @param bool $display Default to echo and not return the link. 944 * @return string|void The registration or admin link when `$display` is false, or an empty 945 * string when registration is disabled or the logged-in user cannot 946 * access the dashboard. Nothing otherwise. 947 * @phpstan-return ( $display is true ? void : string ) 948 */ 949 function wp_register( $before = '<li>', $after = '</li>', $display = true ) { 950 if ( ! is_user_logged_in() ) { 951 if ( get_option( 'users_can_register' ) ) { 952 $link = $before . '<a href="' . esc_url( wp_registration_url() ) . '">' . __( 'Register' ) . '</a>' . $after; 953 } else { 954 $link = ''; 955 } 956 } elseif ( current_user_can( 'read' ) ) { 957 $link = $before . '<a href="' . admin_url() . '">' . __( 'Site Admin' ) . '</a>' . $after; 958 } else { 959 $link = ''; 960 } 961 962 /** 963 * Filters the HTML link to the Registration or Admin page. 964 * 965 * Users are sent to the admin page if logged-in, or the registration page 966 * if enabled and logged-out. 967 * 968 * @since 1.5.0 969 * 970 * @param string $link The HTML code for the link to the Registration or Admin page. 971 */ 972 $link = apply_filters( 'register', $link ); 973 974 if ( $display ) { 975 echo $link; 976 } else { 977 return $link; 978 } 979 } 980 981 /** 982 * Theme container function for the 'wp_meta' action. 983 * 984 * The {@see 'wp_meta'} action can have several purposes, depending on how you use it, 985 * but one purpose might have been to allow for theme switching. 986 * 987 * @since 1.5.0 988 * 989 * @link https://core.trac.wordpress.org/ticket/1458 Explanation of 'wp_meta' action. 990 */ 991 function wp_meta() { 992 /** 993 * Fires before displaying echoed content in the sidebar. 994 * 995 * @since 1.5.0 996 */ 997 do_action( 'wp_meta' ); 998 } 999 1000 /** 1001 * Displays information about the current site. 1002 * 1003 * @since 0.71 1004 * 1005 * @see get_bloginfo() For possible `$show` values 1006 * 1007 * @param string $show Optional. Site information to display. Default empty. 1008 */ 1009 function bloginfo( $show = '' ) { 1010 echo get_bloginfo( $show, 'display' ); 1011 } 1012 1013 /** 1014 * Retrieves information about the current site. 1015 * 1016 * Possible values for `$show` include: 1017 * 1018 * - 'name' - Site title (set in Settings > General) 1019 * - 'description' - Site tagline (set in Settings > General) 1020 * - 'wpurl' - The WordPress address (URL) (set in Settings > General) 1021 * - 'url' - The Site address (URL) (set in Settings > General) 1022 * - 'admin_email' - Admin email (set in Settings > General) 1023 * - 'charset' - The "Encoding for pages and feeds" (set in Settings > Reading) 1024 * - 'version' - The current WordPress version 1025 * - 'html_type' - The Content-Type (default: "text/html"). Themes and plugins 1026 * can override the default value using the {@see 'pre_option_html_type'} filter 1027 * - 'text_direction' - The text direction determined by the site's language. is_rtl() 1028 * should be used instead 1029 * - 'language' - Language code for the current site 1030 * - 'stylesheet_url' - URL to the stylesheet for the active theme. An active child theme 1031 * will take precedence over this value 1032 * - 'stylesheet_directory' - Directory path for the active theme. An active child theme 1033 * will take precedence over this value 1034 * - 'template_url' / 'template_directory' - URL of the active theme's directory. An active 1035 * child theme will NOT take precedence over this value 1036 * - 'pingback_url' - The pingback XML-RPC file URL (xmlrpc.php) 1037 * - 'atom_url' - The Atom feed URL (/feed/atom) 1038 * - 'rdf_url' - The RDF/RSS 1.0 feed URL (/feed/rdf) 1039 * - 'rss_url' - The RSS 0.92 feed URL (/feed/rss) 1040 * - 'rss2_url' - The RSS 2.0 feed URL (/feed) 1041 * - 'comments_atom_url' - The comments Atom feed URL (/comments/feed) 1042 * - 'comments_rss2_url' - The comments RSS 2.0 feed URL (/comments/feed) 1043 * 1044 * Some `$show` values are deprecated and will be removed in future versions. 1045 * These options will trigger the _deprecated_argument() function. 1046 * 1047 * Deprecated arguments include: 1048 * 1049 * - 'siteurl' - Use 'url' instead 1050 * - 'home' - Use 'url' instead 1051 * 1052 * @since 0.71 1053 * 1054 * @global string $wp_version The WordPress version string. 1055 * 1056 * @param string $show Optional. Site info to retrieve. Default empty (site name). 1057 * @param string $filter Optional. How to filter what is retrieved. Default 'raw'. 1058 * @return string Mostly string values, might be empty. 1059 */ 1060 function get_bloginfo( $show = '', $filter = 'raw' ) { 1061 switch ( $show ) { 1062 case 'home': // Deprecated. 1063 case 'siteurl': // Deprecated. 1064 _deprecated_argument( 1065 __FUNCTION__, 1066 '2.2.0', 1067 sprintf( 1068 /* translators: 1: 'siteurl'/'home' argument, 2: bloginfo() function name, 3: 'url' argument. */ 1069 __( 'The %1$s option is deprecated for the family of %2$s functions. Use the %3$s option instead.' ), 1070 '<code>' . $show . '</code>', 1071 '<code>bloginfo()</code>', 1072 '<code>url</code>' 1073 ) 1074 ); 1075 // Intentional fall-through to be handled by the 'url' case. 1076 case 'url': 1077 $output = home_url(); 1078 break; 1079 case 'wpurl': 1080 $output = site_url(); 1081 break; 1082 case 'description': 1083 $output = get_option( 'blogdescription' ); 1084 break; 1085 case 'rdf_url': 1086 $output = get_feed_link( 'rdf' ); 1087 break; 1088 case 'rss_url': 1089 $output = get_feed_link( 'rss' ); 1090 break; 1091 case 'rss2_url': 1092 $output = get_feed_link( 'rss2' ); 1093 break; 1094 case 'atom_url': 1095 $output = get_feed_link( 'atom' ); 1096 break; 1097 case 'comments_atom_url': 1098 $output = get_feed_link( 'comments_atom' ); 1099 break; 1100 case 'comments_rss2_url': 1101 $output = get_feed_link( 'comments_rss2' ); 1102 break; 1103 case 'pingback_url': 1104 $output = site_url( 'xmlrpc.php' ); 1105 break; 1106 case 'stylesheet_url': 1107 $output = get_stylesheet_uri(); 1108 break; 1109 case 'stylesheet_directory': 1110 $output = get_stylesheet_directory_uri(); 1111 break; 1112 case 'template_directory': 1113 case 'template_url': 1114 $output = get_template_directory_uri(); 1115 break; 1116 case 'admin_email': 1117 $output = get_option( 'admin_email' ); 1118 break; 1119 case 'charset': 1120 $output = get_option( 'blog_charset' ); 1121 if ( '' === $output ) { 1122 $output = 'UTF-8'; 1123 } 1124 break; 1125 case 'html_type': 1126 $output = get_option( 'html_type' ); 1127 break; 1128 case 'version': 1129 global $wp_version; 1130 $output = $wp_version; 1131 break; 1132 case 'language': 1133 /* 1134 * translators: Translate this to the correct language tag for your locale, 1135 * see https://www.w3.org/International/articles/language-tags/ for reference. 1136 * Do not translate into your own language. 1137 */ 1138 $output = __( 'html_lang_attribute' ); 1139 if ( 'html_lang_attribute' === $output || preg_match( '/[^a-zA-Z0-9-]/', $output ) ) { 1140 $output = determine_locale(); 1141 $output = str_replace( '_', '-', $output ); 1142 } 1143 break; 1144 case 'text_direction': 1145 _deprecated_argument( 1146 __FUNCTION__, 1147 '2.2.0', 1148 sprintf( 1149 /* translators: 1: 'text_direction' argument, 2: bloginfo() function name, 3: is_rtl() function name. */ 1150 __( 'The %1$s option is deprecated for the family of %2$s functions. Use the %3$s function instead.' ), 1151 '<code>' . $show . '</code>', 1152 '<code>bloginfo()</code>', 1153 '<code>is_rtl()</code>' 1154 ) 1155 ); 1156 if ( function_exists( 'is_rtl' ) ) { 1157 $output = is_rtl() ? 'rtl' : 'ltr'; 1158 } else { 1159 $output = 'ltr'; 1160 } 1161 break; 1162 case 'name': 1163 default: 1164 $output = get_option( 'blogname' ); 1165 break; 1166 } 1167 1168 if ( 'display' === $filter ) { 1169 if ( 1170 str_contains( $show, 'url' ) 1171 || str_contains( $show, 'directory' ) 1172 || str_contains( $show, 'home' ) 1173 ) { 1174 /** 1175 * Filters the URL returned by get_bloginfo(). 1176 * 1177 * @since 2.0.5 1178 * 1179 * @param string $output The URL returned by bloginfo(). 1180 * @param string $show Type of information requested. 1181 */ 1182 $output = apply_filters( 'bloginfo_url', $output, $show ); 1183 } else { 1184 /** 1185 * Filters the site information returned by get_bloginfo(). 1186 * 1187 * @since 0.71 1188 * 1189 * @param mixed $output The requested non-URL site information. 1190 * @param string $show Type of information requested. 1191 */ 1192 $output = apply_filters( 'bloginfo', $output, $show ); 1193 } 1194 } 1195 1196 return $output; 1197 } 1198 1199 /** 1200 * Returns the Site Icon URL. 1201 * 1202 * @since 4.3.0 1203 * 1204 * @param int $size Optional. Size of the site icon. Default 512 (pixels). 1205 * @param string $url Optional. Fallback url if no site icon is found. Default empty. 1206 * @param int $blog_id Optional. ID of the blog to get the site icon for. Default current blog. 1207 * @return string Site Icon URL. 1208 */ 1209 function get_site_icon_url( $size = 512, $url = '', $blog_id = 0 ) { 1210 $switched_blog = false; 1211 1212 if ( is_multisite() && ! empty( $blog_id ) && get_current_blog_id() !== (int) $blog_id ) { 1213 switch_to_blog( $blog_id ); 1214 $switched_blog = true; 1215 } 1216 1217 $site_icon_id = (int) get_option( 'site_icon' ); 1218 1219 if ( $site_icon_id ) { 1220 if ( $size >= 512 ) { 1221 $size_data = 'full'; 1222 } else { 1223 $size_data = array( $size, $size ); 1224 } 1225 $attachment_url = wp_get_attachment_image_url( $site_icon_id, $size_data ); 1226 if ( $attachment_url ) { 1227 $url = is_ssl() ? set_url_scheme( $attachment_url, 'https' ) : $attachment_url; 1228 } 1229 } 1230 1231 if ( $switched_blog ) { 1232 restore_current_blog(); 1233 } 1234 1235 /** 1236 * Filters the site icon URL. 1237 * 1238 * @since 4.4.0 1239 * 1240 * @param string $url Site icon URL. 1241 * @param int $size Size of the site icon. 1242 * @param int $blog_id ID of the blog to get the site icon for. 1243 */ 1244 return apply_filters( 'get_site_icon_url', $url, $size, $blog_id ); 1245 } 1246 1247 /** 1248 * Displays the Site Icon URL. 1249 * 1250 * @since 4.3.0 1251 * 1252 * @param int $size Optional. Size of the site icon. Default 512 (pixels). 1253 * @param string $url Optional. Fallback url if no site icon is found. Default empty. 1254 * @param int $blog_id Optional. ID of the blog to get the site icon for. Default current blog. 1255 */ 1256 function site_icon_url( $size = 512, $url = '', $blog_id = 0 ) { 1257 echo esc_url( get_site_icon_url( $size, $url, $blog_id ) ); 1258 } 1259 1260 /** 1261 * Determines whether the site has a Site Icon. 1262 * 1263 * @since 4.3.0 1264 * 1265 * @param int $blog_id Optional. ID of the blog in question. Default current blog. 1266 * @return bool Whether the site has a site icon or not. 1267 */ 1268 function has_site_icon( $blog_id = 0 ) { 1269 return (bool) get_site_icon_url( 512, '', $blog_id ); 1270 } 1271 1272 /** 1273 * Determines whether the site has a custom logo. 1274 * 1275 * @since 4.5.0 1276 * 1277 * @param int $blog_id Optional. ID of the blog in question. Default is the ID of the current blog. 1278 * @return bool Whether the site has a custom logo or not. 1279 */ 1280 function has_custom_logo( $blog_id = 0 ) { 1281 $switched_blog = false; 1282 1283 if ( is_multisite() && ! empty( $blog_id ) && get_current_blog_id() !== (int) $blog_id ) { 1284 switch_to_blog( $blog_id ); 1285 $switched_blog = true; 1286 } 1287 1288 $custom_logo_id = get_theme_mod( 'custom_logo' ); 1289 $is_image = ( $custom_logo_id ) ? wp_attachment_is_image( $custom_logo_id ) : false; 1290 1291 if ( $switched_blog ) { 1292 restore_current_blog(); 1293 } 1294 1295 return $is_image; 1296 } 1297 1298 /** 1299 * Returns a custom logo, linked to home unless the theme supports removing the link on the home page. 1300 * 1301 * @since 4.5.0 1302 * @since 5.5.0 Added option to remove the link on the home page with `unlink-homepage-logo` theme support 1303 * for the `custom-logo` theme feature. 1304 * @since 5.5.1 Disabled lazy-loading by default. 1305 * 1306 * @param int $blog_id Optional. ID of the blog in question. Default is the ID of the current blog. 1307 * @return string Custom logo markup. 1308 */ 1309 function get_custom_logo( $blog_id = 0 ) { 1310 $html = ''; 1311 $switched_blog = false; 1312 1313 if ( is_multisite() && ! empty( $blog_id ) && get_current_blog_id() !== (int) $blog_id ) { 1314 switch_to_blog( $blog_id ); 1315 $switched_blog = true; 1316 } 1317 1318 // We have a logo. Logo is go. 1319 if ( has_custom_logo() ) { 1320 $custom_logo_id = get_theme_mod( 'custom_logo' ); 1321 $custom_logo_attr = array( 1322 'class' => 'custom-logo', 1323 'loading' => false, 1324 ); 1325 1326 $unlink_homepage_logo = (bool) get_theme_support( 'custom-logo', 'unlink-homepage-logo' ); 1327 1328 if ( $unlink_homepage_logo && is_front_page() && ! is_paged() ) { 1329 /* 1330 * If on the home page, set the logo alt attribute to an empty string, 1331 * as the image is decorative and doesn't need its purpose to be described. 1332 */ 1333 $custom_logo_attr['alt'] = ''; 1334 } else { 1335 /* 1336 * If the logo alt attribute is empty, get the site title and explicitly pass it 1337 * to the attributes used by wp_get_attachment_image(). 1338 */ 1339 $image_alt = get_post_meta( $custom_logo_id, '_wp_attachment_image_alt', true ); 1340 if ( empty( $image_alt ) ) { 1341 $custom_logo_attr['alt'] = get_bloginfo( 'name', 'display' ); 1342 } 1343 } 1344 1345 /** 1346 * Filters the list of custom logo image attributes. 1347 * 1348 * @since 5.5.0 1349 * 1350 * @param array $custom_logo_attr Custom logo image attributes. 1351 * @param int $custom_logo_id Custom logo attachment ID. 1352 * @param int $blog_id ID of the blog to get the custom logo for. 1353 */ 1354 $custom_logo_attr = apply_filters( 'get_custom_logo_image_attributes', $custom_logo_attr, $custom_logo_id, $blog_id ); 1355 1356 /* 1357 * If the alt attribute is not empty, there's no need to explicitly pass it 1358 * because wp_get_attachment_image() already adds the alt attribute. 1359 */ 1360 $image = wp_get_attachment_image( $custom_logo_id, 'full', false, $custom_logo_attr ); 1361 1362 // Check that we have a proper HTML img element. 1363 if ( $image ) { 1364 1365 if ( $unlink_homepage_logo && is_front_page() && ! is_paged() ) { 1366 // If on the home page, don't link the logo to home. 1367 $html = sprintf( 1368 '<span class="custom-logo-link">%1$s</span>', 1369 $image 1370 ); 1371 } else { 1372 $aria_current = ! is_paged() && ( is_front_page() || is_home() && ( (int) get_option( 'page_for_posts' ) !== get_queried_object_id() ) ) ? ' aria-current="page"' : ''; 1373 1374 $html = sprintf( 1375 '<a href="%1$s" class="custom-logo-link" rel="home"%2$s>%3$s</a>', 1376 esc_url( home_url( '/' ) ), 1377 $aria_current, 1378 $image 1379 ); 1380 } 1381 } 1382 } elseif ( is_customize_preview() ) { 1383 // If no logo is set but we're in the Customizer, leave a placeholder (needed for the live preview). 1384 $html = sprintf( 1385 '<a href="%1$s" class="custom-logo-link" style="display:none;"><img class="custom-logo" alt="" /></a>', 1386 esc_url( home_url( '/' ) ) 1387 ); 1388 } 1389 1390 if ( $switched_blog ) { 1391 restore_current_blog(); 1392 } 1393 1394 /** 1395 * Filters the custom logo output. 1396 * 1397 * @since 4.5.0 1398 * @since 4.6.0 Added the `$blog_id` parameter. 1399 * 1400 * @param string $html Custom logo HTML output. 1401 * @param int $blog_id ID of the blog to get the custom logo for. 1402 */ 1403 return apply_filters( 'get_custom_logo', $html, $blog_id ); 1404 } 1405 1406 /** 1407 * Displays a custom logo, linked to home unless the theme supports removing the link on the home page. 1408 * 1409 * @since 4.5.0 1410 * 1411 * @param int $blog_id Optional. ID of the blog in question. Default is the ID of the current blog. 1412 */ 1413 function the_custom_logo( $blog_id = 0 ) { 1414 echo get_custom_logo( $blog_id ); 1415 } 1416 1417 /** 1418 * Returns document title for the current page. 1419 * 1420 * @since 4.4.0 1421 * 1422 * @global int $page Page number of a single post. 1423 * @global int $paged Page number of a list of posts. 1424 * 1425 * @return string Tag with the document title. 1426 */ 1427 function wp_get_document_title() { 1428 1429 /** 1430 * Filters the document title before it is generated. 1431 * 1432 * Passing a non-empty value will short-circuit wp_get_document_title(), 1433 * returning that value instead. 1434 * 1435 * @since 4.4.0 1436 * 1437 * @param string $title The document title. Default empty string. 1438 */ 1439 $title = apply_filters( 'pre_get_document_title', '' ); 1440 if ( ! empty( $title ) ) { 1441 return $title; 1442 } 1443 1444 global $page, $paged; 1445 1446 $title = array( 1447 'title' => '', 1448 ); 1449 1450 // If it's a 404 page, use a "Page not found" title. 1451 if ( is_404() ) { 1452 $title['title'] = __( 'Page not found' ); 1453 1454 // If it's a search, use a dynamic search results title. 1455 } elseif ( is_search() ) { 1456 /* translators: %s: Search query. */ 1457 $title['title'] = sprintf( __( 'Search Results for “%s”' ), get_search_query() ); 1458 1459 // If on the front page, use the site title. 1460 } elseif ( is_front_page() ) { 1461 $title['title'] = get_bloginfo( 'name', 'display' ); 1462 1463 // If on a post type archive, use the post type archive title. 1464 } elseif ( is_post_type_archive() ) { 1465 $title['title'] = post_type_archive_title( '', false ); 1466 1467 // If on a taxonomy archive, use the term title. 1468 } elseif ( is_tax() ) { 1469 $title['title'] = single_term_title( '', false ); 1470 1471 /* 1472 * If we're on the blog page that is not the homepage 1473 * or a single post of any post type, use the post title. 1474 */ 1475 } elseif ( is_home() || is_singular() ) { 1476 $title['title'] = single_post_title( '', false ); 1477 1478 // If on a category or tag archive, use the term title. 1479 } elseif ( is_category() || is_tag() ) { 1480 $title['title'] = single_term_title( '', false ); 1481 1482 // If on an author archive, use the author's display name. 1483 } elseif ( is_author() && get_queried_object() ) { 1484 $author = get_queried_object(); 1485 $title['title'] = $author->display_name; 1486 1487 // If it's a date archive, use the date as the title. 1488 } elseif ( is_year() ) { 1489 $title['title'] = get_the_date( _x( 'Y', 'yearly archives date format' ) ); 1490 1491 } elseif ( is_month() ) { 1492 $title['title'] = get_the_date( _x( 'F Y', 'monthly archives date format' ) ); 1493 1494 } elseif ( is_day() ) { 1495 $title['title'] = get_the_date(); 1496 } 1497 1498 // Add a page number if necessary. 1499 if ( ( $paged >= 2 || $page >= 2 ) && ! is_404() ) { 1500 /* translators: %s: Page number. */ 1501 $title['page'] = sprintf( __( 'Page %s' ), max( $paged, $page ) ); 1502 } 1503 1504 // Append the description or site title to give context. 1505 if ( is_front_page() ) { 1506 $title['tagline'] = get_bloginfo( 'description', 'display' ); 1507 } else { 1508 $title['site'] = get_bloginfo( 'name', 'display' ); 1509 } 1510 1511 /** 1512 * Filters the separator for the document title. 1513 * 1514 * @since 4.4.0 1515 * 1516 * @param string $sep Document title separator. Default '-'. 1517 */ 1518 $sep = apply_filters( 'document_title_separator', '-' ); 1519 1520 /** 1521 * Filters the parts of the document title. 1522 * 1523 * @since 4.4.0 1524 * 1525 * @param array $title { 1526 * The document title parts. 1527 * 1528 * @type string $title Title of the viewed page. 1529 * @type string $page Optional. Page number if paginated. 1530 * @type string $tagline Optional. Site description when on home page. 1531 * @type string $site Optional. Site title when not on home page. 1532 * } 1533 */ 1534 $title = apply_filters( 'document_title_parts', $title ); 1535 1536 $title = implode( " $sep ", array_filter( $title ) ); 1537 1538 /** 1539 * Filters the document title. 1540 * 1541 * @since 5.8.0 1542 * 1543 * @param string $title Document title. 1544 */ 1545 $title = apply_filters( 'document_title', $title ); 1546 1547 return $title; 1548 } 1549 1550 /** 1551 * Displays title tag with content. 1552 * 1553 * @since 4.1.0 1554 * @since 4.4.0 Improved title output replaced `wp_title()`. 1555 * @access private 1556 */ 1557 function _wp_render_title_tag() { 1558 if ( ! current_theme_supports( 'title-tag' ) ) { 1559 return; 1560 } 1561 1562 echo '<title>' . wp_get_document_title() . '</title>' . "\n"; 1563 } 1564 1565 /** 1566 * Displays or retrieves page title for all areas of blog. 1567 * 1568 * By default, the page title will display the separator before the page title, 1569 * so that the blog title will be before the page title. This is not good for 1570 * title display, since the blog title shows up on most tabs and not what is 1571 * important, which is the page that the user is looking at. 1572 * 1573 * There are also SEO benefits to having the blog title after or to the 'right' 1574 * of the page title. However, it is mostly common sense to have the blog title 1575 * to the right with most browsers supporting tabs. You can achieve this by 1576 * using the seplocation parameter and setting the value to 'right'. This change 1577 * was introduced around 2.5.0, in case backward compatibility of themes is 1578 * important. 1579 * 1580 * @since 1.0.0 1581 * 1582 * @global WP_Locale $wp_locale WordPress date and time locale object. 1583 * 1584 * @param string $sep Optional. How to separate the various items within the page title. 1585 * Default '»'. 1586 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1587 * @param string $seplocation Optional. Location of the separator (either 'left' or 'right'). 1588 * @return string|void String when `$display` is false, nothing otherwise. 1589 * @phpstan-return ( $display is true ? void : string ) 1590 */ 1591 function wp_title( $sep = '»', $display = true, $seplocation = '' ) { 1592 global $wp_locale; 1593 1594 $m = get_query_var( 'm' ); 1595 $year = get_query_var( 'year' ); 1596 $monthnum = get_query_var( 'monthnum' ); 1597 $day = get_query_var( 'day' ); 1598 $search = get_query_var( 's' ); 1599 $title = ''; 1600 1601 $t_sep = '%WP_TITLE_SEP%'; // Temporary separator, for accurate flipping, if necessary. 1602 1603 // If there is a post. 1604 if ( is_single() || ( is_home() && ! is_front_page() ) || ( is_page() && ! is_front_page() ) ) { 1605 $title = single_post_title( '', false ); 1606 } 1607 1608 // If there's a post type archive. 1609 if ( is_post_type_archive() ) { 1610 $post_type = get_query_var( 'post_type' ); 1611 if ( is_array( $post_type ) ) { 1612 $post_type = reset( $post_type ); 1613 } 1614 $post_type_object = get_post_type_object( $post_type ); 1615 if ( ! $post_type_object->has_archive ) { 1616 $title = post_type_archive_title( '', false ); 1617 } 1618 } 1619 1620 // If there's a category or tag. 1621 if ( is_category() || is_tag() ) { 1622 $title = single_term_title( '', false ); 1623 } 1624 1625 // If there's a taxonomy. 1626 if ( is_tax() ) { 1627 $term = get_queried_object(); 1628 if ( $term ) { 1629 $tax = get_taxonomy( $term->taxonomy ); 1630 $title = single_term_title( $tax->labels->name . $t_sep, false ); 1631 } 1632 } 1633 1634 // If there's an author. 1635 if ( is_author() && ! is_post_type_archive() ) { 1636 $author = get_queried_object(); 1637 if ( $author ) { 1638 $title = $author->display_name; 1639 } 1640 } 1641 1642 // Post type archives with has_archive should override terms. 1643 if ( is_post_type_archive() && $post_type_object->has_archive ) { 1644 $title = post_type_archive_title( '', false ); 1645 } 1646 1647 // If there's a month. 1648 if ( is_archive() && ! empty( $m ) ) { 1649 $my_year = substr( $m, 0, 4 ); 1650 $my_month = substr( $m, 4, 2 ); 1651 $my_day = (int) substr( $m, 6, 2 ); 1652 $title = $my_year . 1653 ( $my_month ? $t_sep . $wp_locale->get_month( $my_month ) : '' ) . 1654 ( $my_day ? $t_sep . $my_day : '' ); 1655 } 1656 1657 // If there's a year. 1658 if ( is_archive() && ! empty( $year ) ) { 1659 $title = $year; 1660 if ( ! empty( $monthnum ) ) { 1661 $title .= $t_sep . $wp_locale->get_month( $monthnum ); 1662 } 1663 if ( ! empty( $day ) ) { 1664 $title .= $t_sep . zeroise( $day, 2 ); 1665 } 1666 } 1667 1668 // If it's a search. 1669 if ( is_search() ) { 1670 /* translators: 1: Separator, 2: Search query. */ 1671 $title = sprintf( __( 'Search Results %1$s %2$s' ), $t_sep, strip_tags( $search ) ); 1672 } 1673 1674 // If it's a 404 page. 1675 if ( is_404() ) { 1676 $title = __( 'Page not found' ); 1677 } 1678 1679 if ( ! is_string( $title ) ) { 1680 $title = ''; 1681 } 1682 1683 $prefix = ''; 1684 $title_array = array(); 1685 if ( '' !== $title ) { 1686 $prefix = " $sep "; 1687 $title_array = explode( $t_sep, $title ); 1688 } 1689 1690 /** 1691 * Filters the parts of the page title. 1692 * 1693 * @since 4.0.0 1694 * 1695 * @param string[] $title_array Array of parts of the page title. 1696 */ 1697 $title_array = apply_filters( 'wp_title_parts', $title_array ); 1698 1699 // Determines position of the separator and direction of the breadcrumb. 1700 if ( 'right' === $seplocation ) { // Separator on right, so reverse the order. 1701 $title_array = array_reverse( $title_array ); 1702 $title = implode( " $sep ", $title_array ) . $prefix; 1703 } else { 1704 $title = $prefix . implode( " $sep ", $title_array ); 1705 } 1706 1707 /** 1708 * Filters the text of the page title. 1709 * 1710 * @since 2.0.0 1711 * 1712 * @param string $title Page title. 1713 * @param string $sep Title separator. 1714 * @param string $seplocation Location of the separator (either 'left' or 'right'). 1715 */ 1716 $title = apply_filters( 'wp_title', $title, $sep, $seplocation ); 1717 1718 // Send it out. 1719 if ( ! $display ) { 1720 return $title; 1721 } 1722 1723 echo $title; 1724 } 1725 1726 /** 1727 * Displays or retrieves page title for post. 1728 * 1729 * This is optimized for single.php template file for displaying the post title. 1730 * 1731 * It does not support placing the separator after the title, but by leaving the 1732 * prefix parameter empty, you can set the title separator manually. The prefix 1733 * does not automatically place a space between the prefix, so if there should 1734 * be a space, the parameter value will need to have it at the end. 1735 * 1736 * @since 0.71 1737 * 1738 * @param string $prefix Optional. What to display before the title. 1739 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1740 * @return string|null|void Title when retrieving, null on failure. 1741 * Nothing when displaying. 1742 * @phpstan-return ( $display is true ? void : string|null ) 1743 */ 1744 function single_post_title( $prefix = '', $display = true ) { 1745 $_post = get_queried_object(); 1746 1747 if ( ! isset( $_post->post_title ) ) { 1748 return null; 1749 } 1750 1751 /** 1752 * Filters the page title for a single post. 1753 * 1754 * @since 0.71 1755 * 1756 * @param string $_post_title The single post page title. 1757 * @param WP_Post $_post The current post. 1758 */ 1759 $title = apply_filters( 'single_post_title', $_post->post_title, $_post ); 1760 1761 if ( ! $display ) { 1762 return $prefix . $title; 1763 } 1764 1765 echo $prefix . $title; 1766 } 1767 1768 /** 1769 * Displays or retrieves title for a post type archive. 1770 * 1771 * This is optimized for archive.php and archive-{$post_type}.php template files 1772 * for displaying the title of the post type. 1773 * 1774 * @since 3.1.0 1775 * 1776 * @param string $prefix Optional. What to display before the title. 1777 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1778 * @return string|null|void Title when retrieving, null on failure. 1779 * Nothing when displaying. 1780 * @phpstan-return ( $display is true ? void : string|null ) 1781 */ 1782 function post_type_archive_title( $prefix = '', $display = true ) { 1783 if ( ! is_post_type_archive() ) { 1784 return null; 1785 } 1786 1787 $post_type = get_query_var( 'post_type' ); 1788 if ( is_array( $post_type ) ) { 1789 $post_type = reset( $post_type ); 1790 } 1791 1792 $post_type_obj = get_post_type_object( $post_type ); 1793 1794 /** 1795 * Filters the post type archive title. 1796 * 1797 * @since 3.1.0 1798 * 1799 * @param string $post_type_name Post type 'name' label. 1800 * @param string $post_type Post type. 1801 */ 1802 $title = apply_filters( 'post_type_archive_title', $post_type_obj->labels->name, $post_type ); 1803 1804 if ( ! $display ) { 1805 return $prefix . $title; 1806 } 1807 1808 echo $prefix . $title; 1809 } 1810 1811 /** 1812 * Displays or retrieves page title for category archive. 1813 * 1814 * Useful for category template files for displaying the category page title. 1815 * The prefix does not automatically place a space between the prefix, so if 1816 * there should be a space, the parameter value will need to have it at the end. 1817 * 1818 * @since 0.71 1819 * 1820 * @param string $prefix Optional. What to display before the title. 1821 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1822 * @return string|null|void Title when retrieving, null on failure. 1823 * Nothing when displaying. 1824 * @phpstan-return ( $display is true ? void : string|null ) 1825 */ 1826 function single_cat_title( $prefix = '', $display = true ) { 1827 if ( ! $display ) { 1828 return single_term_title( $prefix, false ); 1829 } 1830 1831 single_term_title( $prefix, true ); 1832 } 1833 1834 /** 1835 * Displays or retrieves page title for tag post archive. 1836 * 1837 * Useful for tag template files for displaying the tag page title. The prefix 1838 * does not automatically place a space between the prefix, so if there should 1839 * be a space, the parameter value will need to have it at the end. 1840 * 1841 * @since 2.3.0 1842 * 1843 * @param string $prefix Optional. What to display before the title. 1844 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1845 * @return string|null|void Title when retrieving, null on failure. 1846 * Nothing when displaying. 1847 * @phpstan-return ( $display is true ? void : string|null ) 1848 */ 1849 function single_tag_title( $prefix = '', $display = true ) { 1850 if ( ! $display ) { 1851 return single_term_title( $prefix, false ); 1852 } 1853 1854 single_term_title( $prefix, true ); 1855 } 1856 1857 /** 1858 * Displays or retrieves page title for taxonomy term archive. 1859 * 1860 * Useful for taxonomy term template files for displaying the taxonomy term page title. 1861 * The prefix does not automatically place a space between the prefix, so if there should 1862 * be a space, the parameter value will need to have it at the end. 1863 * 1864 * @since 3.1.0 1865 * 1866 * @param string $prefix Optional. What to display before the title. 1867 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1868 * @return string|null|void Title when retrieving, null on failure. 1869 * Nothing when displaying. 1870 * @phpstan-return ( $display is true ? void : string|null ) 1871 */ 1872 function single_term_title( $prefix = '', $display = true ) { 1873 $term = get_queried_object(); 1874 1875 if ( ! $term ) { 1876 return null; 1877 } 1878 1879 if ( is_category() ) { 1880 /** 1881 * Filters the category archive page title. 1882 * 1883 * @since 2.0.10 1884 * 1885 * @param string $term_name Category name for archive being displayed. 1886 */ 1887 $term_name = apply_filters( 'single_cat_title', $term->name ); 1888 } elseif ( is_tag() ) { 1889 /** 1890 * Filters the tag archive page title. 1891 * 1892 * @since 2.3.0 1893 * 1894 * @param string $term_name Tag name for archive being displayed. 1895 */ 1896 $term_name = apply_filters( 'single_tag_title', $term->name ); 1897 } elseif ( is_tax() ) { 1898 /** 1899 * Filters the custom taxonomy archive page title. 1900 * 1901 * @since 3.1.0 1902 * 1903 * @param string $term_name Term name for archive being displayed. 1904 */ 1905 $term_name = apply_filters( 'single_term_title', $term->name ); 1906 } else { 1907 return null; 1908 } 1909 1910 if ( empty( $term_name ) ) { 1911 return null; 1912 } 1913 1914 if ( ! $display ) { 1915 return $prefix . $term_name; 1916 } 1917 1918 echo $prefix . $term_name; 1919 } 1920 1921 /** 1922 * Displays or retrieves page title for post archive based on date. 1923 * 1924 * Useful for when the template only needs to display the month and year, 1925 * if either are available. The prefix does not automatically place a space 1926 * between the prefix, so if there should be a space, the parameter value 1927 * will need to have it at the end. 1928 * 1929 * @since 0.71 1930 * 1931 * @global WP_Locale $wp_locale WordPress date and time locale object. 1932 * 1933 * @param string $prefix Optional. What to display before the title. 1934 * @param bool $display Optional. Whether to display or retrieve title. Default true. 1935 * @return string|false|null False if there's no valid title for the month. Title when retrieving. 1936 * 1937 * @phpstan-return ( $display is true ? false|null : string|false ) 1938 */ 1939 function single_month_title( $prefix = '', $display = true ) { 1940 global $wp_locale; 1941 1942 $m = get_query_var( 'm' ); 1943 $year = get_query_var( 'year' ); 1944 $monthnum = get_query_var( 'monthnum' ); 1945 1946 if ( ! empty( $monthnum ) && ! empty( $year ) ) { 1947 $my_year = $year; 1948 $my_month = $wp_locale->get_month( $monthnum ); 1949 } elseif ( ! empty( $m ) ) { 1950 $my_year = substr( $m, 0, 4 ); 1951 $my_month = $wp_locale->get_month( substr( $m, 4, 2 ) ); 1952 } 1953 1954 if ( empty( $my_month ) ) { 1955 return false; 1956 } 1957 1958 $result = $prefix . $my_month . $prefix . $my_year; 1959 1960 if ( ! $display ) { 1961 return $result; 1962 } 1963 1964 echo $result; 1965 1966 return null; 1967 } 1968 1969 /** 1970 * Displays the archive title based on the queried object. 1971 * 1972 * @since 4.1.0 1973 * 1974 * @see get_the_archive_title() 1975 * 1976 * @param string $before Optional. Content to prepend to the title. Default empty. 1977 * @param string $after Optional. Content to append to the title. Default empty. 1978 */ 1979 function the_archive_title( $before = '', $after = '' ) { 1980 $title = get_the_archive_title(); 1981 1982 if ( ! empty( $title ) ) { 1983 echo $before . $title . $after; 1984 } 1985 } 1986 1987 /** 1988 * Retrieves the archive title based on the queried object. 1989 * 1990 * @since 4.1.0 1991 * @since 5.5.0 The title part is wrapped in a `<span>` element. 1992 * 1993 * @return string Archive title. 1994 */ 1995 function get_the_archive_title() { 1996 $title = __( 'Archives' ); 1997 $prefix = ''; 1998 1999 if ( is_category() ) { 2000 $title = single_cat_title( '', false ); 2001 $prefix = _x( 'Category:', 'category archive title prefix' ); 2002 } elseif ( is_tag() ) { 2003 $title = single_tag_title( '', false ); 2004 $prefix = _x( 'Tag:', 'tag archive title prefix' ); 2005 } elseif ( is_author() ) { 2006 $title = get_the_author(); 2007 $prefix = _x( 'Author:', 'author archive title prefix' ); 2008 } elseif ( is_year() ) { 2009 /* translators: See https://www.php.net/manual/datetime.format.php */ 2010 $title = get_the_date( _x( 'Y', 'yearly archives date format' ) ); 2011 $prefix = _x( 'Year:', 'date archive title prefix' ); 2012 } elseif ( is_month() ) { 2013 /* translators: See https://www.php.net/manual/datetime.format.php */ 2014 $title = get_the_date( _x( 'F Y', 'monthly archives date format' ) ); 2015 $prefix = _x( 'Month:', 'date archive title prefix' ); 2016 } elseif ( is_day() ) { 2017 /* translators: See https://www.php.net/manual/datetime.format.php */ 2018 $title = get_the_date( _x( 'F j, Y', 'daily archives date format' ) ); 2019 $prefix = _x( 'Day:', 'date archive title prefix' ); 2020 } elseif ( is_tax( 'post_format' ) ) { 2021 if ( is_tax( 'post_format', 'post-format-aside' ) ) { 2022 $title = _x( 'Asides', 'post format archive title' ); 2023 } elseif ( is_tax( 'post_format', 'post-format-gallery' ) ) { 2024 $title = _x( 'Galleries', 'post format archive title' ); 2025 } elseif ( is_tax( 'post_format', 'post-format-image' ) ) { 2026 $title = _x( 'Images', 'post format archive title' ); 2027 } elseif ( is_tax( 'post_format', 'post-format-video' ) ) { 2028 $title = _x( 'Videos', 'post format archive title' ); 2029 } elseif ( is_tax( 'post_format', 'post-format-quote' ) ) { 2030 $title = _x( 'Quotes', 'post format archive title' ); 2031 } elseif ( is_tax( 'post_format', 'post-format-link' ) ) { 2032 $title = _x( 'Links', 'post format archive title' ); 2033 } elseif ( is_tax( 'post_format', 'post-format-status' ) ) { 2034 $title = _x( 'Statuses', 'post format archive title' ); 2035 } elseif ( is_tax( 'post_format', 'post-format-audio' ) ) { 2036 $title = _x( 'Audio', 'post format archive title' ); 2037 } elseif ( is_tax( 'post_format', 'post-format-chat' ) ) { 2038 $title = _x( 'Chats', 'post format archive title' ); 2039 } 2040 } elseif ( is_post_type_archive() ) { 2041 $title = post_type_archive_title( '', false ); 2042 $prefix = _x( 'Archives:', 'post type archive title prefix' ); 2043 } elseif ( is_tax() ) { 2044 $queried_object = get_queried_object(); 2045 if ( $queried_object ) { 2046 $tax = get_taxonomy( $queried_object->taxonomy ); 2047 $title = single_term_title( '', false ); 2048 $prefix = sprintf( 2049 /* translators: %s: Taxonomy singular name. */ 2050 _x( '%s:', 'taxonomy term archive title prefix' ), 2051 $tax->labels->singular_name 2052 ); 2053 } 2054 } 2055 2056 $original_title = $title; 2057 2058 /** 2059 * Filters the archive title prefix. 2060 * 2061 * @since 5.5.0 2062 * 2063 * @param string $prefix Archive title prefix. 2064 */ 2065 $prefix = apply_filters( 'get_the_archive_title_prefix', $prefix ); 2066 if ( $prefix ) { 2067 $title = sprintf( 2068 /* translators: 1: Title prefix. 2: Title. */ 2069 _x( '%1$s %2$s', 'archive title' ), 2070 $prefix, 2071 '<span>' . $title . '</span>' 2072 ); 2073 } 2074 2075 /** 2076 * Filters the archive title. 2077 * 2078 * @since 4.1.0 2079 * @since 5.5.0 Added the `$prefix` and `$original_title` parameters. 2080 * 2081 * @param string $title Archive title to be displayed. 2082 * @param string $original_title Archive title without prefix. 2083 * @param string $prefix Archive title prefix. 2084 */ 2085 return apply_filters( 'get_the_archive_title', $title, $original_title, $prefix ); 2086 } 2087 2088 /** 2089 * Displays category, tag, term, or author description. 2090 * 2091 * @since 4.1.0 2092 * 2093 * @see get_the_archive_description() 2094 * 2095 * @param string $before Optional. Content to prepend to the description. Default empty. 2096 * @param string $after Optional. Content to append to the description. Default empty. 2097 */ 2098 function the_archive_description( $before = '', $after = '' ) { 2099 $description = get_the_archive_description(); 2100 if ( $description ) { 2101 echo $before . $description . $after; 2102 } 2103 } 2104 2105 /** 2106 * Retrieves the description for an author, post type, or term archive. 2107 * 2108 * @since 4.1.0 2109 * @since 4.7.0 Added support for author archives. 2110 * @since 4.9.0 Added support for post type archives. 2111 * 2112 * @see term_description() 2113 * 2114 * @return string Archive description. 2115 */ 2116 function get_the_archive_description() { 2117 if ( is_author() ) { 2118 $description = get_the_author_meta( 'description' ); 2119 } elseif ( is_post_type_archive() ) { 2120 $description = get_the_post_type_description(); 2121 } else { 2122 $description = term_description(); 2123 } 2124 2125 /** 2126 * Filters the archive description. 2127 * 2128 * @since 4.1.0 2129 * 2130 * @param string $description Archive description to be displayed. 2131 */ 2132 return apply_filters( 'get_the_archive_description', $description ); 2133 } 2134 2135 /** 2136 * Retrieves the description for a post type archive. 2137 * 2138 * @since 4.9.0 2139 * 2140 * @return string The post type description. 2141 */ 2142 function get_the_post_type_description() { 2143 $post_type = get_query_var( 'post_type' ); 2144 2145 if ( is_array( $post_type ) ) { 2146 $post_type = reset( $post_type ); 2147 } 2148 2149 $post_type_obj = get_post_type_object( $post_type ); 2150 2151 // Check if a description is set. 2152 $description = $post_type_obj->description ?? ''; 2153 2154 /** 2155 * Filters the description for a post type archive. 2156 * 2157 * @since 4.9.0 2158 * 2159 * @param string $description The post type description. 2160 * @param WP_Post_Type $post_type_obj The post type object. 2161 */ 2162 return apply_filters( 'get_the_post_type_description', $description, $post_type_obj ); 2163 } 2164 2165 /** 2166 * Retrieves archive link content based on predefined or custom code. 2167 * 2168 * The format can be one of four styles. The 'link' for head element, 'option' 2169 * for use in the select element, 'html' for use in list (either ol or ul HTML 2170 * elements). Custom content is also supported using the before and after 2171 * parameters. 2172 * 2173 * The 'link' format uses the `<link>` HTML element with the **archives** 2174 * relationship. The before and after parameters are not used. The text 2175 * parameter is used to describe the link. 2176 * 2177 * The 'option' format uses the option HTML element for use in select element. 2178 * The value is the url parameter and the before and after parameters are used 2179 * between the text description. 2180 * 2181 * The 'html' format, which is the default, uses the li HTML element for use in 2182 * the list HTML elements. The before parameter is before the link and the after 2183 * parameter is after the closing link. 2184 * 2185 * The custom format uses the before parameter before the link ('a' HTML 2186 * element) and the after parameter after the closing link tag. If the above 2187 * three values for the format are not used, then custom format is assumed. 2188 * 2189 * @since 1.0.0 2190 * @since 5.2.0 Added the `$selected` parameter. 2191 * 2192 * @param string $url URL to archive. 2193 * @param string $text Archive text description. 2194 * @param string $format Optional. Can be 'link', 'option', 'html', or custom. Default 'html'. 2195 * @param string $before Optional. Content to prepend to the description. Default empty. 2196 * @param string $after Optional. Content to append to the description. Default empty. 2197 * @param bool $selected Optional. Set to true if the current page is the selected archive page. Default false. 2198 * @return string HTML link content for archive. 2199 */ 2200 function get_archives_link( $url, $text, $format = 'html', $before = '', $after = '', $selected = false ) { 2201 $text = wptexturize( $text ); 2202 $url = esc_url( $url ); 2203 $aria_current = $selected ? ' aria-current="page"' : ''; 2204 2205 if ( 'link' === $format ) { 2206 $link_html = "\t<link rel='archives' title='" . esc_attr( $text ) . "' href='$url' />\n"; 2207 } elseif ( 'option' === $format ) { 2208 $selected_attr = $selected ? " selected='selected'" : ''; 2209 $link_html = "\t<option value='$url'$selected_attr>$before $text $after</option>\n"; 2210 } elseif ( 'html' === $format ) { 2211 $link_html = "\t<li>$before<a href='$url'$aria_current>$text</a>$after</li>\n"; 2212 } else { // Custom. 2213 $link_html = "\t$before<a href='$url'$aria_current>$text</a>$after\n"; 2214 } 2215 2216 /** 2217 * Filters the archive link content. 2218 * 2219 * @since 2.6.0 2220 * @since 4.5.0 Added the `$url`, `$text`, `$format`, `$before`, and `$after` parameters. 2221 * @since 5.2.0 Added the `$selected` parameter. 2222 * 2223 * @param string $link_html The archive HTML link content. 2224 * @param string $url URL to archive. 2225 * @param string $text Archive text description. 2226 * @param string $format Link format. Can be 'link', 'option', 'html', or custom. 2227 * @param string $before Content to prepend to the description. 2228 * @param string $after Content to append to the description. 2229 * @param bool $selected True if the current page is the selected archive. 2230 */ 2231 return apply_filters( 'get_archives_link', $link_html, $url, $text, $format, $before, $after, $selected ); 2232 } 2233 2234 /** 2235 * Displays archive links based on type and format. 2236 * 2237 * @since 1.2.0 2238 * @since 4.4.0 The `$post_type` argument was added. 2239 * @since 5.2.0 The `$year`, `$monthnum`, `$day`, and `$w` arguments were added. 2240 * 2241 * @see get_archives_link() 2242 * 2243 * @global wpdb $wpdb WordPress database abstraction object. 2244 * @global WP_Locale $wp_locale WordPress date and time locale object. 2245 * 2246 * @param string|array $args { 2247 * Default archive links arguments. Optional. 2248 * 2249 * @type string $type Type of archive to retrieve. Accepts 'daily', 'weekly', 'monthly', 2250 * 'yearly', 'postbypost', or 'alpha'. Both 'postbypost' and 'alpha' 2251 * display the same archive link list as well as post titles instead 2252 * of displaying dates. The difference between the two is that 'alpha' 2253 * will order by post title and 'postbypost' will order by post date. 2254 * Default 'monthly'. 2255 * @type string|int $limit Number of links to limit the query to. Default empty (no limit). 2256 * @type string $format Format each link should take using the $before and $after args. 2257 * Accepts 'link' (`<link>` tag), 'option' (`<option>` tag), 'html' 2258 * (`<li>` tag), or a custom format, which generates a link anchor 2259 * with $before preceding and $after succeeding. Default 'html'. 2260 * @type string $before Markup to prepend to the beginning of each link. Default empty. 2261 * @type string $after Markup to append to the end of each link. Default empty. 2262 * @type bool $show_post_count Whether to display the post count alongside the link. Default false. 2263 * @type bool|int $echo Whether to echo or return the links list. Default 1|true to echo. 2264 * @type string $order Whether to use ascending or descending order. Accepts 'ASC', or 'DESC'. 2265 * Default 'DESC'. 2266 * @type string $post_type Post type. Default 'post'. 2267 * @type string $year Year. Default current year. 2268 * @type string $monthnum Month number. Default current month number. 2269 * @type string $day Day. Default current day. 2270 * @type string $w Week. Default current week. 2271 * } 2272 * @return string|null|void Archive links when 'echo' is false, null when the post type is 2273 * not viewable. Nothing otherwise. 2274 * @phpstan-return ( 2275 * $args is array{ echo: false|0|''|'0', ... } 2276 * ? string|null 2277 * : ( $args is ''|'0'|array ? void : string|null ) 2278 * ) 2279 */ 2280 function wp_get_archives( $args = '' ) { 2281 global $wpdb, $wp_locale; 2282 2283 $defaults = array( 2284 'type' => 'monthly', 2285 'limit' => '', 2286 'format' => 'html', 2287 'before' => '', 2288 'after' => '', 2289 'show_post_count' => false, 2290 'echo' => 1, 2291 'order' => 'DESC', 2292 'post_type' => 'post', 2293 'year' => get_query_var( 'year' ), 2294 'monthnum' => get_query_var( 'monthnum' ), 2295 'day' => get_query_var( 'day' ), 2296 'w' => get_query_var( 'w' ), 2297 ); 2298 2299 /** 2300 * Filters the arguments for displaying archive links. 2301 * 2302 * @since 7.0.0 2303 * 2304 * @see wp_get_archives() 2305 * 2306 * @param array<string, string|int|bool> $args Arguments. 2307 */ 2308 $args = apply_filters( 'wp_get_archives_args', $args ); 2309 2310 $parsed_args = wp_parse_args( $args, $defaults ); 2311 2312 $post_type_object = get_post_type_object( $parsed_args['post_type'] ); 2313 if ( ! is_post_type_viewable( $post_type_object ) ) { 2314 return null; 2315 } 2316 2317 $parsed_args['post_type'] = $post_type_object->name; 2318 2319 if ( '' === $parsed_args['type'] ) { 2320 $parsed_args['type'] = 'monthly'; 2321 } 2322 2323 if ( ! empty( $parsed_args['limit'] ) ) { 2324 $parsed_args['limit'] = absint( $parsed_args['limit'] ); 2325 $parsed_args['limit'] = ' LIMIT ' . $parsed_args['limit']; 2326 } 2327 2328 $order = strtoupper( $parsed_args['order'] ); 2329 if ( 'ASC' !== $order ) { 2330 $order = 'DESC'; 2331 } 2332 2333 // This is what will separate dates on weekly archive links. 2334 $archive_week_separator = '–'; 2335 2336 $sql_where = $wpdb->prepare( "WHERE post_type = %s AND post_status = 'publish'", $parsed_args['post_type'] ); 2337 2338 /** 2339 * Filters the SQL WHERE clause for retrieving archives. 2340 * 2341 * @since 2.2.0 2342 * 2343 * @param string $sql_where Portion of SQL query containing the WHERE clause. 2344 * @param array $parsed_args An array of default arguments. 2345 */ 2346 $where = apply_filters( 'getarchives_where', $sql_where, $parsed_args ); 2347 2348 /** 2349 * Filters the SQL JOIN clause for retrieving archives. 2350 * 2351 * @since 2.2.0 2352 * 2353 * @param string $sql_join Portion of SQL query containing JOIN clause. 2354 * @param array $parsed_args An array of default arguments. 2355 */ 2356 $join = apply_filters( 'getarchives_join', '', $parsed_args ); 2357 2358 $output = ''; 2359 2360 $last_changed = wp_cache_get_last_changed( 'posts' ); 2361 2362 $limit = $parsed_args['limit']; 2363 2364 if ( 'monthly' === $parsed_args['type'] ) { 2365 $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date $order $limit"; 2366 $key = md5( $query ); 2367 $key = "wp_get_archives:$key"; 2368 $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); 2369 if ( ! $results ) { 2370 $results = $wpdb->get_results( $query ); 2371 wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); 2372 } 2373 if ( $results ) { 2374 $after = $parsed_args['after']; 2375 foreach ( (array) $results as $result ) { 2376 $url = get_month_link( $result->year, $result->month ); 2377 if ( 'post' !== $parsed_args['post_type'] ) { 2378 $url = add_query_arg( 'post_type', $parsed_args['post_type'], $url ); 2379 } 2380 /* translators: 1: Month name, 2: 4-digit year. */ 2381 $text = sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $result->month ), $result->year ); 2382 if ( $parsed_args['show_post_count'] ) { 2383 $parsed_args['after'] = ' (' . $result->posts . ')' . $after; 2384 } 2385 $selected = is_archive() && (string) $parsed_args['year'] === $result->year && (string) $parsed_args['monthnum'] === $result->month; 2386 $output .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected ); 2387 } 2388 } 2389 } elseif ( 'yearly' === $parsed_args['type'] ) { 2390 $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit"; 2391 $key = md5( $query ); 2392 $key = "wp_get_archives:$key"; 2393 $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); 2394 if ( ! $results ) { 2395 $results = $wpdb->get_results( $query ); 2396 wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); 2397 } 2398 if ( $results ) { 2399 $after = $parsed_args['after']; 2400 foreach ( (array) $results as $result ) { 2401 $url = get_year_link( $result->year ); 2402 if ( 'post' !== $parsed_args['post_type'] ) { 2403 $url = add_query_arg( 'post_type', $parsed_args['post_type'], $url ); 2404 } 2405 $text = sprintf( '%d', $result->year ); 2406 if ( $parsed_args['show_post_count'] ) { 2407 $parsed_args['after'] = ' (' . $result->posts . ')' . $after; 2408 } 2409 $selected = is_archive() && (string) $parsed_args['year'] === $result->year; 2410 $output .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected ); 2411 } 2412 } 2413 } elseif ( 'daily' === $parsed_args['type'] ) { 2414 $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit"; 2415 $key = md5( $query ); 2416 $key = "wp_get_archives:$key"; 2417 $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); 2418 if ( ! $results ) { 2419 $results = $wpdb->get_results( $query ); 2420 wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); 2421 } 2422 if ( $results ) { 2423 $after = $parsed_args['after']; 2424 $date_format = get_option( 'date_format' ); 2425 foreach ( (array) $results as $result ) { 2426 $url = get_day_link( $result->year, $result->month, $result->dayofmonth ); 2427 if ( 'post' !== $parsed_args['post_type'] ) { 2428 $url = add_query_arg( 'post_type', $parsed_args['post_type'], $url ); 2429 } 2430 $date = sprintf( '%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth ); 2431 $text = mysql2date( $date_format, $date ); 2432 if ( $parsed_args['show_post_count'] ) { 2433 $parsed_args['after'] = ' (' . $result->posts . ')' . $after; 2434 } 2435 $selected = is_archive() && (string) $parsed_args['year'] === $result->year && (string) $parsed_args['monthnum'] === $result->month && (string) $parsed_args['day'] === $result->dayofmonth; 2436 $output .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected ); 2437 } 2438 } 2439 } elseif ( 'weekly' === $parsed_args['type'] ) { 2440 $week = _wp_mysql_week( '`post_date`' ); 2441 $query = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit"; 2442 $key = md5( $query ); 2443 $key = "wp_get_archives:$key"; 2444 $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); 2445 if ( ! $results ) { 2446 $results = $wpdb->get_results( $query ); 2447 wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); 2448 } 2449 $arc_w_last = ''; 2450 if ( $results ) { 2451 $after = $parsed_args['after']; 2452 $start_of_week = get_option( 'start_of_week' ); 2453 $date_format = get_option( 'date_format' ); 2454 foreach ( (array) $results as $result ) { 2455 if ( $result->week !== $arc_w_last ) { 2456 $arc_year = $result->yr; 2457 $arc_w_last = $result->week; 2458 $arc_week = get_weekstartend( $result->yyyymmdd, $start_of_week ); 2459 $arc_week_start = date_i18n( $date_format, $arc_week['start'] ); 2460 $arc_week_end = date_i18n( $date_format, $arc_week['end'] ); 2461 $url = add_query_arg( 2462 array( 2463 'm' => $arc_year, 2464 'w' => $result->week, 2465 ), 2466 home_url( '/' ) 2467 ); 2468 if ( 'post' !== $parsed_args['post_type'] ) { 2469 $url = add_query_arg( 'post_type', $parsed_args['post_type'], $url ); 2470 } 2471 $text = $arc_week_start . $archive_week_separator . $arc_week_end; 2472 if ( $parsed_args['show_post_count'] ) { 2473 $parsed_args['after'] = ' (' . $result->posts . ')' . $after; 2474 } 2475 $selected = is_archive() && (string) $parsed_args['year'] === $result->yr && (string) $parsed_args['w'] === $result->week; 2476 $output .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected ); 2477 } 2478 } 2479 } 2480 } elseif ( ( 'postbypost' === $parsed_args['type'] ) || ( 'alpha' === $parsed_args['type'] ) ) { 2481 $orderby = ( 'alpha' === $parsed_args['type'] ) ? 'post_title ASC ' : 'post_date DESC, ID DESC '; 2482 $query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit"; 2483 $key = md5( $query ); 2484 $key = "wp_get_archives:$key"; 2485 $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); 2486 if ( ! $results ) { 2487 $results = $wpdb->get_results( $query ); 2488 wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); 2489 } 2490 if ( $results ) { 2491 foreach ( (array) $results as $result ) { 2492 if ( '0000-00-00 00:00:00' !== $result->post_date ) { 2493 $url = get_permalink( $result ); 2494 if ( $result->post_title ) { 2495 /** This filter is documented in wp-includes/post-template.php */ 2496 $text = strip_tags( apply_filters( 'the_title', $result->post_title, $result->ID ) ); 2497 } else { 2498 $text = $result->ID; 2499 } 2500 $selected = get_the_ID() === $result->ID; 2501 $output .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected ); 2502 } 2503 } 2504 } 2505 } 2506 2507 if ( $parsed_args['echo'] ) { 2508 echo $output; 2509 } else { 2510 return $output; 2511 } 2512 } 2513 2514 /** 2515 * Gets number of days since the start of the week. 2516 * 2517 * @since 1.5.0 2518 * 2519 * @param int $num Number of day. 2520 * @return float Days since the start of the week. 2521 */ 2522 function calendar_week_mod( $num ) { 2523 $base = 7; 2524 return ( $num - $base * floor( $num / $base ) ); 2525 } 2526 2527 /** 2528 * Displays calendar with days that have posts as links. 2529 * 2530 * The calendar is cached, which will be retrieved, if it exists. If there are 2531 * no posts for the month, then it will not be displayed. 2532 * 2533 * @since 1.0.0 2534 * @since 6.8.0 Added the `$args` parameter, with backward compatibility 2535 * for the replaced `$initial` and `$display` parameters. 2536 * 2537 * @global wpdb $wpdb WordPress database abstraction object. 2538 * @global int $m 2539 * @global int $monthnum 2540 * @global int $year 2541 * @global WP_Locale $wp_locale WordPress date and time locale object. 2542 * @global array $posts 2543 * 2544 * @param array $args { 2545 * Optional. Arguments for the `get_calendar` function. 2546 * 2547 * @type bool $initial Whether to use initial calendar names. Default true. 2548 * @type bool $display Whether to display the calendar output. Default true. 2549 * @type string $post_type Optional. Post type. Default 'post'. 2550 * } 2551 * @return string|null|void Calendar HTML when `$display` is false, null when the site has 2552 * no posts. Nothing otherwise. 2553 * @phpstan-return ( $args is array{ display: false|0|''|'0', ... } ? string|null : void ) 2554 */ 2555 function get_calendar( $args = array() ) { 2556 global $wpdb, $m, $monthnum, $year, $wp_locale, $posts; 2557 2558 $defaults = array( 2559 'initial' => true, 2560 'display' => true, 2561 'post_type' => 'post', 2562 ); 2563 2564 $original_args = func_get_args(); 2565 $args = array(); 2566 2567 if ( ! empty( $original_args ) ) { 2568 if ( ! is_array( $original_args[0] ) ) { 2569 if ( isset( $original_args[0] ) && is_bool( $original_args[0] ) ) { 2570 $defaults['initial'] = $original_args[0]; 2571 } 2572 if ( isset( $original_args[1] ) && is_bool( $original_args[1] ) ) { 2573 $defaults['display'] = $original_args[1]; 2574 } 2575 } else { 2576 $args = $original_args[0]; 2577 } 2578 } 2579 2580 /** 2581 * Filter the `get_calendar` function arguments before they are used. 2582 * 2583 * @since 6.8.0 2584 * 2585 * @param array $args { 2586 * Optional. Arguments for the `get_calendar` function. 2587 * 2588 * @type bool $initial Whether to use initial calendar names. Default true. 2589 * @type bool $display Whether to display the calendar output. Default true. 2590 * @type string $post_type Optional. Post type. Default 'post'. 2591 * } 2592 */ 2593 $args = apply_filters( 'get_calendar_args', wp_parse_args( $args, $defaults ) ); 2594 2595 if ( ! post_type_exists( $args['post_type'] ) ) { 2596 $args['post_type'] = 'post'; 2597 } 2598 2599 $w = 0; 2600 if ( isset( $_GET['w'] ) ) { 2601 $w = (int) $_GET['w']; 2602 } 2603 2604 /* 2605 * Normalize the cache key. 2606 * 2607 * The following ensures the same cache key is used for the same parameter 2608 * and parameter equivalents. This prevents `post_type > post, initial > true` 2609 * from generating a different key from the same values in the reverse order. 2610 * 2611 * `display` is excluded from the cache key as the cache contains the same 2612 * HTML regardless of this function's need to echo or return the output. 2613 * 2614 * The global values contain data generated by the URL query string variables. 2615 */ 2616 $cache_args = $args; 2617 unset( $cache_args['display'] ); 2618 2619 $cache_args['globals'] = array( 2620 'm' => $m, 2621 'monthnum' => $monthnum, 2622 'year' => $year, 2623 'week' => $w, 2624 ); 2625 2626 wp_recursive_ksort( $cache_args ); 2627 $key = md5( serialize( $cache_args ) ); 2628 $cache = wp_cache_get( 'get_calendar', 'calendar' ); 2629 2630 if ( $cache && is_array( $cache ) && isset( $cache[ $key ] ) ) { 2631 /** This filter is documented in wp-includes/general-template.php */ 2632 $output = apply_filters( 'get_calendar', $cache[ $key ], $args ); 2633 2634 if ( $args['display'] ) { 2635 echo $output; 2636 return; 2637 } 2638 2639 return $output; 2640 } 2641 2642 if ( ! is_array( $cache ) ) { 2643 $cache = array(); 2644 } 2645 2646 $post_type = $args['post_type']; 2647 2648 // Quick check. If we have no posts at all, abort! 2649 if ( ! $posts ) { 2650 $gotsome = $wpdb->get_var( 2651 $wpdb->prepare( 2652 "SELECT 1 as test 2653 FROM $wpdb->posts 2654 WHERE post_type = %s 2655 AND post_status = 'publish' 2656 LIMIT 1", 2657 $post_type 2658 ) 2659 ); 2660 2661 if ( ! $gotsome ) { 2662 $cache[ $key ] = ''; 2663 wp_cache_set( 'get_calendar', $cache, 'calendar' ); 2664 return null; 2665 } 2666 } 2667 2668 // week_begins = 0 stands for Sunday. 2669 $week_begins = (int) get_option( 'start_of_week' ); 2670 2671 // Read the current date. 2672 list( $current_year, $current_month, $current_day ) = array_map( 'intval', explode( '-', current_time( 'Y-m-j' ) ) ); 2673 2674 // Let's figure out when we are. 2675 if ( ! empty( $monthnum ) && ! empty( $year ) ) { 2676 $thismonth = (int) $monthnum; 2677 $thisyear = (int) $year; 2678 } elseif ( ! empty( $w ) ) { 2679 // We need to get the month from MySQL. 2680 $thisyear = (int) substr( $m, 0, 4 ); 2681 // It seems MySQL's weeks disagree with PHP's. 2682 $d = ( ( $w - 1 ) * 7 ) + 6; 2683 $thismonth = (int) $wpdb->get_var( 2684 $wpdb->prepare( 2685 "SELECT DATE_FORMAT((DATE_ADD('%d0101', INTERVAL %d DAY) ), '%%m')", 2686 $thisyear, 2687 $d 2688 ) 2689 ); 2690 } elseif ( ! empty( $m ) ) { 2691 $thisyear = (int) substr( $m, 0, 4 ); 2692 if ( strlen( $m ) < 6 ) { 2693 $thismonth = 1; 2694 } else { 2695 $thismonth = (int) substr( $m, 4, 2 ); 2696 } 2697 } else { 2698 $thisyear = $current_year; 2699 $thismonth = $current_month; 2700 } 2701 2702 $unixmonth = mktime( 0, 0, 0, $thismonth, 1, $thisyear ); 2703 $last_day = gmdate( 't', $unixmonth ); 2704 2705 // Get the next and previous month and year with at least one post. 2706 $previous = $wpdb->get_row( 2707 $wpdb->prepare( 2708 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year 2709 FROM $wpdb->posts 2710 WHERE post_date < '%d-%d-01' 2711 AND post_type = %s AND post_status = 'publish' 2712 ORDER BY post_date DESC 2713 LIMIT 1", 2714 $thisyear, 2715 zeroise( $thismonth, 2 ), 2716 $post_type 2717 ) 2718 ); 2719 2720 $next = $wpdb->get_row( 2721 $wpdb->prepare( 2722 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year 2723 FROM $wpdb->posts 2724 WHERE post_date > '%d-%d-%d 23:59:59' 2725 AND post_type = %s AND post_status = 'publish' 2726 ORDER BY post_date ASC 2727 LIMIT 1", 2728 $thisyear, 2729 zeroise( $thismonth, 2 ), 2730 $last_day, 2731 $post_type 2732 ) 2733 ); 2734 2735 /* translators: Calendar caption: 1: Month name, 2: 4-digit year. */ 2736 $calendar_caption = _x( '%1$s %2$s', 'calendar caption' ); 2737 $calendar_output = '<table id="wp-calendar" class="wp-calendar-table"> 2738 <caption>' . sprintf( 2739 $calendar_caption, 2740 $wp_locale->get_month( $thismonth ), 2741 gmdate( 'Y', $unixmonth ) 2742 ) . '</caption> 2743 <thead> 2744 <tr>'; 2745 2746 $myweek = array(); 2747 2748 for ( $wdcount = 0; $wdcount <= 6; $wdcount++ ) { 2749 $myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 ); 2750 } 2751 2752 foreach ( $myweek as $wd ) { 2753 $day_name = $args['initial'] ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd ); 2754 $wd = esc_attr( $wd ); 2755 $calendar_output .= "\n\t\t<th scope=\"col\" aria-label=\"$wd\">$day_name</th>"; 2756 } 2757 2758 $calendar_output .= ' 2759 </tr> 2760 </thead> 2761 <tbody> 2762 <tr>'; 2763 2764 $daywithpost = array(); 2765 2766 // Get days with posts. 2767 $dayswithposts = $wpdb->get_results( 2768 $wpdb->prepare( 2769 "SELECT DISTINCT DAYOFMONTH(post_date) 2770 FROM $wpdb->posts WHERE post_date >= '%d-%d-01 00:00:00' 2771 AND post_type = %s AND post_status = 'publish' 2772 AND post_date <= '%d-%d-%d 23:59:59'", 2773 $thisyear, 2774 zeroise( $thismonth, 2 ), 2775 $post_type, 2776 $thisyear, 2777 zeroise( $thismonth, 2 ), 2778 $last_day 2779 ), 2780 ARRAY_N 2781 ); 2782 2783 if ( $dayswithposts ) { 2784 foreach ( (array) $dayswithposts as $daywith ) { 2785 $daywithpost[] = (int) $daywith[0]; 2786 } 2787 } 2788 2789 // See how much we should pad in the beginning. 2790 $pad = calendar_week_mod( (int) gmdate( 'w', $unixmonth ) - $week_begins ); 2791 if ( $pad > 0 ) { 2792 $calendar_output .= "\n\t\t" . '<td colspan="' . esc_attr( $pad ) . '" class="pad"> </td>'; 2793 } 2794 2795 $newrow = false; 2796 $daysinmonth = (int) gmdate( 't', $unixmonth ); 2797 2798 for ( $day = 1; $day <= $daysinmonth; ++$day ) { 2799 if ( $newrow ) { 2800 $calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t"; 2801 } 2802 2803 $newrow = false; 2804 2805 if ( $current_day === $day 2806 && $current_month === $thismonth 2807 && $current_year === $thisyear 2808 ) { 2809 $calendar_output .= '<td id="today">'; 2810 } else { 2811 $calendar_output .= '<td>'; 2812 } 2813 2814 if ( in_array( $day, $daywithpost, true ) ) { 2815 // Any posts today? 2816 $date_format = gmdate( _x( 'F j, Y', 'daily archives date format' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) ); 2817 /* translators: Post calendar label. %s: Date. */ 2818 $label = sprintf( __( 'Posts published on %s' ), $date_format ); 2819 $calendar_output .= sprintf( 2820 '<a href="%s" aria-label="%s">%s</a>', 2821 get_day_link( $thisyear, $thismonth, $day ), 2822 esc_attr( $label ), 2823 $day 2824 ); 2825 } else { 2826 $calendar_output .= $day; 2827 } 2828 2829 $calendar_output .= '</td>'; 2830 2831 if ( 6 === (int) calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins ) ) { 2832 $newrow = true; 2833 } 2834 } 2835 2836 $pad = 7 - calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins ); 2837 if ( 0 < $pad && $pad < 7 ) { 2838 $calendar_output .= "\n\t\t" . '<td class="pad" colspan="' . esc_attr( $pad ) . '"> </td>'; 2839 } 2840 2841 $calendar_output .= "\n\t</tr>\n\t</tbody>"; 2842 2843 $calendar_output .= "\n\t</table>"; 2844 2845 $calendar_output .= '<nav aria-label="' . __( 'Previous and next months' ) . '" class="wp-calendar-nav">'; 2846 2847 if ( $previous ) { 2848 $calendar_output .= "\n\t\t" . sprintf( 2849 '<span class="wp-calendar-nav-prev"><a href="%1$s">« %2$s</a></span>', 2850 get_month_link( $previous->year, $previous->month ), 2851 $wp_locale->get_month_abbrev( $wp_locale->get_month( $previous->month ) ) 2852 ); 2853 } else { 2854 $calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-prev"> </span>'; 2855 } 2856 2857 $calendar_output .= "\n\t\t" . '<span class="pad"> </span>'; 2858 2859 if ( $next ) { 2860 $calendar_output .= "\n\t\t" . sprintf( 2861 '<span class="wp-calendar-nav-next"><a href="%1$s">%2$s »</a></span>', 2862 get_month_link( $next->year, $next->month ), 2863 $wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) ) 2864 ); 2865 } else { 2866 $calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-next"> </span>'; 2867 } 2868 2869 $calendar_output .= ' 2870 </nav>'; 2871 2872 $cache[ $key ] = $calendar_output; 2873 wp_cache_set( 'get_calendar', $cache, 'calendar' ); 2874 2875 /** 2876 * Filters the HTML calendar output. 2877 * 2878 * @since 3.0.0 2879 * @since 6.8.0 Added the `$args` parameter. 2880 * 2881 * @param string $calendar_output HTML output of the calendar. 2882 * @param array $args { 2883 * Optional. Array of display arguments. 2884 * 2885 * @type bool $initial Whether to use initial calendar names. Default true. 2886 * @type bool $display Whether to display the calendar output. Default true. 2887 * @type string $post_type Optional. Post type. Default 'post'. 2888 * } 2889 */ 2890 $calendar_output = apply_filters( 'get_calendar', $calendar_output, $args ); 2891 2892 if ( $args['display'] ) { 2893 echo $calendar_output; 2894 return; 2895 } 2896 2897 return $calendar_output; 2898 } 2899 2900 /** 2901 * Purges the cached results of get_calendar. 2902 * 2903 * @see get_calendar() 2904 * @since 2.1.0 2905 */ 2906 function delete_get_calendar_cache() { 2907 wp_cache_delete( 'get_calendar', 'calendar' ); 2908 } 2909 2910 /** 2911 * Displays all of the allowed tags in HTML format with attributes. 2912 * 2913 * This is useful for displaying in the comment area, which elements and 2914 * attributes are supported. As well as any plugins which want to display it. 2915 * 2916 * @since 1.0.1 2917 * @since 4.4.0 No longer used in core. 2918 * 2919 * @global array $allowedtags 2920 * 2921 * @return string HTML allowed tags entity encoded. 2922 */ 2923 function allowed_tags() { 2924 global $allowedtags; 2925 $allowed = ''; 2926 foreach ( (array) $allowedtags as $tag => $attributes ) { 2927 $allowed .= '<' . $tag; 2928 if ( 0 < count( $attributes ) ) { 2929 foreach ( $attributes as $attribute => $limits ) { 2930 $allowed .= ' ' . $attribute . '=""'; 2931 } 2932 } 2933 $allowed .= '> '; 2934 } 2935 return htmlentities( $allowed ); 2936 } 2937 2938 /***** Date/Time tags */ 2939 2940 /** 2941 * Outputs the date in iso8601 format for xml files. 2942 * 2943 * @since 1.0.0 2944 */ 2945 function the_date_xml() { 2946 echo mysql2date( 'Y-m-d', get_post()->post_date, false ); 2947 } 2948 2949 /** 2950 * Displays or retrieves the date of the post (once per date). 2951 * 2952 * Will only output the date if the current post's date is different from the 2953 * previous one output. 2954 * 2955 * i.e. Only one date listing will show per day worth of posts shown in the loop, even if the 2956 * function is called several times for each post. 2957 * 2958 * HTML output can be filtered with {@see 'the_date'}. 2959 * Date string output can be filtered with {@see 'get_the_date'}. 2960 * 2961 * @since 0.71 2962 * 2963 * @global string $currentday The day of the current post in the loop. 2964 * @global string $previousday The day of the previous post in the loop. 2965 * 2966 * @param string $format Optional. PHP date format. Defaults to the 'date_format' option. 2967 * @param string $before Optional. Output before the date. Default empty. 2968 * @param string $after Optional. Output after the date. Default empty. 2969 * @param bool $display Optional. Whether to echo the date or return it. Default true. 2970 * @return string|void The date when `$display` is false, or an empty string when the post's 2971 * date matches the previously output one. Nothing otherwise. 2972 * @phpstan-return ( $display is true ? void : string ) 2973 */ 2974 function the_date( $format = '', $before = '', $after = '', $display = true ) { 2975 global $currentday, $previousday; 2976 2977 $the_date = ''; 2978 2979 if ( is_new_day() ) { 2980 $the_date = $before . get_the_date( $format ) . $after; 2981 $previousday = $currentday; 2982 } 2983 2984 /** 2985 * Filters the date of the post, for display. 2986 * 2987 * @since 0.71 2988 * 2989 * @param string $the_date The formatted date string. 2990 * @param string $format PHP date format. 2991 * @param string $before HTML output before the date. 2992 * @param string $after HTML output after the date. 2993 */ 2994 $the_date = apply_filters( 'the_date', $the_date, $format, $before, $after ); 2995 2996 if ( ! $display ) { 2997 return $the_date; 2998 } 2999 3000 echo $the_date; 3001 } 3002 3003 /** 3004 * Retrieves the date of the post. 3005 * 3006 * Unlike the_date() this function will always return the date. 3007 * Modify output with the {@see 'get_the_date'} filter. 3008 * 3009 * @since 3.0.0 3010 * 3011 * @param string $format Optional. PHP date format. Defaults to the 'date_format' option. 3012 * @param int|WP_Post|null $post Optional. Post ID or WP_Post object. Default current post. 3013 * @return string|int|false Date the current post was written. False on failure. 3014 * 3015 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3016 */ 3017 function get_the_date( $format = '', $post = null ) { 3018 $post = get_post( $post ); 3019 3020 if ( ! $post ) { 3021 return false; 3022 } 3023 3024 $_format = ! empty( $format ) ? $format : get_option( 'date_format' ); 3025 3026 $the_date = get_post_time( $_format, false, $post, true ); 3027 3028 /** 3029 * Filters the date of the post. 3030 * 3031 * @since 3.0.0 3032 * 3033 * @param string|int $the_date Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3034 * @param string $format PHP date format. 3035 * @param WP_Post $post The post object. 3036 */ 3037 return apply_filters( 'get_the_date', $the_date, $format, $post ); 3038 } 3039 3040 /** 3041 * Displays the date on which the post was last modified. 3042 * 3043 * @since 2.1.0 3044 * 3045 * @param string $format Optional. PHP date format. Defaults to the 'date_format' option. 3046 * @param string $before Optional. Output before the date. Default empty. 3047 * @param string $after Optional. Output after the date. Default empty. 3048 * @param bool $display Optional. Whether to echo the date or return it. Default true. 3049 * @return string|void The modified date when `$display` is false, nothing otherwise. 3050 * @phpstan-return ( $display is true ? void : string ) 3051 */ 3052 function the_modified_date( $format = '', $before = '', $after = '', $display = true ) { 3053 $the_modified_date = $before . get_the_modified_date( $format ) . $after; 3054 3055 /** 3056 * Filters the date a post was last modified, for display. 3057 * 3058 * @since 2.1.0 3059 * 3060 * @param string $the_modified_date The last modified date. 3061 * @param string $format PHP date format. 3062 * @param string $before HTML output before the date. 3063 * @param string $after HTML output after the date. 3064 */ 3065 $the_modified_date = apply_filters( 'the_modified_date', $the_modified_date, $format, $before, $after ); 3066 3067 if ( ! $display ) { 3068 return $the_modified_date; 3069 } 3070 3071 echo $the_modified_date; 3072 } 3073 3074 /** 3075 * Retrieves the date on which the post was last modified. 3076 * 3077 * @since 2.1.0 3078 * @since 4.6.0 Added the `$post` parameter. 3079 * 3080 * @param string $format Optional. PHP date format. Defaults to the 'date_format' option. 3081 * @param int|WP_Post|null $post Optional. Post ID or WP_Post object. Default current post. 3082 * @return string|int|false Date the current post was modified. False on failure. 3083 * 3084 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3085 */ 3086 function get_the_modified_date( $format = '', $post = null ) { 3087 $post = get_post( $post ); 3088 3089 if ( ! $post ) { 3090 // For backward compatibility, failures go through the filter below. 3091 $the_time = false; 3092 } else { 3093 $_format = ! empty( $format ) ? $format : get_option( 'date_format' ); 3094 3095 $the_time = get_post_modified_time( $_format, false, $post, true ); 3096 } 3097 3098 /** 3099 * Filters the date a post was last modified. 3100 * 3101 * @since 2.1.0 3102 * @since 4.6.0 Added the `$post` parameter. 3103 * 3104 * @param string|int|false $the_time The formatted date or false if no post is found. 3105 * @param string $format PHP date format. 3106 * @param WP_Post|null $post WP_Post object or null if no post is found. 3107 */ 3108 return apply_filters( 'get_the_modified_date', $the_time, $format, $post ); 3109 } 3110 3111 /** 3112 * Displays the time of the post. 3113 * 3114 * @since 0.71 3115 * 3116 * @param string $format Optional. Format to use for retrieving the time the post 3117 * was written. Accepts 'G', 'U', or PHP date format. 3118 * Defaults to the 'time_format' option. 3119 */ 3120 function the_time( $format = '' ) { 3121 /** 3122 * Filters the time of the post, for display. 3123 * 3124 * @since 0.71 3125 * 3126 * @param string $get_the_time The formatted time. 3127 * @param string $format Format to use for retrieving the time the post 3128 * was written. Accepts 'G', 'U', or PHP date format. 3129 */ 3130 echo apply_filters( 'the_time', get_the_time( $format ), $format ); 3131 } 3132 3133 /** 3134 * Retrieves the time of the post. 3135 * 3136 * @since 1.5.0 3137 * 3138 * @param string $format Optional. Format to use for retrieving the time the post 3139 * was written. Accepts 'G', 'U', or PHP date format. 3140 * Defaults to the 'time_format' option. 3141 * @param int|WP_Post|null $post Post ID or post object. Default is global `$post` object. 3142 * @return string|int|false Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3143 * False on failure. 3144 * 3145 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3146 */ 3147 function get_the_time( $format = '', $post = null ) { 3148 $post = get_post( $post ); 3149 3150 if ( ! $post ) { 3151 return false; 3152 } 3153 3154 $_format = ! empty( $format ) ? $format : get_option( 'time_format' ); 3155 3156 $the_time = get_post_time( $_format, false, $post, true ); 3157 3158 /** 3159 * Filters the time of the post. 3160 * 3161 * @since 1.5.0 3162 * 3163 * @param string|int $the_time Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3164 * @param string $format Format to use for retrieving the time the post 3165 * was written. Accepts 'G', 'U', or PHP date format. 3166 * @param WP_Post $post Post object. 3167 */ 3168 return apply_filters( 'get_the_time', $the_time, $format, $post ); 3169 } 3170 3171 /** 3172 * Retrieves the localized time of the post. 3173 * 3174 * @since 2.0.0 3175 * 3176 * @param string $format Optional. Format to use for retrieving the time the post 3177 * was written. Accepts 'G', 'U', or PHP date format. Default 'U'. 3178 * @param bool $gmt Optional. Whether to retrieve the GMT time. Default false. 3179 * @param int|WP_Post|null $post Post ID or post object. Default is global `$post` object. 3180 * @param bool $translate Whether to translate the time string. Default false. 3181 * @return string|int|false Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3182 * False on failure. 3183 * 3184 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3185 */ 3186 function get_post_time( $format = 'U', $gmt = false, $post = null, $translate = false ) { 3187 $post = get_post( $post ); 3188 3189 if ( ! $post ) { 3190 return false; 3191 } 3192 3193 $source = ( $gmt ) ? 'gmt' : 'local'; 3194 $datetime = get_post_datetime( $post, 'date', $source ); 3195 3196 if ( false === $datetime ) { 3197 return false; 3198 } 3199 3200 if ( 'U' === $format || 'G' === $format ) { 3201 $time = $datetime->getTimestamp(); 3202 3203 // Returns a sum of timestamp with timezone offset. Ideally should never be used. 3204 if ( ! $gmt ) { 3205 $time += $datetime->getOffset(); 3206 } 3207 } elseif ( $translate ) { 3208 $time = wp_date( $format, $datetime->getTimestamp(), $gmt ? new DateTimeZone( 'UTC' ) : null ); 3209 } else { 3210 if ( $gmt ) { 3211 $datetime = $datetime->setTimezone( new DateTimeZone( 'UTC' ) ); 3212 } 3213 3214 $time = $datetime->format( $format ); 3215 } 3216 3217 /** 3218 * Filters the localized time of the post. 3219 * 3220 * @since 2.6.0 3221 * 3222 * @param string|int $time Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3223 * @param string $format Format to use for retrieving the date of the post. 3224 * Accepts 'G', 'U', or PHP date format. 3225 * @param bool $gmt Whether to retrieve the GMT time. 3226 */ 3227 return apply_filters( 'get_post_time', $time, $format, $gmt ); 3228 } 3229 3230 /** 3231 * Retrieves post published or modified time as a `DateTimeImmutable` object instance. 3232 * 3233 * The object will be set to the timezone from WordPress settings. 3234 * 3235 * For legacy reasons, this function allows to choose to instantiate from local or UTC time in database. 3236 * Normally this should make no difference to the result. However, the values might get out of sync in database, 3237 * typically because of timezone setting changes. The parameter ensures the ability to reproduce backwards 3238 * compatible behaviors in such cases. 3239 * 3240 * @since 5.3.0 3241 * 3242 * @param int|WP_Post|null $post Optional. Post ID or post object. Default is global `$post` object. 3243 * @param string $field Optional. Published or modified time to use from database. Accepts 'date' or 'modified'. 3244 * Default 'date'. 3245 * @param string $source Optional. Local or UTC time to use from database. Accepts 'local' or 'gmt'. 3246 * Default 'local'. 3247 * @return DateTimeImmutable|false Time object on success, false on failure. 3248 */ 3249 function get_post_datetime( $post = null, $field = 'date', $source = 'local' ) { 3250 $post = get_post( $post ); 3251 3252 if ( ! $post ) { 3253 return false; 3254 } 3255 3256 $wp_timezone = wp_timezone(); 3257 3258 if ( 'gmt' === $source ) { 3259 $time = ( 'modified' === $field ) ? $post->post_modified_gmt : $post->post_date_gmt; 3260 $timezone = new DateTimeZone( 'UTC' ); 3261 } else { 3262 $time = ( 'modified' === $field ) ? $post->post_modified : $post->post_date; 3263 $timezone = $wp_timezone; 3264 } 3265 3266 if ( empty( $time ) || '0000-00-00 00:00:00' === $time ) { 3267 return false; 3268 } 3269 3270 $datetime = date_create_immutable_from_format( 'Y-m-d H:i:s', $time, $timezone ); 3271 3272 if ( false === $datetime ) { 3273 return false; 3274 } 3275 3276 return $datetime->setTimezone( $wp_timezone ); 3277 } 3278 3279 /** 3280 * Retrieves post published or modified time as a Unix timestamp. 3281 * 3282 * Note that this function returns a true Unix timestamp, not summed with timezone offset 3283 * like older WP functions. 3284 * 3285 * @since 5.3.0 3286 * 3287 * @param int|WP_Post|null $post Optional. Post ID or post object. Default is global `$post` object. 3288 * @param string $field Optional. Published or modified time to use from database. Accepts 'date' or 'modified'. 3289 * Default 'date'. 3290 * @return int|false Unix timestamp on success, false on failure. 3291 */ 3292 function get_post_timestamp( $post = null, $field = 'date' ) { 3293 $datetime = get_post_datetime( $post, $field ); 3294 3295 if ( false === $datetime ) { 3296 return false; 3297 } 3298 3299 return $datetime->getTimestamp(); 3300 } 3301 3302 /** 3303 * Displays the time at which the post was last modified. 3304 * 3305 * @since 2.0.0 3306 * 3307 * @param string $format Optional. Format to use for retrieving the time the post 3308 * was modified. Accepts 'G', 'U', or PHP date format. 3309 * Defaults to the 'time_format' option. 3310 */ 3311 function the_modified_time( $format = '' ) { 3312 /** 3313 * Filters the localized time a post was last modified, for display. 3314 * 3315 * @since 2.0.0 3316 * 3317 * @param string|false $get_the_modified_time The formatted time or false if no post is found. 3318 * @param string $format Format to use for retrieving the time the post 3319 * was modified. Accepts 'G', 'U', or PHP date format. 3320 */ 3321 echo apply_filters( 'the_modified_time', get_the_modified_time( $format ), $format ); 3322 } 3323 3324 /** 3325 * Retrieves the time at which the post was last modified. 3326 * 3327 * @since 2.0.0 3328 * @since 4.6.0 Added the `$post` parameter. 3329 * 3330 * @param string $format Optional. Format to use for retrieving the time the post 3331 * was modified. Accepts 'G', 'U', or PHP date format. 3332 * Defaults to the 'time_format' option. 3333 * @param int|WP_Post|null $post Optional. Post ID or WP_Post object. Default current post. 3334 * @return string|int|false Formatted date string or Unix timestamp. False on failure. 3335 * 3336 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3337 */ 3338 function get_the_modified_time( $format = '', $post = null ) { 3339 $post = get_post( $post ); 3340 3341 if ( ! $post ) { 3342 // For backward compatibility, failures go through the filter below. 3343 $the_time = false; 3344 } else { 3345 $_format = ! empty( $format ) ? $format : get_option( 'time_format' ); 3346 3347 $the_time = get_post_modified_time( $_format, false, $post, true ); 3348 } 3349 3350 /** 3351 * Filters the localized time a post was last modified. 3352 * 3353 * @since 2.0.0 3354 * @since 4.6.0 Added the `$post` parameter. 3355 * 3356 * @param string|int|false $the_time The formatted time or false if no post is found. 3357 * @param string $format Format to use for retrieving the time the post 3358 * was modified. Accepts 'G', 'U', or PHP date format. 3359 * @param WP_Post|null $post WP_Post object or null if no post is found. 3360 */ 3361 return apply_filters( 'get_the_modified_time', $the_time, $format, $post ); 3362 } 3363 3364 /** 3365 * Retrieves the time at which the post was last modified. 3366 * 3367 * @since 2.0.0 3368 * 3369 * @param string $format Optional. Format to use for retrieving the time the post 3370 * was modified. Accepts 'G', 'U', or PHP date format. Default 'U'. 3371 * @param bool $gmt Optional. Whether to retrieve the GMT time. Default false. 3372 * @param int|WP_Post|null $post Post ID or post object. Default is global `$post` object. 3373 * @param bool $translate Whether to translate the time string. Default false. 3374 * @return string|int|false Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3375 * False on failure. 3376 * 3377 * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false ) 3378 */ 3379 function get_post_modified_time( $format = 'U', $gmt = false, $post = null, $translate = false ) { 3380 $post = get_post( $post ); 3381 3382 if ( ! $post ) { 3383 return false; 3384 } 3385 3386 $source = ( $gmt ) ? 'gmt' : 'local'; 3387 $datetime = get_post_datetime( $post, 'modified', $source ); 3388 3389 if ( false === $datetime ) { 3390 return false; 3391 } 3392 3393 if ( 'U' === $format || 'G' === $format ) { 3394 $time = $datetime->getTimestamp(); 3395 3396 // Returns a sum of timestamp with timezone offset. Ideally should never be used. 3397 if ( ! $gmt ) { 3398 $time += $datetime->getOffset(); 3399 } 3400 } elseif ( $translate ) { 3401 $time = wp_date( $format, $datetime->getTimestamp(), $gmt ? new DateTimeZone( 'UTC' ) : null ); 3402 } else { 3403 if ( $gmt ) { 3404 $datetime = $datetime->setTimezone( new DateTimeZone( 'UTC' ) ); 3405 } 3406 3407 $time = $datetime->format( $format ); 3408 } 3409 3410 /** 3411 * Filters the localized time a post was last modified. 3412 * 3413 * @since 2.8.0 3414 * 3415 * @param string|int $time Formatted date string or Unix timestamp if `$format` is 'U' or 'G'. 3416 * @param string $format Format to use for retrieving the time the post was modified. 3417 * Accepts 'G', 'U', or PHP date format. Default 'U'. 3418 * @param bool $gmt Whether to retrieve the GMT time. Default false. 3419 */ 3420 return apply_filters( 'get_post_modified_time', $time, $format, $gmt ); 3421 } 3422 3423 /** 3424 * Displays the localized weekday for the post. 3425 * 3426 * @since 0.71 3427 * 3428 * @global WP_Locale $wp_locale WordPress date and time locale object. 3429 */ 3430 function the_weekday() { 3431 global $wp_locale; 3432 3433 $post = get_post(); 3434 3435 if ( ! $post ) { 3436 return; 3437 } 3438 3439 $the_weekday = $wp_locale->get_weekday( get_post_time( 'w', false, $post ) ); 3440 3441 /** 3442 * Filters the localized weekday of the post, for display. 3443 * 3444 * @since 0.71 3445 * 3446 * @param string $the_weekday 3447 */ 3448 echo apply_filters( 'the_weekday', $the_weekday ); 3449 } 3450 3451 /** 3452 * Displays the localized weekday for the post. 3453 * 3454 * Will only output the weekday if the current post's weekday is different from 3455 * the previous one output. 3456 * 3457 * @since 0.71 3458 * 3459 * @global WP_Locale $wp_locale WordPress date and time locale object. 3460 * @global string $currentday The day of the current post in the loop. 3461 * @global string $previousweekday The day of the previous post in the loop. 3462 * 3463 * @param string $before Optional. Output before the date. Default empty. 3464 * @param string $after Optional. Output after the date. Default empty. 3465 */ 3466 function the_weekday_date( $before = '', $after = '' ) { 3467 global $wp_locale, $currentday, $previousweekday; 3468 3469 $post = get_post(); 3470 3471 if ( ! $post ) { 3472 return; 3473 } 3474 3475 $the_weekday_date = ''; 3476 3477 if ( $currentday !== $previousweekday ) { 3478 $the_weekday_date .= $before; 3479 $the_weekday_date .= $wp_locale->get_weekday( get_post_time( 'w', false, $post ) ); 3480 $the_weekday_date .= $after; 3481 $previousweekday = $currentday; 3482 } 3483 3484 /** 3485 * Filters the localized weekday of the post, for display. 3486 * 3487 * @since 0.71 3488 * 3489 * @param string $the_weekday_date The weekday on which the post was written. 3490 * @param string $before The HTML to output before the date. 3491 * @param string $after The HTML to output after the date. 3492 */ 3493 echo apply_filters( 'the_weekday_date', $the_weekday_date, $before, $after ); 3494 } 3495 3496 /** 3497 * Fires the wp_head action. 3498 * 3499 * See {@see 'wp_head'}. 3500 * 3501 * @since 1.2.0 3502 */ 3503 function wp_head() { 3504 /** 3505 * Prints scripts or data in the head tag on the front end. 3506 * 3507 * @since 1.5.0 3508 */ 3509 do_action( 'wp_head' ); 3510 } 3511 3512 /** 3513 * Fires the wp_footer action. 3514 * 3515 * See {@see 'wp_footer'}. 3516 * 3517 * @since 1.5.1 3518 */ 3519 function wp_footer() { 3520 /** 3521 * Prints scripts or data before the closing body tag on the front end. 3522 * 3523 * @since 1.5.1 3524 */ 3525 do_action( 'wp_footer' ); 3526 } 3527 3528 /** 3529 * Fires the wp_body_open action. 3530 * 3531 * See {@see 'wp_body_open'}. 3532 * 3533 * @since 5.2.0 3534 */ 3535 function wp_body_open() { 3536 /** 3537 * Triggered after the opening body tag. 3538 * 3539 * @since 5.2.0 3540 */ 3541 do_action( 'wp_body_open' ); 3542 } 3543 3544 /** 3545 * Displays the links to the general feeds. 3546 * 3547 * @since 2.8.0 3548 * 3549 * @param array $args Optional arguments. 3550 */ 3551 function feed_links( $args = array() ) { 3552 if ( ! current_theme_supports( 'automatic-feed-links' ) ) { 3553 return; 3554 } 3555 3556 $defaults = array( 3557 /* translators: Separator between site name and feed type in feed links. */ 3558 'separator' => _x( '»', 'feed link' ), 3559 /* translators: 1: Site title, 2: Separator (raquo). */ 3560 'feedtitle' => __( '%1$s %2$s Feed' ), 3561 /* translators: 1: Site title, 2: Separator (raquo). */ 3562 'comstitle' => __( '%1$s %2$s Comments Feed' ), 3563 ); 3564 3565 $args = wp_parse_args( $args, $defaults ); 3566 3567 /** 3568 * Filters the feed links arguments. 3569 * 3570 * @since 6.7.0 3571 * 3572 * @param array $args An array of feed links arguments. 3573 */ 3574 $args = apply_filters( 'feed_links_args', $args ); 3575 3576 /** 3577 * Filters whether to display the posts feed link. 3578 * 3579 * @since 4.4.0 3580 * 3581 * @param bool $show Whether to display the posts feed link. Default true. 3582 */ 3583 if ( apply_filters( 'feed_links_show_posts_feed', true ) ) { 3584 printf( 3585 '<link rel="alternate" type="%s" title="%s" href="%s" />' . "\n", 3586 feed_content_type(), 3587 esc_attr( sprintf( $args['feedtitle'], get_bloginfo( 'name' ), $args['separator'] ) ), 3588 esc_url( get_feed_link() ) 3589 ); 3590 } 3591 3592 /** 3593 * Filters whether to display the comments feed link. 3594 * 3595 * @since 4.4.0 3596 * 3597 * @param bool $show Whether to display the comments feed link. Default true. 3598 */ 3599 if ( apply_filters( 'feed_links_show_comments_feed', true ) ) { 3600 printf( 3601 '<link rel="alternate" type="%s" title="%s" href="%s" />' . "\n", 3602 feed_content_type(), 3603 esc_attr( sprintf( $args['comstitle'], get_bloginfo( 'name' ), $args['separator'] ) ), 3604 esc_url( get_feed_link( 'comments_' . get_default_feed() ) ) 3605 ); 3606 } 3607 } 3608 3609 /** 3610 * Displays the links to the extra feeds such as category feeds. 3611 * 3612 * @since 2.8.0 3613 * 3614 * @param array $args Optional arguments. 3615 */ 3616 function feed_links_extra( $args = array() ) { 3617 $defaults = array( 3618 /* translators: Separator between site name and feed type in feed links. */ 3619 'separator' => _x( '»', 'feed link' ), 3620 /* translators: 1: Site name, 2: Separator (raquo), 3: Post title. */ 3621 'singletitle' => __( '%1$s %2$s %3$s Comments Feed' ), 3622 /* translators: 1: Site name, 2: Separator (raquo), 3: Category name. */ 3623 'cattitle' => __( '%1$s %2$s %3$s Category Feed' ), 3624 /* translators: 1: Site name, 2: Separator (raquo), 3: Tag name. */ 3625 'tagtitle' => __( '%1$s %2$s %3$s Tag Feed' ), 3626 /* translators: 1: Site name, 2: Separator (raquo), 3: Term name, 4: Taxonomy singular name. */ 3627 'taxtitle' => __( '%1$s %2$s %3$s %4$s Feed' ), 3628 /* translators: 1: Site name, 2: Separator (raquo), 3: Author name. */ 3629 'authortitle' => __( '%1$s %2$s Posts by %3$s Feed' ), 3630 /* translators: 1: Site name, 2: Separator (raquo), 3: Search query. */ 3631 'searchtitle' => __( '%1$s %2$s Search Results for “%3$s” Feed' ), 3632 /* translators: 1: Site name, 2: Separator (raquo), 3: Post type name. */ 3633 'posttypetitle' => __( '%1$s %2$s %3$s Feed' ), 3634 ); 3635 3636 $args = wp_parse_args( $args, $defaults ); 3637 3638 /** 3639 * Filters the extra feed links arguments. 3640 * 3641 * @since 6.7.0 3642 * 3643 * @param array $args An array of extra feed links arguments. 3644 */ 3645 $args = apply_filters( 'feed_links_extra_args', $args ); 3646 3647 /* 3648 * The template conditionals are referring to the global query, so the queried object is used rather than 3649 * depending on a global $post being set. 3650 */ 3651 $queried_object = get_queried_object(); 3652 if ( is_singular() && $queried_object instanceof WP_Post ) { 3653 $post = $queried_object; 3654 3655 /** This filter is documented in wp-includes/general-template.php */ 3656 $show_comments_feed = apply_filters( 'feed_links_show_comments_feed', true ); 3657 3658 /** 3659 * Filters whether to display the post comments feed link. 3660 * 3661 * This filter allows to enable or disable the feed link for a singular post 3662 * in a way that is independent of {@see 'feed_links_show_comments_feed'} 3663 * (which controls the global comments feed). The result of that filter 3664 * is accepted as a parameter. 3665 * 3666 * @since 6.1.0 3667 * 3668 * @param bool $show_comments_feed Whether to display the post comments feed link. Defaults to 3669 * the {@see 'feed_links_show_comments_feed'} filter result. 3670 */ 3671 $show_post_comments_feed = apply_filters( 'feed_links_extra_show_post_comments_feed', $show_comments_feed ); 3672 3673 if ( $show_post_comments_feed && ( comments_open( $post ) || pings_open( $post ) || (int) $post->comment_count > 0 ) ) { 3674 $title = sprintf( 3675 $args['singletitle'], 3676 get_bloginfo( 'name' ), 3677 $args['separator'], 3678 the_title_attribute( 3679 array( 3680 'echo' => false, 3681 'post' => $post, 3682 ) 3683 ) 3684 ); 3685 3686 $feed_link = get_post_comments_feed_link( $post->ID ); 3687 3688 if ( $feed_link ) { 3689 $href = $feed_link; 3690 } 3691 } 3692 } elseif ( is_post_type_archive() ) { 3693 /** 3694 * Filters whether to display the post type archive feed link. 3695 * 3696 * @since 6.1.0 3697 * 3698 * @param bool $show Whether to display the post type archive feed link. Default true. 3699 */ 3700 $show_post_type_archive_feed = apply_filters( 'feed_links_extra_show_post_type_archive_feed', true ); 3701 3702 if ( $show_post_type_archive_feed ) { 3703 $post_type = get_query_var( 'post_type' ); 3704 3705 if ( is_array( $post_type ) ) { 3706 $post_type = reset( $post_type ); 3707 } 3708 3709 $post_type_obj = get_post_type_object( $post_type ); 3710 3711 $title = sprintf( 3712 $args['posttypetitle'], 3713 get_bloginfo( 'name' ), 3714 $args['separator'], 3715 $post_type_obj->labels->name 3716 ); 3717 3718 $href = get_post_type_archive_feed_link( $post_type_obj->name ); 3719 } 3720 } elseif ( is_category() ) { 3721 /** 3722 * Filters whether to display the category feed link. 3723 * 3724 * @since 6.1.0 3725 * 3726 * @param bool $show Whether to display the category feed link. Default true. 3727 */ 3728 $show_category_feed = apply_filters( 'feed_links_extra_show_category_feed', true ); 3729 3730 if ( $show_category_feed ) { 3731 $term = get_queried_object(); 3732 3733 if ( $term ) { 3734 $title = sprintf( 3735 $args['cattitle'], 3736 get_bloginfo( 'name' ), 3737 $args['separator'], 3738 $term->name 3739 ); 3740 3741 $href = get_category_feed_link( $term->term_id ); 3742 } 3743 } 3744 } elseif ( is_tag() ) { 3745 /** 3746 * Filters whether to display the tag feed link. 3747 * 3748 * @since 6.1.0 3749 * 3750 * @param bool $show Whether to display the tag feed link. Default true. 3751 */ 3752 $show_tag_feed = apply_filters( 'feed_links_extra_show_tag_feed', true ); 3753 3754 if ( $show_tag_feed ) { 3755 $term = get_queried_object(); 3756 3757 if ( $term ) { 3758 $title = sprintf( 3759 $args['tagtitle'], 3760 get_bloginfo( 'name' ), 3761 $args['separator'], 3762 $term->name 3763 ); 3764 3765 $href = get_tag_feed_link( $term->term_id ); 3766 } 3767 } 3768 } elseif ( is_tax() ) { 3769 /** 3770 * Filters whether to display the custom taxonomy feed link. 3771 * 3772 * @since 6.1.0 3773 * 3774 * @param bool $show Whether to display the custom taxonomy feed link. Default true. 3775 */ 3776 $show_tax_feed = apply_filters( 'feed_links_extra_show_tax_feed', true ); 3777 3778 if ( $show_tax_feed ) { 3779 $term = get_queried_object(); 3780 3781 if ( $term ) { 3782 $tax = get_taxonomy( $term->taxonomy ); 3783 3784 $title = sprintf( 3785 $args['taxtitle'], 3786 get_bloginfo( 'name' ), 3787 $args['separator'], 3788 $term->name, 3789 $tax->labels->singular_name 3790 ); 3791 3792 $href = get_term_feed_link( $term->term_id, $term->taxonomy ); 3793 } 3794 } 3795 } elseif ( is_author() ) { 3796 /** 3797 * Filters whether to display the author feed link. 3798 * 3799 * @since 6.1.0 3800 * 3801 * @param bool $show Whether to display the author feed link. Default true. 3802 */ 3803 $show_author_feed = apply_filters( 'feed_links_extra_show_author_feed', true ); 3804 3805 if ( $show_author_feed ) { 3806 $author_id = (int) get_query_var( 'author' ); 3807 3808 $title = sprintf( 3809 $args['authortitle'], 3810 get_bloginfo( 'name' ), 3811 $args['separator'], 3812 get_the_author_meta( 'display_name', $author_id ) 3813 ); 3814 3815 $href = get_author_feed_link( $author_id ); 3816 } 3817 } elseif ( is_search() ) { 3818 /** 3819 * Filters whether to display the search results feed link. 3820 * 3821 * @since 6.1.0 3822 * 3823 * @param bool $show Whether to display the search results feed link. Default true. 3824 */ 3825 $show_search_feed = apply_filters( 'feed_links_extra_show_search_feed', true ); 3826 3827 if ( $show_search_feed ) { 3828 $title = sprintf( 3829 $args['searchtitle'], 3830 get_bloginfo( 'name' ), 3831 $args['separator'], 3832 get_search_query( false ) 3833 ); 3834 3835 $href = get_search_feed_link(); 3836 } 3837 } 3838 3839 if ( isset( $title ) && isset( $href ) ) { 3840 printf( 3841 '<link rel="alternate" type="%s" title="%s" href="%s" />' . "\n", 3842 feed_content_type(), 3843 esc_attr( $title ), 3844 esc_url( $href ) 3845 ); 3846 } 3847 } 3848 3849 /** 3850 * Displays the link to the Really Simple Discovery service endpoint. 3851 * 3852 * @link http://archipelago.phrasewise.com/rsd 3853 * @since 2.0.0 3854 */ 3855 function rsd_link() { 3856 printf( 3857 '<link rel="EditURI" type="application/rsd+xml" title="RSD" href="%s" />' . "\n", 3858 esc_url( site_url( 'xmlrpc.php?rsd', 'rpc' ) ) 3859 ); 3860 } 3861 3862 /** 3863 * Displays a referrer `strict-origin-when-cross-origin` meta tag. 3864 * 3865 * Outputs a referrer `strict-origin-when-cross-origin` meta tag that tells the browser not to send 3866 * the full URL as a referrer to other sites when cross-origin assets are loaded. 3867 * 3868 * Typical usage is as a {@see 'wp_head'} callback: 3869 * 3870 * add_action( 'wp_head', 'wp_strict_cross_origin_referrer' ); 3871 * 3872 * @since 5.7.0 3873 */ 3874 function wp_strict_cross_origin_referrer() { 3875 ?> 3876 <meta name='referrer' content='strict-origin-when-cross-origin' /> 3877 <?php 3878 } 3879 3880 /** 3881 * Displays site icon meta tags. 3882 * 3883 * @since 4.3.0 3884 * 3885 * @link https://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon HTML5 specification link icon. 3886 */ 3887 function wp_site_icon() { 3888 if ( ! has_site_icon() && ! is_customize_preview() ) { 3889 return; 3890 } 3891 3892 $meta_tags = array(); 3893 $icon_32 = get_site_icon_url( 32 ); 3894 if ( empty( $icon_32 ) && is_customize_preview() ) { 3895 $icon_32 = '/favicon.ico'; // Serve default favicon URL in customizer so element can be updated for preview. 3896 } 3897 if ( $icon_32 ) { 3898 $meta_tags[] = sprintf( '<link rel="icon" href="%s" sizes="32x32" />', esc_url( $icon_32 ) ); 3899 } 3900 $icon_192 = get_site_icon_url( 192 ); 3901 if ( $icon_192 ) { 3902 $meta_tags[] = sprintf( '<link rel="icon" href="%s" sizes="192x192" />', esc_url( $icon_192 ) ); 3903 } 3904 $icon_180 = get_site_icon_url( 180 ); 3905 if ( $icon_180 ) { 3906 $meta_tags[] = sprintf( '<link rel="apple-touch-icon" href="%s" />', esc_url( $icon_180 ) ); 3907 } 3908 $icon_270 = get_site_icon_url( 270 ); 3909 if ( $icon_270 ) { 3910 $meta_tags[] = sprintf( '<meta name="msapplication-TileImage" content="%s" />', esc_url( $icon_270 ) ); 3911 } 3912 3913 /** 3914 * Filters the site icon meta tags, so plugins can add their own. 3915 * 3916 * @since 4.3.0 3917 * 3918 * @param string[] $meta_tags Array of Site Icon meta tags. 3919 */ 3920 $meta_tags = apply_filters( 'site_icon_meta_tags', $meta_tags ); 3921 $meta_tags = array_filter( $meta_tags ); 3922 3923 foreach ( $meta_tags as $meta_tag ) { 3924 echo "$meta_tag\n"; 3925 } 3926 } 3927 3928 /** 3929 * Prints resource hints to browsers for pre-fetching, pre-rendering 3930 * and pre-connecting to websites. 3931 * 3932 * Gives hints to browsers to prefetch specific pages or render them 3933 * in the background, to perform DNS lookups or to begin the connection 3934 * handshake (DNS, TCP, TLS) in the background. 3935 * 3936 * These performance improving indicators work by using `<link rel"…">`. 3937 * 3938 * @since 4.6.0 3939 */ 3940 function wp_resource_hints() { 3941 $hints = array( 3942 'dns-prefetch' => wp_dependencies_unique_hosts(), 3943 'preconnect' => array(), 3944 'prefetch' => array(), 3945 'prerender' => array(), 3946 ); 3947 3948 foreach ( $hints as $relation_type => $urls ) { 3949 $unique_urls = array(); 3950 3951 /** 3952 * Filters domains and URLs for resource hints of the given relation type. 3953 * 3954 * @since 4.6.0 3955 * @since 4.7.0 The `$urls` parameter accepts arrays of specific HTML attributes 3956 * as its child elements. 3957 * 3958 * @param array $urls { 3959 * Array of resources and their attributes, or URLs to print for resource hints. 3960 * 3961 * @type array|string ...$0 { 3962 * Array of resource attributes, or a URL string. 3963 * 3964 * @type string $href URL to include in resource hints. Required. 3965 * @type string $as How the browser should treat the resource 3966 * (`script`, `style`, `image`, `document`, etc). 3967 * @type string $crossorigin Indicates the CORS policy of the specified resource. 3968 * @type float $pr Expected probability that the resource hint will be used. 3969 * @type string $type Type of the resource (`text/html`, `text/css`, etc). 3970 * } 3971 * } 3972 * @param string $relation_type The relation type the URLs are printed for. One of 3973 * 'dns-prefetch', 'preconnect', 'prefetch', or 'prerender'. 3974 */ 3975 $urls = apply_filters( 'wp_resource_hints', $urls, $relation_type ); 3976 3977 foreach ( $urls as $key => $url ) { 3978 $atts = array(); 3979 3980 if ( is_array( $url ) ) { 3981 if ( isset( $url['href'] ) ) { 3982 $atts = $url; 3983 $url = $url['href']; 3984 } else { 3985 continue; 3986 } 3987 } 3988 3989 $url = esc_url( $url, array( 'http', 'https' ) ); 3990 3991 if ( ! $url ) { 3992 continue; 3993 } 3994 3995 if ( isset( $unique_urls[ $url ] ) ) { 3996 continue; 3997 } 3998 3999 if ( in_array( $relation_type, array( 'preconnect', 'dns-prefetch' ), true ) ) { 4000 $parsed = wp_parse_url( $url ); 4001 4002 if ( empty( $parsed['host'] ) ) { 4003 continue; 4004 } 4005 4006 if ( 'preconnect' === $relation_type && ! empty( $parsed['scheme'] ) ) { 4007 $url = $parsed['scheme'] . '://' . $parsed['host']; 4008 } else { 4009 // Use protocol-relative URLs for dns-prefetch or if scheme is missing. 4010 $url = '//' . $parsed['host']; 4011 } 4012 } 4013 4014 $atts['rel'] = $relation_type; 4015 $atts['href'] = $url; 4016 4017 $unique_urls[ $url ] = $atts; 4018 } 4019 4020 foreach ( $unique_urls as $atts ) { 4021 $html = ''; 4022 4023 foreach ( $atts as $attr => $value ) { 4024 if ( ! is_scalar( $value ) 4025 || ( ! in_array( $attr, array( 'as', 'crossorigin', 'href', 'pr', 'rel', 'type' ), true ) && ! is_numeric( $attr ) ) 4026 ) { 4027 4028 continue; 4029 } 4030 4031 $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); 4032 4033 if ( ! is_string( $attr ) ) { 4034 $html .= " $value"; 4035 } else { 4036 $html .= " $attr='$value'"; 4037 } 4038 } 4039 4040 $html = trim( $html ); 4041 4042 echo "<link $html />\n"; 4043 } 4044 } 4045 } 4046 4047 /** 4048 * Prints resource preloads directives to browsers. 4049 * 4050 * Gives directive to browsers to preload specific resources that website will 4051 * need very soon, this ensures that they are available earlier and are less 4052 * likely to block the page's render. Preload directives should not be used for 4053 * non-render-blocking elements, as then they would compete with the 4054 * render-blocking ones, slowing down the render. 4055 * 4056 * These performance improving indicators work by using `<link rel="preload">`. 4057 * 4058 * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload 4059 * @link https://web.dev/preload-responsive-images/ 4060 * 4061 * @since 6.1.0 4062 */ 4063 function wp_preload_resources() { 4064 /** 4065 * Filters domains and URLs for resource preloads. 4066 * 4067 * @since 6.1.0 4068 * @since 6.6.0 Added the `$fetchpriority` attribute. 4069 * 4070 * @param array $preload_resources { 4071 * Array of resources and their attributes, or URLs to print for resource preloads. 4072 * 4073 * @type array ...$0 { 4074 * Array of resource attributes. 4075 * 4076 * @type string $href URL to include in resource preloads. Required. 4077 * @type string $as How the browser should treat the resource 4078 * (`script`, `style`, `image`, `document`, etc). 4079 * @type string $crossorigin Indicates the CORS policy of the specified resource. 4080 * @type string $type Type of the resource (`text/html`, `text/css`, etc). 4081 * @type string $media Accepts media types or media queries. Allows responsive preloading. 4082 * @type string $imagesizes Responsive source size to the source Set. 4083 * @type string $imagesrcset Responsive image sources to the source set. 4084 * @type string $fetchpriority Fetchpriority value for the resource. 4085 * } 4086 * } 4087 */ 4088 $preload_resources = apply_filters( 'wp_preload_resources', array() ); 4089 4090 if ( ! is_array( $preload_resources ) ) { 4091 return; 4092 } 4093 4094 $unique_resources = array(); 4095 4096 // Parse the complete resource list and extract unique resources. 4097 foreach ( $preload_resources as $resource ) { 4098 if ( ! is_array( $resource ) ) { 4099 continue; 4100 } 4101 4102 $attributes = $resource; 4103 if ( isset( $resource['href'] ) ) { 4104 $href = $resource['href']; 4105 if ( isset( $unique_resources[ $href ] ) ) { 4106 continue; 4107 } 4108 $unique_resources[ $href ] = $attributes; 4109 // Media can use imagesrcset and not href. 4110 } elseif ( ( 'image' === $resource['as'] ) && 4111 ( isset( $resource['imagesrcset'] ) || isset( $resource['imagesizes'] ) ) 4112 ) { 4113 if ( isset( $unique_resources[ $resource['imagesrcset'] ] ) ) { 4114 continue; 4115 } 4116 $unique_resources[ $resource['imagesrcset'] ] = $attributes; 4117 } else { 4118 continue; 4119 } 4120 } 4121 4122 // Build and output the HTML for each unique resource. 4123 foreach ( $unique_resources as $unique_resource ) { 4124 $html = ''; 4125 4126 foreach ( $unique_resource as $resource_key => $resource_value ) { 4127 if ( ! is_scalar( $resource_value ) ) { 4128 continue; 4129 } 4130 4131 // Ignore non-supported attributes. 4132 $non_supported_attributes = array( 'as', 'crossorigin', 'href', 'imagesrcset', 'imagesizes', 'type', 'media', 'fetchpriority' ); 4133 if ( ! in_array( $resource_key, $non_supported_attributes, true ) && ! is_numeric( $resource_key ) ) { 4134 continue; 4135 } 4136 4137 // imagesrcset only usable when preloading image, ignore otherwise. 4138 if ( ( 'imagesrcset' === $resource_key ) && ( ! isset( $unique_resource['as'] ) || ( 'image' !== $unique_resource['as'] ) ) ) { 4139 continue; 4140 } 4141 4142 // imagesizes only usable when preloading image and imagesrcset present, ignore otherwise. 4143 if ( ( 'imagesizes' === $resource_key ) && 4144 ( ! isset( $unique_resource['as'] ) || ( 'image' !== $unique_resource['as'] ) || ! isset( $unique_resource['imagesrcset'] ) ) 4145 ) { 4146 continue; 4147 } 4148 4149 $resource_value = ( 'href' === $resource_key ) ? esc_url( $resource_value, array( 'http', 'https' ) ) : esc_attr( $resource_value ); 4150 4151 if ( ! is_string( $resource_key ) ) { 4152 $html .= " $resource_value"; 4153 } else { 4154 $html .= " $resource_key='$resource_value'"; 4155 } 4156 } 4157 $html = trim( $html ); 4158 4159 printf( "<link rel='preload' %s />\n", $html ); 4160 } 4161 } 4162 4163 /** 4164 * Retrieves a list of unique hosts of all enqueued scripts and styles. 4165 * 4166 * @since 4.6.0 4167 * 4168 * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. 4169 * @global WP_Styles $wp_styles The WP_Styles object for printing styles. 4170 * 4171 * @return string[] A list of unique hosts of enqueued scripts and styles. 4172 */ 4173 function wp_dependencies_unique_hosts() { 4174 global $wp_scripts, $wp_styles; 4175 4176 $unique_hosts = array(); 4177 4178 foreach ( array( $wp_scripts, $wp_styles ) as $dependencies ) { 4179 if ( $dependencies instanceof WP_Dependencies && ! empty( $dependencies->queue ) ) { 4180 foreach ( $dependencies->queue as $handle ) { 4181 if ( ! isset( $dependencies->registered[ $handle ] ) ) { 4182 continue; 4183 } 4184 4185 /* @var _WP_Dependency $dependency */ 4186 $dependency = $dependencies->registered[ $handle ]; 4187 $parsed = wp_parse_url( $dependency->src ); 4188 4189 if ( ! empty( $parsed['host'] ) 4190 && ! in_array( $parsed['host'], $unique_hosts, true ) && $parsed['host'] !== $_SERVER['SERVER_NAME'] 4191 ) { 4192 $unique_hosts[] = $parsed['host']; 4193 } 4194 } 4195 } 4196 } 4197 4198 return $unique_hosts; 4199 } 4200 4201 /** 4202 * Determines whether the user can access the visual editor. 4203 * 4204 * Checks if the user can access the visual editor and that it's supported by the user's browser. 4205 * 4206 * @since 2.0.0 4207 * 4208 * @global bool $wp_rich_edit Whether the user can access the visual editor. 4209 * @global bool $is_gecko Whether the browser is Gecko-based. 4210 * @global bool $is_opera Whether the browser is Opera. 4211 * @global bool $is_safari Whether the browser is Safari. 4212 * @global bool $is_chrome Whether the browser is Chrome. 4213 * @global bool $is_IE Whether the browser is Internet Explorer. 4214 * @global bool $is_edge Whether the browser is Microsoft Edge. 4215 * 4216 * @return bool True if the user can access the visual editor, false otherwise. 4217 */ 4218 function user_can_richedit() { 4219 global $wp_rich_edit, $is_gecko, $is_opera, $is_safari, $is_chrome, $is_IE, $is_edge; 4220 4221 if ( ! isset( $wp_rich_edit ) ) { 4222 $wp_rich_edit = false; 4223 4224 if ( 'true' === get_user_option( 'rich_editing' ) || ! is_user_logged_in() ) { // Default to 'true' for logged out users. 4225 if ( $is_safari ) { 4226 $wp_rich_edit = ! wp_is_mobile() || ( preg_match( '!AppleWebKit/(\d+)!', $_SERVER['HTTP_USER_AGENT'], $match ) && (int) $match[1] >= 534 ); 4227 } elseif ( $is_IE ) { 4228 $wp_rich_edit = str_contains( $_SERVER['HTTP_USER_AGENT'], 'Trident/7.0;' ); 4229 } elseif ( $is_gecko || $is_chrome || $is_edge || ( $is_opera && ! wp_is_mobile() ) ) { 4230 $wp_rich_edit = true; 4231 } 4232 } 4233 } 4234 4235 /** 4236 * Filters whether the user can access the visual editor. 4237 * 4238 * @since 2.1.0 4239 * 4240 * @param bool $wp_rich_edit Whether the user can access the visual editor. 4241 */ 4242 return apply_filters( 'user_can_richedit', $wp_rich_edit ); 4243 } 4244 4245 /** 4246 * Finds out which editor should be displayed by default. 4247 * 4248 * Works out which of the editors to display as the current editor for a 4249 * user. The 'html' setting is for the "Code" editor tab. 4250 * 4251 * @since 2.5.0 4252 * 4253 * @return string Either 'tinymce', 'html', or 'test' 4254 */ 4255 function wp_default_editor() { 4256 $r = user_can_richedit() ? 'tinymce' : 'html'; // Defaults. 4257 if ( wp_get_current_user() ) { // Look for cookie. 4258 $ed = get_user_setting( 'editor', 'tinymce' ); 4259 $r = ( in_array( $ed, array( 'tinymce', 'html', 'test' ), true ) ) ? $ed : $r; 4260 } 4261 4262 /** 4263 * Filters which editor should be displayed by default. 4264 * 4265 * @since 2.5.0 4266 * 4267 * @param string $r Which editor should be displayed by default. Either 'tinymce', 'html', or 'test'. 4268 */ 4269 return apply_filters( 'wp_default_editor', $r ); 4270 } 4271 4272 /** 4273 * Renders an editor. 4274 * 4275 * Using this function is the proper way to output all needed components for both TinyMCE and Quicktags. 4276 * _WP_Editors should not be used directly. See https://core.trac.wordpress.org/ticket/17144. 4277 * 4278 * NOTE: Once initialized the TinyMCE editor cannot be safely moved in the DOM. For that reason 4279 * running wp_editor() inside of a meta box is not a good idea unless only Quicktags is used. 4280 * On the post edit screen several actions can be used to include additional editors 4281 * containing TinyMCE: 'edit_page_form', 'edit_form_advanced' and 'dbx_post_sidebar'. 4282 * See https://core.trac.wordpress.org/ticket/19173 for more information. 4283 * 4284 * @see _WP_Editors::editor() 4285 * @see _WP_Editors::parse_settings() 4286 * @since 3.3.0 4287 * 4288 * @param string $content Initial content for the editor. 4289 * @param string $editor_id HTML ID attribute value for the textarea and TinyMCE. 4290 * Should not contain square brackets. 4291 * @param array $settings See _WP_Editors::parse_settings() for description. 4292 */ 4293 function wp_editor( $content, $editor_id, $settings = array() ) { 4294 if ( ! class_exists( '_WP_Editors', false ) ) { 4295 require ABSPATH . WPINC . '/class-wp-editor.php'; 4296 } 4297 _WP_Editors::editor( $content, $editor_id, $settings ); 4298 } 4299 4300 /** 4301 * Outputs the editor scripts, stylesheets, and default settings. 4302 * 4303 * The editor can be initialized when needed after page load. 4304 * See wp.editor.initialize() in wp-admin/js/editor.js for initialization options. 4305 * 4306 * @uses _WP_Editors 4307 * @since 4.8.0 4308 */ 4309 function wp_enqueue_editor() { 4310 if ( ! class_exists( '_WP_Editors', false ) ) { 4311 require ABSPATH . WPINC . '/class-wp-editor.php'; 4312 } 4313 4314 _WP_Editors::enqueue_default_editor(); 4315 } 4316 4317 /** 4318 * Enqueues assets needed by the code editor for the given settings. 4319 * 4320 * @since 4.9.0 4321 * 4322 * @see wp_enqueue_editor() 4323 * @see wp_get_code_editor_settings() 4324 * @see _WP_Editors::parse_settings() 4325 * 4326 * @param array $args { 4327 * Args. 4328 * 4329 * @type string $type The MIME type of the file to be edited. 4330 * @type string $file Filename to be edited. Extension is used to sniff the type. Can be supplied as alternative to `$type` param. 4331 * @type WP_Theme $theme Theme being edited when on the theme file editor. 4332 * @type string $plugin Plugin being edited when on the plugin file editor. 4333 * @type array $codemirror Additional CodeMirror setting overrides. 4334 * @type array $csslint CSSLint rule overrides. 4335 * @type array $jshint JSHint rule overrides. 4336 * @type array $htmlhint HTMLHint rule overrides. 4337 * } 4338 * @return array|false Settings for the enqueued code editor, or false if the editor was not enqueued. 4339 */ 4340 function wp_enqueue_code_editor( $args ) { 4341 if ( is_user_logged_in() && 'false' === wp_get_current_user()->syntax_highlighting ) { 4342 return false; 4343 } 4344 4345 $settings = wp_get_code_editor_settings( $args ); 4346 4347 if ( empty( $settings ) || empty( $settings['codemirror'] ) ) { 4348 return false; 4349 } 4350 4351 wp_enqueue_script( 'code-editor' ); 4352 wp_enqueue_style( 'code-editor' ); 4353 4354 if ( isset( $settings['codemirror']['mode'] ) ) { 4355 $mode = $settings['codemirror']['mode']; 4356 if ( is_string( $mode ) ) { 4357 $mode = array( 4358 'name' => $mode, 4359 ); 4360 } 4361 4362 if ( ! empty( $settings['codemirror']['lint'] ) ) { 4363 switch ( $mode['name'] ) { 4364 case 'css': 4365 case 'text/css': 4366 case 'text/x-scss': 4367 case 'text/x-less': 4368 wp_enqueue_script( 'csslint' ); 4369 break; 4370 case 'htmlmixed': 4371 case 'text/html': 4372 case 'php': 4373 case 'application/x-httpd-php': 4374 case 'text/x-php': 4375 wp_enqueue_script( 'htmlhint' ); 4376 wp_enqueue_script( 'csslint' ); 4377 if ( ! current_user_can( 'unfiltered_html' ) ) { 4378 wp_enqueue_script( 'htmlhint-kses' ); 4379 } 4380 break; 4381 case 'javascript': 4382 case 'application/ecmascript': 4383 case 'application/json': 4384 case 'application/javascript': 4385 case 'application/ld+json': 4386 case 'text/typescript': 4387 case 'application/typescript': 4388 wp_enqueue_script( 'jsonlint' ); 4389 break; 4390 } 4391 } 4392 } 4393 4394 wp_add_inline_script( 'code-editor', sprintf( 'jQuery.extend( wp.codeEditor.defaultSettings, %s );', wp_json_encode( $settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) ) ); 4395 4396 /** 4397 * Fires when scripts and styles are enqueued for the code editor. 4398 * 4399 * @since 4.9.0 4400 * 4401 * @param array $settings Settings for the enqueued code editor. 4402 */ 4403 do_action( 'wp_enqueue_code_editor', $settings ); 4404 4405 return $settings; 4406 } 4407 4408 /** 4409 * Generates and returns code editor settings. 4410 * 4411 * @since 5.0.0 4412 * 4413 * @see wp_enqueue_code_editor() 4414 * 4415 * @param array $args { 4416 * Args. 4417 * 4418 * @type string $type The MIME type of the file to be edited. 4419 * @type string $file Filename to be edited. Extension is used to sniff the type. Can be supplied as alternative to `$type` param. 4420 * @type WP_Theme $theme Theme being edited when on the theme file editor. 4421 * @type string $plugin Plugin being edited when on the plugin file editor. 4422 * @type array $codemirror Additional CodeMirror setting overrides. 4423 * @type array $csslint CSSLint rule overrides. 4424 * @type array $jshint JSHint rule overrides. 4425 * @type array $htmlhint HTMLHint rule overrides. 4426 * } 4427 * @return array|false Settings for the code editor. 4428 */ 4429 function wp_get_code_editor_settings( $args ) { 4430 $settings = array( 4431 'codemirror' => array( 4432 'indentUnit' => 4, 4433 'indentWithTabs' => true, 4434 'inputStyle' => 'contenteditable', 4435 'lineNumbers' => true, 4436 'lineWrapping' => true, 4437 'styleActiveLine' => true, 4438 'continueComments' => true, 4439 'extraKeys' => array( 4440 'Ctrl-Space' => 'autocomplete', 4441 'Ctrl-/' => 'toggleComment', 4442 'Cmd-/' => 'toggleComment', 4443 'Alt-F' => 'findPersistent', 4444 'Ctrl-F' => 'findPersistent', 4445 'Cmd-F' => 'findPersistent', 4446 ), 4447 'direction' => 'ltr', // Code is shown in LTR even in RTL languages. 4448 'gutters' => array(), 4449 ), 4450 'csslint' => array( 4451 'errors' => true, // Parsing errors. 4452 'box-model' => true, 4453 'display-property-grouping' => true, 4454 'duplicate-properties' => true, 4455 'known-properties' => true, 4456 'outline-none' => true, 4457 ), 4458 'jshint' => array( 4459 'esversion' => 11, 4460 'module' => str_ends_with( $args['file'] ?? '', '.mjs' ), 4461 4462 // This script module URL is intentionally referenced here instead of registering an espree script module 4463 // in wp_default_script_modules(). This is a first stab at a core-only private module. 4464 'espreeModuleUrl' => add_query_arg( 'ver', '9.6.1', includes_url( 'js/codemirror/espree.min.js' ) ), 4465 4466 // The following JSHint *linting rule* options are copied from 4467 // <https://github.com/WordPress/wordpress-develop/blob/6.9.0/.jshintrc>. 4468 // Parsing-related options such as `esversion` (and, in other contexts, `es5`, `es3`, `module`, `strict`) 4469 // are honored by the Espree-based integration, but these linting-rule options are not interpreted by Espree 4470 // and are kept only for compatibility/documentation with the original JSHint configuration. 4471 'boss' => true, 4472 'curly' => true, 4473 'eqeqeq' => true, 4474 'eqnull' => true, 4475 'expr' => true, 4476 'immed' => true, 4477 'noarg' => true, 4478 'nonbsp' => true, 4479 'quotmark' => 'single', 4480 'undef' => true, 4481 'unused' => true, 4482 'browser' => true, 4483 'globals' => array( 4484 '_' => false, 4485 'Backbone' => false, 4486 'jQuery' => false, 4487 'JSON' => false, 4488 'wp' => false, 4489 'export' => false, 4490 'module' => false, 4491 'require' => false, 4492 'WorkerGlobalScope' => false, 4493 'self' => false, 4494 'OffscreenCanvas' => false, 4495 'Promise' => false, 4496 ), 4497 ), 4498 'htmlhint' => array( 4499 'tagname-lowercase' => true, 4500 'attr-lowercase' => true, 4501 'attr-value-double-quotes' => false, 4502 'doctype-first' => false, 4503 'tag-pair' => true, 4504 'spec-char-escape' => true, 4505 'id-unique' => true, 4506 'src-not-empty' => true, 4507 'attr-no-duplication' => true, 4508 'alt-require' => true, 4509 'space-tab-mixed-disabled' => 'tab', 4510 'attr-unsafe-chars' => true, 4511 ), 4512 ); 4513 4514 $type = ''; 4515 if ( isset( $args['type'] ) ) { 4516 $type = $args['type']; 4517 4518 // Remap MIME types to ones that CodeMirror modes will recognize. 4519 if ( 'application/x-patch' === $type || 'text/x-patch' === $type ) { 4520 $type = 'text/x-diff'; 4521 } 4522 } elseif ( isset( $args['file'] ) && str_contains( basename( $args['file'] ), '.' ) ) { 4523 $extension = strtolower( pathinfo( $args['file'], PATHINFO_EXTENSION ) ); 4524 foreach ( wp_get_mime_types() as $exts => $mime ) { 4525 if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) { 4526 $type = $mime; 4527 break; 4528 } 4529 } 4530 4531 // Supply any types that are not matched by wp_get_mime_types(). 4532 if ( empty( $type ) ) { 4533 switch ( $extension ) { 4534 case 'conf': 4535 $type = 'text/nginx'; 4536 break; 4537 case 'css': 4538 $type = 'text/css'; 4539 break; 4540 case 'diff': 4541 case 'patch': 4542 $type = 'text/x-diff'; 4543 break; 4544 case 'html': 4545 case 'htm': 4546 $type = 'text/html'; 4547 break; 4548 case 'http': 4549 $type = 'message/http'; 4550 break; 4551 case 'js': 4552 case 'mjs': 4553 $type = 'text/javascript'; 4554 break; 4555 case 'json': 4556 $type = 'application/json'; 4557 break; 4558 case 'jsx': 4559 $type = 'text/jsx'; 4560 break; 4561 case 'less': 4562 $type = 'text/x-less'; 4563 break; 4564 case 'md': 4565 $type = 'text/x-gfm'; 4566 break; 4567 case 'php': 4568 case 'phtml': 4569 case 'php3': 4570 case 'php4': 4571 case 'php5': 4572 case 'php7': 4573 case 'phps': 4574 $type = 'application/x-httpd-php'; 4575 break; 4576 case 'scss': 4577 $type = 'text/x-scss'; 4578 break; 4579 case 'sass': 4580 $type = 'text/x-sass'; 4581 break; 4582 case 'sh': 4583 case 'bash': 4584 $type = 'text/x-sh'; 4585 break; 4586 case 'sql': 4587 $type = 'text/x-sql'; 4588 break; 4589 case 'svg': 4590 $type = 'application/svg+xml'; 4591 break; 4592 case 'xml': 4593 $type = 'text/xml'; 4594 break; 4595 case 'yml': 4596 case 'yaml': 4597 $type = 'text/x-yaml'; 4598 break; 4599 case 'txt': 4600 default: 4601 $type = 'text/plain'; 4602 break; 4603 } 4604 } 4605 } 4606 4607 if ( in_array( $type, array( 'text/css', 'text/x-scss', 'text/x-less', 'text/x-sass' ), true ) ) { 4608 $settings['codemirror'] = array_merge( 4609 $settings['codemirror'], 4610 array( 4611 'mode' => $type, 4612 'lint' => false, 4613 'autoCloseBrackets' => true, 4614 'matchBrackets' => true, 4615 ) 4616 ); 4617 } elseif ( 'text/x-diff' === $type ) { 4618 $settings['codemirror'] = array_merge( 4619 $settings['codemirror'], 4620 array( 4621 'mode' => 'diff', 4622 ) 4623 ); 4624 } elseif ( 'text/html' === $type ) { 4625 $settings['codemirror'] = array_merge( 4626 $settings['codemirror'], 4627 array( 4628 'mode' => 'htmlmixed', 4629 'lint' => true, 4630 'autoCloseBrackets' => true, 4631 'autoCloseTags' => true, 4632 'matchTags' => array( 4633 'bothTags' => true, 4634 ), 4635 ) 4636 ); 4637 4638 if ( ! current_user_can( 'unfiltered_html' ) ) { 4639 $settings['htmlhint']['kses'] = wp_kses_allowed_html( 'post' ); 4640 } 4641 } elseif ( 'text/x-gfm' === $type ) { 4642 $settings['codemirror'] = array_merge( 4643 $settings['codemirror'], 4644 array( 4645 'mode' => 'gfm', 4646 'highlightFormatting' => true, 4647 ) 4648 ); 4649 } elseif ( 'application/javascript' === $type || 'text/javascript' === $type ) { 4650 $settings['codemirror'] = array_merge( 4651 $settings['codemirror'], 4652 array( 4653 'mode' => 'javascript', 4654 'lint' => true, 4655 'autoCloseBrackets' => true, 4656 'matchBrackets' => true, 4657 ) 4658 ); 4659 } elseif ( str_contains( $type, 'json' ) ) { 4660 $settings['codemirror'] = array_merge( 4661 $settings['codemirror'], 4662 array( 4663 'mode' => array( 4664 'name' => 'javascript', 4665 ), 4666 'lint' => true, 4667 'autoCloseBrackets' => true, 4668 'matchBrackets' => true, 4669 ) 4670 ); 4671 if ( 'application/ld+json' === $type ) { 4672 $settings['codemirror']['mode']['jsonld'] = true; 4673 } else { 4674 $settings['codemirror']['mode']['json'] = true; 4675 } 4676 } elseif ( str_contains( $type, 'jsx' ) ) { 4677 $settings['codemirror'] = array_merge( 4678 $settings['codemirror'], 4679 array( 4680 'mode' => 'jsx', 4681 'autoCloseBrackets' => true, 4682 'matchBrackets' => true, 4683 ) 4684 ); 4685 } elseif ( 'text/x-markdown' === $type ) { 4686 $settings['codemirror'] = array_merge( 4687 $settings['codemirror'], 4688 array( 4689 'mode' => 'markdown', 4690 'highlightFormatting' => true, 4691 ) 4692 ); 4693 } elseif ( 'text/nginx' === $type ) { 4694 $settings['codemirror'] = array_merge( 4695 $settings['codemirror'], 4696 array( 4697 'mode' => 'nginx', 4698 ) 4699 ); 4700 } elseif ( 'application/x-httpd-php' === $type ) { 4701 $settings['codemirror'] = array_merge( 4702 $settings['codemirror'], 4703 array( 4704 'mode' => 'php', 4705 'autoCloseBrackets' => true, 4706 'autoCloseTags' => true, 4707 'matchBrackets' => true, 4708 'matchTags' => array( 4709 'bothTags' => true, 4710 ), 4711 ) 4712 ); 4713 } elseif ( 'text/x-sql' === $type || 'text/x-mysql' === $type ) { 4714 $settings['codemirror'] = array_merge( 4715 $settings['codemirror'], 4716 array( 4717 'mode' => 'sql', 4718 'autoCloseBrackets' => true, 4719 'matchBrackets' => true, 4720 ) 4721 ); 4722 } elseif ( str_contains( $type, 'xml' ) ) { 4723 $settings['codemirror'] = array_merge( 4724 $settings['codemirror'], 4725 array( 4726 'mode' => 'xml', 4727 'autoCloseBrackets' => true, 4728 'autoCloseTags' => true, 4729 'matchTags' => array( 4730 'bothTags' => true, 4731 ), 4732 ) 4733 ); 4734 } elseif ( 'text/x-yaml' === $type ) { 4735 $settings['codemirror'] = array_merge( 4736 $settings['codemirror'], 4737 array( 4738 'mode' => 'yaml', 4739 ) 4740 ); 4741 } else { 4742 $settings['codemirror']['mode'] = $type; 4743 } 4744 4745 if ( ! empty( $settings['codemirror']['lint'] ) ) { 4746 $settings['codemirror']['gutters'][] = 'CodeMirror-lint-markers'; 4747 } 4748 4749 // Let settings supplied via args override any defaults. 4750 foreach ( wp_array_slice_assoc( $args, array( 'codemirror', 'csslint', 'jshint', 'htmlhint' ) ) as $key => $value ) { 4751 $settings[ $key ] = array_merge( 4752 $settings[ $key ], 4753 $value 4754 ); 4755 } 4756 4757 /** 4758 * Filters settings that are passed into the code editor. 4759 * 4760 * Returning a falsey value will disable the syntax-highlighting code editor. 4761 * 4762 * @since 4.9.0 4763 * 4764 * @param array $settings The array of settings passed to the code editor. 4765 * A falsey value disables the editor. 4766 * @param array $args { 4767 * Args passed when calling `get_code_editor_settings()`. 4768 * 4769 * @type string $type The MIME type of the file to be edited. 4770 * @type string $file Filename being edited. 4771 * @type WP_Theme $theme Theme being edited when on the theme file editor. 4772 * @type string $plugin Plugin being edited when on the plugin file editor. 4773 * @type array $codemirror Additional CodeMirror setting overrides. 4774 * @type array $csslint CSSLint rule overrides. 4775 * @type array $jshint JSHint rule overrides. 4776 * @type array $htmlhint HTMLHint rule overrides. 4777 * } 4778 */ 4779 return apply_filters( 'wp_code_editor_settings', $settings, $args ); 4780 } 4781 4782 /** 4783 * Retrieves the contents of the search WordPress query variable. 4784 * 4785 * The search query string is passed through esc_attr() to ensure that it is safe 4786 * for placing in an HTML attribute. 4787 * 4788 * @since 2.3.0 4789 * 4790 * @param bool $escaped Whether the result is escaped. Default true. 4791 * Only use when you are later escaping it. Do not use unescaped. 4792 * @return string 4793 */ 4794 function get_search_query( $escaped = true ) { 4795 /** 4796 * Filters the contents of the search query variable. 4797 * 4798 * @since 2.3.0 4799 * 4800 * @param mixed $search Contents of the search query variable. 4801 */ 4802 $query = apply_filters( 'get_search_query', get_query_var( 's' ) ); 4803 4804 if ( $escaped ) { 4805 $query = esc_attr( $query ); 4806 } 4807 return $query; 4808 } 4809 4810 /** 4811 * Displays the contents of the search query variable. 4812 * 4813 * The search query string is passed through esc_attr() to ensure that it is safe 4814 * for placing in an HTML attribute. 4815 * 4816 * @since 2.1.0 4817 */ 4818 function the_search_query() { 4819 /** 4820 * Filters the contents of the search query variable, for display. 4821 * 4822 * @since 2.3.0 4823 * 4824 * @param mixed $search Contents of the search query variable. 4825 */ 4826 echo esc_attr( apply_filters( 'the_search_query', get_search_query( false ) ) ); 4827 } 4828 4829 /** 4830 * Gets the language attributes for the 'html' tag. 4831 * 4832 * Builds up a set of HTML attributes containing the text direction and language 4833 * information for the page. 4834 * 4835 * @since 4.3.0 4836 * 4837 * @param string $doctype Optional. The type of HTML document. Accepts 'xhtml' or 'html'. Default 'html'. 4838 * @return string A space-separated list of language attributes. 4839 */ 4840 function get_language_attributes( $doctype = 'html' ) { 4841 $attributes = array(); 4842 4843 if ( function_exists( 'is_rtl' ) && is_rtl() ) { 4844 $attributes[] = 'dir="rtl"'; 4845 } 4846 4847 $lang = get_bloginfo( 'language' ); 4848 $html_type = get_option( 'html_type' ); 4849 4850 if ( $lang ) { 4851 if ( 'text/html' === $html_type || 'html' === $doctype ) { 4852 $attributes[] = 'lang="' . esc_attr( $lang ) . '"'; 4853 } 4854 4855 // The $html_type option may be false on a new install on the setup-config.php page. 4856 if ( ( $html_type && 'text/html' !== $html_type ) || 'xhtml' === $doctype ) { 4857 $attributes[] = 'xml:lang="' . esc_attr( $lang ) . '"'; 4858 } 4859 } 4860 4861 $output = implode( ' ', $attributes ); 4862 4863 /** 4864 * Filters the language attributes for display in the 'html' tag. 4865 * 4866 * @since 2.5.0 4867 * @since 4.3.0 Added the `$doctype` parameter. 4868 * 4869 * @param string $output A space-separated list of language attributes. 4870 * @param string $doctype The type of HTML document (xhtml|html). 4871 */ 4872 return apply_filters( 'language_attributes', $output, $doctype ); 4873 } 4874 4875 /** 4876 * Displays the language attributes for the 'html' tag. 4877 * 4878 * Builds up a set of HTML attributes containing the text direction and language 4879 * information for the page. 4880 * 4881 * @since 2.1.0 4882 * @since 4.3.0 Converted into a wrapper for get_language_attributes(). 4883 * 4884 * @param string $doctype Optional. The type of HTML document. Accepts 'xhtml' or 'html'. Default 'html'. 4885 */ 4886 function language_attributes( $doctype = 'html' ) { 4887 echo get_language_attributes( $doctype ); 4888 } 4889 4890 /** 4891 * Retrieves paginated links for archive post pages. 4892 * 4893 * Technically, the function can be used to create paginated link list for any 4894 * area. The 'base' argument is used to reference the url, which will be used to 4895 * create the paginated links. The 'format' argument is then used for replacing 4896 * the page number. It is however, most likely and by default, to be used on the 4897 * archive post pages. 4898 * 4899 * The 'type' argument controls format of the returned value. The default is 4900 * 'plain', which is just a string with the links separated by a newline 4901 * character. The other possible values are either 'array' or 'list'. The 4902 * 'array' value will return an array of the paginated link list to offer full 4903 * control of display. The 'list' value will place all of the paginated links in 4904 * an unordered HTML list. 4905 * 4906 * The 'total' argument is the total amount of pages and is an integer. The 4907 * 'current' argument is the current page number and is also an integer. 4908 * 4909 * An example of the 'base' argument is "http://example.com/all_posts.php%_%" 4910 * and the '%_%' is required. The '%_%' will be replaced by the contents of in 4911 * the 'format' argument. An example for the 'format' argument is "?page=%#%" 4912 * and the '%#%' is also required. The '%#%' will be replaced with the page 4913 * number. 4914 * 4915 * You can include the previous and next links in the list by setting the 4916 * 'prev_next' argument to true, which it is by default. You can set the 4917 * previous text, by using the 'prev_text' argument. You can set the next text 4918 * by setting the 'next_text' argument. 4919 * 4920 * If the 'show_all' argument is set to true, then it will show all of the pages 4921 * instead of a short list of the pages near the current page. By default, the 4922 * 'show_all' is set to false and controlled by the 'end_size' and 'mid_size' 4923 * arguments. The 'end_size' argument is how many numbers on either the start 4924 * and the end list edges, by default is 1. The 'mid_size' argument is how many 4925 * numbers to either side of current page, but not including current page. 4926 * 4927 * It is possible to add query vars to the link by using the 'add_args' argument 4928 * and see add_query_arg() for more information. 4929 * 4930 * The 'before_page_number' and 'after_page_number' arguments allow users to 4931 * augment the links themselves. Typically this might be to add context to the 4932 * numbered links so that screen reader users understand what the links are for. 4933 * The text strings are added before and after the page number - within the 4934 * anchor tag. 4935 * 4936 * @since 2.1.0 4937 * @since 4.9.0 Added the `aria_current` argument. 4938 * 4939 * @global WP_Query $wp_query WordPress Query object. 4940 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. 4941 * 4942 * @param string|array $args { 4943 * Optional. Array or string of arguments for generating paginated links for archives. 4944 * 4945 * @type string $base Base of the paginated url. Default empty. 4946 * @type string $format Format for the pagination structure. Default empty. 4947 * @type int $total The total amount of pages. Default is the value WP_Query's 4948 * `max_num_pages` or 1. 4949 * @type int $current The current page number. Default is 'paged' query var or 1. 4950 * @type string $aria_current The value for the aria-current attribute. Possible values are 'page', 4951 * 'step', 'location', 'date', 'time', 'true', 'false'. Default is 'page'. 4952 * @type bool $show_all Whether to show all pages. Default false. 4953 * @type int $end_size How many numbers on either the start and the end list edges. 4954 * Default 1. 4955 * @type int $mid_size How many numbers to either side of the current pages. Default 2. 4956 * @type bool $prev_next Whether to include the previous and next links in the list. Default true. 4957 * @type string $prev_text The previous page text. Default '« Previous'. 4958 * @type string $next_text The next page text. Default 'Next »'. 4959 * @type string $type Controls format of the returned value. Possible values are 'plain', 4960 * 'array' and 'list'. Default is 'plain'. 4961 * @type array $add_args An array of query args to add. Default false. 4962 * @type string $add_fragment A string to append to each link. Default empty. 4963 * @type string $before_page_number A string to appear before the page number. Default empty. 4964 * @type string $after_page_number A string to append after the page number. Default empty. 4965 * } 4966 * @return string|string[]|null String of page links or array of page links, depending on 'type' argument. 4967 * Null if total number of pages is less than 2. 4968 * 4969 * @phpstan-return ( 4970 * $args is array{ total: int<min, 1>, ... } 4971 * ? null 4972 * : ( 4973 * $args is array{ total: int<2, max>, ... } 4974 * ? ( $args is array{ type: 'array', ... } ? list<string> : string ) 4975 * : ( $args is array{ type: 'array', ... } ? list<string> : string )|null 4976 * ) 4977 * ) 4978 */ 4979 function paginate_links( $args = '' ) { 4980 global $wp_query, $wp_rewrite; 4981 4982 // Setting up default values based on the current URL. 4983 $pagenum_link = html_entity_decode( get_pagenum_link() ); 4984 $url_parts = explode( '?', $pagenum_link ); 4985 4986 // Get max pages and current page out of the current query, if available. 4987 $total = $wp_query->max_num_pages ?? 1; 4988 $current = get_query_var( 'paged' ) ? (int) get_query_var( 'paged' ) : 1; 4989 4990 /* 4991 * Ensures sites not using trailing slashes get links in the form 4992 * `/page/2` rather than `/page/2/`. On these sites, linking to the 4993 * URL with a trailing slash will result in a 301 redirect from the 4994 * incorrect URL to the correctly formatted one. This presents an 4995 * unnecessary performance hit. 4996 */ 4997 if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { 4998 $pagenum_link = untrailingslashit( $url_parts[0] ); 4999 } else { 5000 $pagenum_link = trailingslashit( $url_parts[0] ); 5001 } 5002 $pagenum_link .= '%_%'; 5003 5004 // URL base depends on permalink settings. 5005 $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; 5006 $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; 5007 if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { 5008 $format = '/' . ltrim( $format, '/' ); 5009 } 5010 5011 $defaults = array( 5012 'base' => $pagenum_link, // http://example.com/all_posts.php%_% : %_% is replaced by format (below). 5013 'format' => $format, // ?page=%#% : %#% is replaced by the page number. 5014 'total' => $total, 5015 'current' => $current, 5016 'aria_current' => 'page', 5017 'show_all' => false, 5018 'prev_next' => true, 5019 'prev_text' => __( '« Previous' ), 5020 'next_text' => __( 'Next »' ), 5021 'end_size' => 1, 5022 'mid_size' => 2, 5023 'type' => 'plain', 5024 'add_args' => array(), // Array of query args to add. 5025 'add_fragment' => '', 5026 'before_page_number' => '', 5027 'after_page_number' => '', 5028 ); 5029 5030 $args = wp_parse_args( $args, $defaults ); 5031 5032 if ( ! is_array( $args['add_args'] ) ) { 5033 $args['add_args'] = array(); 5034 } 5035 5036 // Merge additional query vars found in the original URL into 'add_args' array. 5037 if ( isset( $url_parts[1] ) ) { 5038 // Find the format argument. 5039 $format = explode( '?', str_replace( '%_%', $args['format'], $args['base'] ) ); 5040 $format_query = $format[1] ?? ''; 5041 wp_parse_str( $format_query, $format_args ); 5042 5043 // Find the query args of the requested URL. 5044 wp_parse_str( $url_parts[1], $url_query_args ); 5045 5046 // Remove the format argument from the array of query arguments, to avoid overwriting custom format. 5047 foreach ( $format_args as $format_arg => $format_arg_value ) { 5048 unset( $url_query_args[ $format_arg ] ); 5049 } 5050 5051 $args['add_args'] = array_merge( $args['add_args'], urlencode_deep( $url_query_args ) ); 5052 } 5053 5054 // Who knows what else people pass in $args. 5055 $total = (int) $args['total']; 5056 if ( $total < 2 ) { 5057 return null; 5058 } 5059 $current = (int) $args['current']; 5060 $end_size = (int) $args['end_size']; // Out of bounds? Make it the default. 5061 if ( $end_size < 1 ) { 5062 $end_size = 1; 5063 } 5064 $mid_size = (int) $args['mid_size']; 5065 if ( $mid_size < 0 ) { 5066 $mid_size = 2; 5067 } 5068 5069 $add_args = $args['add_args']; 5070 $r = ''; 5071 $page_links = array(); 5072 $dots = false; 5073 5074 if ( $args['prev_next'] && $current && 1 < $current ) : 5075 $link = str_replace( '%_%', 2 === $current ? '' : $args['format'], $args['base'] ); 5076 $link = str_replace( '%#%', $current - 1, $link ); 5077 if ( $add_args ) { 5078 $link = add_query_arg( $add_args, $link ); 5079 } 5080 $link .= $args['add_fragment']; 5081 5082 $page_links[] = sprintf( 5083 '<a class="prev page-numbers" href="%s">%s</a>', 5084 /** 5085 * Filters the paginated links for the given archive pages. 5086 * 5087 * @since 3.0.0 5088 * 5089 * @param string $link The paginated link URL. 5090 */ 5091 esc_url( apply_filters( 'paginate_links', $link ) ), 5092 $args['prev_text'] 5093 ); 5094 endif; 5095 5096 for ( $n = 1; $n <= $total; $n++ ) : 5097 if ( $n === $current ) : 5098 $page_links[] = sprintf( 5099 '<span aria-current="%s" class="page-numbers current">%s</span>', 5100 esc_attr( $args['aria_current'] ), 5101 $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] 5102 ); 5103 5104 $dots = true; 5105 else : 5106 if ( $args['show_all'] || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) : 5107 $link = str_replace( '%_%', 1 === $n ? '' : $args['format'], $args['base'] ); 5108 $link = str_replace( '%#%', $n, $link ); 5109 if ( $add_args ) { 5110 $link = add_query_arg( $add_args, $link ); 5111 } 5112 $link .= $args['add_fragment']; 5113 5114 $page_links[] = sprintf( 5115 '<a class="page-numbers" href="%s">%s</a>', 5116 /** This filter is documented in wp-includes/general-template.php */ 5117 esc_url( apply_filters( 'paginate_links', $link ) ), 5118 $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] 5119 ); 5120 5121 $dots = true; 5122 elseif ( $dots && ! $args['show_all'] ) : 5123 $page_links[] = '<span class="page-numbers dots">' . __( '…' ) . '</span>'; 5124 5125 $dots = false; 5126 endif; 5127 endif; 5128 endfor; 5129 5130 if ( $args['prev_next'] && $current && $current < $total ) : 5131 $link = str_replace( '%_%', $args['format'], $args['base'] ); 5132 $link = str_replace( '%#%', $current + 1, $link ); 5133 if ( $add_args ) { 5134 $link = add_query_arg( $add_args, $link ); 5135 } 5136 $link .= $args['add_fragment']; 5137 5138 $page_links[] = sprintf( 5139 '<a class="next page-numbers" href="%s">%s</a>', 5140 /** This filter is documented in wp-includes/general-template.php */ 5141 esc_url( apply_filters( 'paginate_links', $link ) ), 5142 $args['next_text'] 5143 ); 5144 endif; 5145 5146 switch ( $args['type'] ) { 5147 case 'array': 5148 return $page_links; 5149 5150 case 'list': 5151 $r .= "<ul class='page-numbers'>\n\t<li>"; 5152 $r .= implode( "</li>\n\t<li>", $page_links ); 5153 $r .= "</li>\n</ul>\n"; 5154 break; 5155 5156 default: 5157 $r = implode( "\n", $page_links ); 5158 break; 5159 } 5160 5161 /** 5162 * Filters the HTML output of paginated links for archives. 5163 * 5164 * @since 5.7.0 5165 * 5166 * @param string $r HTML output. 5167 * @param array $args An array of arguments. See paginate_links() 5168 * for information on accepted arguments. 5169 */ 5170 $r = apply_filters( 'paginate_links_output', $r, $args ); 5171 5172 return $r; 5173 } 5174 5175 /** 5176 * Registers an admin color scheme css file. 5177 * 5178 * Allows a plugin to register a new admin color scheme. For example: 5179 * 5180 * wp_admin_css_color( 'classic', __( 'Classic' ), admin_url( "css/colors-classic.css" ), array( 5181 * '#07273E', '#14568A', '#D54E21', '#2683AE' 5182 * ) ); 5183 * 5184 * @since 2.5.0 5185 * 5186 * @global array $_wp_admin_css_colors 5187 * 5188 * @param string $key The unique key for this theme. 5189 * @param string $name The name of the theme. 5190 * @param string $url The URL of the CSS file containing the color scheme. 5191 * @param array $colors Optional. An array of CSS color definition strings which are used 5192 * to give the user a feel for the theme. 5193 * @param array $icons { 5194 * Optional. CSS color definitions used to color any SVG icons. 5195 * 5196 * @type string $base SVG icon base color. 5197 * @type string $focus SVG icon color on focus. 5198 * @type string $current SVG icon color of current admin menu link. 5199 * } 5200 */ 5201 function wp_admin_css_color( $key, $name, $url, $colors = array(), $icons = array() ) { 5202 global $_wp_admin_css_colors; 5203 5204 if ( ! isset( $_wp_admin_css_colors ) ) { 5205 $_wp_admin_css_colors = array(); 5206 } 5207 5208 $_wp_admin_css_colors[ $key ] = (object) array( 5209 'name' => $name, 5210 'url' => $url, 5211 'colors' => $colors, 5212 'icon_colors' => $icons, 5213 ); 5214 } 5215 5216 /** 5217 * Registers the default admin color schemes. 5218 * 5219 * Registers the initial set of eight color schemes in the Profile section 5220 * of the dashboard which allows for styling the admin menu and toolbar. 5221 * 5222 * @see wp_admin_css_color() 5223 * 5224 * @since 3.0.0 5225 */ 5226 function register_admin_color_schemes() { 5227 $suffix = is_rtl() ? '-rtl' : ''; 5228 $suffix .= SCRIPT_DEBUG ? '' : '.min'; 5229 5230 wp_admin_css_color( 5231 'modern', 5232 _x( 'Default', 'admin color scheme' ), 5233 admin_url( "css/colors/modern/colors$suffix.css" ), 5234 array( '#1e1e1e', '#3858e9', '#7b90ff' ), 5235 array( 5236 'base' => '#f3f1f1', 5237 'focus' => '#fff', 5238 'current' => '#fff', 5239 ) 5240 ); 5241 5242 wp_admin_css_color( 5243 'fresh', 5244 _x( 'Fresh', 'admin color scheme' ), 5245 false, 5246 array( '#1d2327', '#2c3338', '#2271b1', '#72aee6' ), 5247 array( 5248 'base' => '#a7aaad', 5249 'focus' => '#72aee6', 5250 'current' => '#fff', 5251 ) 5252 ); 5253 5254 wp_admin_css_color( 5255 'light', 5256 _x( 'Light', 'admin color scheme' ), 5257 admin_url( "css/colors/light/colors$suffix.css" ), 5258 array( '#e5e5e5', '#6a6a6a', '#c64606', '#007cba' ), 5259 array( 5260 'base' => '#999', 5261 'focus' => '#ccc', 5262 'current' => '#ccc', 5263 ) 5264 ); 5265 5266 wp_admin_css_color( 5267 'blue', 5268 _x( 'Blue', 'admin color scheme' ), 5269 admin_url( "css/colors/blue/colors$suffix.css" ), 5270 array( '#183751', '#245278', '#437aa8', '#e1a948' ), 5271 array( 5272 'base' => '#e5f8ff', 5273 'focus' => '#fff', 5274 'current' => '#fff', 5275 ) 5276 ); 5277 5278 wp_admin_css_color( 5279 'midnight', 5280 _x( 'Midnight', 'admin color scheme' ), 5281 admin_url( "css/colors/midnight/colors$suffix.css" ), 5282 array( '#232a2e', '#333c42', '#69a8bb', '#cf4339' ), 5283 array( 5284 'base' => '#f1f2f3', 5285 'focus' => '#fff', 5286 'current' => '#fff', 5287 ) 5288 ); 5289 5290 wp_admin_css_color( 5291 'sunrise', 5292 _x( 'Sunrise', 'admin color scheme' ), 5293 admin_url( "css/colors/sunrise/colors$suffix.css" ), 5294 array( '#6f2724', '#8a312d', '#ad631e', '#ccaf0b' ), 5295 array( 5296 'base' => '#f3f1f1', 5297 'focus' => '#fff', 5298 'current' => '#fff', 5299 ) 5300 ); 5301 5302 wp_admin_css_color( 5303 'ectoplasm', 5304 _x( 'Ectoplasm', 'admin color scheme' ), 5305 admin_url( "css/colors/ectoplasm/colors$suffix.css" ), 5306 array( '#392751', '#4a3369', '#646c3e', '#d46f15' ), 5307 array( 5308 'base' => '#ece6f6', 5309 'focus' => '#fff', 5310 'current' => '#fff', 5311 ) 5312 ); 5313 5314 wp_admin_css_color( 5315 'ocean', 5316 _x( 'Ocean', 'admin color scheme' ), 5317 admin_url( "css/colors/ocean/colors$suffix.css" ), 5318 array( '#2b3f44', '#39535a', '#567958', '#aa9d88' ), 5319 array( 5320 'base' => '#f2fcff', 5321 'focus' => '#fff', 5322 'current' => '#fff', 5323 ) 5324 ); 5325 5326 wp_admin_css_color( 5327 'coffee', 5328 _x( 'Coffee', 'admin color scheme' ), 5329 admin_url( "css/colors/coffee/colors$suffix.css" ), 5330 array( '#382e27', '#5c4c40', '#916745', '#9ea476' ), 5331 array( 5332 'base' => '#f3f2f1', 5333 'focus' => '#fff', 5334 'current' => '#fff', 5335 ) 5336 ); 5337 } 5338 5339 /** 5340 * Displays the URL of a WordPress admin CSS file. 5341 * 5342 * @see WP_Styles::_css_href() and its {@see 'style_loader_src'} filter. 5343 * 5344 * @since 2.3.0 5345 * 5346 * @param string $file file relative to wp-admin/ without its ".css" extension. 5347 * @return string 5348 */ 5349 function wp_admin_css_uri( $file = 'wp-admin' ) { 5350 if ( defined( 'WP_INSTALLING' ) ) { 5351 $_file = "./$file.css"; 5352 } else { 5353 $_file = admin_url( "$file.css" ); 5354 } 5355 $_file = add_query_arg( 'version', get_bloginfo( 'version' ), $_file ); 5356 5357 /** 5358 * Filters the URI of a WordPress admin CSS file. 5359 * 5360 * @since 2.3.0 5361 * 5362 * @param string $_file Relative path to the file with query arguments attached. 5363 * @param string $file Relative path to the file, minus its ".css" extension. 5364 */ 5365 return apply_filters( 'wp_admin_css_uri', $_file, $file ); 5366 } 5367 5368 /** 5369 * Enqueues or directly prints a stylesheet link to the specified CSS file. 5370 * 5371 * "Intelligently" decides to enqueue or to print the CSS file. If the 5372 * {@see 'wp_print_styles'} action has *not* yet been called, the CSS file will be 5373 * enqueued. If the {@see 'wp_print_styles'} action has been called, the CSS link will 5374 * be printed. Printing may be forced by passing true as the $force_echo 5375 * (second) parameter. 5376 * 5377 * For backward compatibility with WordPress 2.3 calling method: If the $file 5378 * (first) parameter does not correspond to a registered CSS file, we assume 5379 * $file is a file relative to wp-admin/ without its ".css" extension. A 5380 * stylesheet link to that generated URL is printed. 5381 * 5382 * @since 2.3.0 5383 * 5384 * @param string $file Optional. Style handle name or file name (without ".css" extension) relative 5385 * to wp-admin/. Defaults to 'wp-admin'. 5386 * @param bool $force_echo Optional. Force the stylesheet link to be printed rather than enqueued. 5387 */ 5388 function wp_admin_css( $file = 'wp-admin', $force_echo = false ) { 5389 // For backward compatibility. 5390 $handle = str_starts_with( $file, 'css/' ) ? substr( $file, 4 ) : $file; 5391 5392 if ( wp_styles()->query( $handle ) ) { 5393 if ( $force_echo || did_action( 'wp_print_styles' ) ) { 5394 // We already printed the style queue. Print this one immediately. 5395 wp_print_styles( $handle ); 5396 } else { 5397 // Add to style queue. 5398 wp_enqueue_style( $handle ); 5399 } 5400 return; 5401 } 5402 5403 $stylesheet_link = sprintf( 5404 "<link rel='stylesheet' href='%s' />\n", 5405 esc_url( wp_admin_css_uri( $file ) ) 5406 ); 5407 5408 /** 5409 * Filters the stylesheet link to the specified CSS file. 5410 * 5411 * If the site is set to display right-to-left, the RTL stylesheet link 5412 * will be used instead. 5413 * 5414 * @since 2.3.0 5415 * @param string $stylesheet_link HTML link element for the stylesheet. 5416 * @param string $file Style handle name or filename (without ".css" extension) 5417 * relative to wp-admin/. Defaults to 'wp-admin'. 5418 */ 5419 echo apply_filters( 'wp_admin_css', $stylesheet_link, $file ); 5420 5421 if ( function_exists( 'is_rtl' ) && is_rtl() ) { 5422 $rtl_stylesheet_link = sprintf( 5423 "<link rel='stylesheet' href='%s' />\n", 5424 esc_url( wp_admin_css_uri( "$file-rtl" ) ) 5425 ); 5426 5427 /** This filter is documented in wp-includes/general-template.php */ 5428 echo apply_filters( 'wp_admin_css', $rtl_stylesheet_link, "$file-rtl" ); 5429 } 5430 } 5431 5432 /** 5433 * Enqueues the default ThickBox js and css. 5434 * 5435 * If any of the settings need to be changed, this can be done with another js 5436 * file similar to media-upload.js. That file should 5437 * require array('thickbox') to ensure it is loaded after. 5438 * 5439 * @since 2.5.0 5440 */ 5441 function add_thickbox() { 5442 wp_enqueue_script( 'thickbox' ); 5443 wp_enqueue_style( 'thickbox' ); 5444 5445 if ( is_network_admin() ) { 5446 add_action( 'admin_head', '_thickbox_path_admin_subfolder' ); 5447 } 5448 } 5449 5450 /** 5451 * Displays the XHTML generator that is generated on the wp_head hook. 5452 * 5453 * See {@see 'wp_head'}. 5454 * 5455 * @since 2.5.0 5456 */ 5457 function wp_generator() { 5458 /** 5459 * Filters the output of the XHTML generator tag. 5460 * 5461 * @since 2.5.0 5462 * 5463 * @param string $generator_type The XHTML generator. 5464 */ 5465 the_generator( apply_filters( 'wp_generator_type', 'xhtml' ) ); 5466 } 5467 5468 /** 5469 * Displays the generator XML or Comment for RSS, ATOM, etc. 5470 * 5471 * Returns the correct generator type for the requested output format. Allows 5472 * for a plugin to filter generators overall the {@see 'the_generator'} filter. 5473 * 5474 * @since 2.5.0 5475 * 5476 * @param string $type The type of generator to output - (html|xhtml|atom|rss2|rdf|comment|export). 5477 */ 5478 function the_generator( $type ) { 5479 /** 5480 * Filters the output of the XHTML generator tag, for display. 5481 * 5482 * @since 2.5.0 5483 * 5484 * @param string $generator_type The generator output. 5485 * @param string $type The type of generator to output. Accepts 'html', 5486 * 'xhtml', 'atom', 'rss2', 'rdf', 'comment', 'export'. 5487 */ 5488 echo apply_filters( 'the_generator', get_the_generator( $type ), $type ) . "\n"; 5489 } 5490 5491 /** 5492 * Creates the generator XML or Comment for RSS, ATOM, etc. 5493 * 5494 * Returns the correct generator type for the requested output format. Allows 5495 * for a plugin to filter generators on an individual basis using the 5496 * {@see 'get_the_generator_$type'} filter. 5497 * 5498 * @since 2.5.0 5499 * 5500 * @param string $type The type of generator to return - (html|xhtml|atom|rss2|rdf|comment|export). 5501 * @return string|null The HTML content for the generator. 5502 */ 5503 function get_the_generator( $type = '' ) { 5504 if ( empty( $type ) ) { 5505 5506 $current_filter = current_filter(); 5507 if ( empty( $current_filter ) ) { 5508 return null; 5509 } 5510 5511 switch ( $current_filter ) { 5512 case 'rss2_head': 5513 case 'commentsrss2_head': 5514 $type = 'rss2'; 5515 break; 5516 case 'rss_head': 5517 case 'opml_head': 5518 $type = 'comment'; 5519 break; 5520 case 'rdf_header': 5521 $type = 'rdf'; 5522 break; 5523 case 'atom_head': 5524 case 'comments_atom_head': 5525 case 'app_head': 5526 $type = 'atom'; 5527 break; 5528 } 5529 } 5530 5531 switch ( $type ) { 5532 case 'html': 5533 $gen = '<meta name="generator" content="WordPress ' . esc_attr( get_bloginfo( 'version' ) ) . '">'; 5534 break; 5535 case 'xhtml': 5536 $gen = '<meta name="generator" content="WordPress ' . esc_attr( get_bloginfo( 'version' ) ) . '" />'; 5537 break; 5538 case 'atom': 5539 $gen = '<generator uri="https://wordpress.org/" version="' . esc_attr( get_bloginfo_rss( 'version' ) ) . '">WordPress</generator>'; 5540 break; 5541 case 'rss2': 5542 $gen = '<generator>' . sanitize_url( 'https://wordpress.org/?v=' . get_bloginfo_rss( 'version' ) ) . '</generator>'; 5543 break; 5544 case 'rdf': 5545 $gen = '<admin:generatorAgent rdf:resource="' . sanitize_url( 'https://wordpress.org/?v=' . get_bloginfo_rss( 'version' ) ) . '" />'; 5546 break; 5547 case 'comment': 5548 $gen = '<!-- generator="WordPress/' . esc_attr( get_bloginfo( 'version' ) ) . '" -->'; 5549 break; 5550 case 'export': 5551 $gen = '<!-- generator="WordPress/' . esc_attr( get_bloginfo_rss( 'version' ) ) . '" created="' . gmdate( 'Y-m-d H:i' ) . '" -->'; 5552 break; 5553 } 5554 5555 /** 5556 * Filters the HTML for the retrieved generator type. 5557 * 5558 * The dynamic portion of the hook name, `$type`, refers to the generator type. 5559 * 5560 * Possible hook names include: 5561 * 5562 * - `get_the_generator_atom` 5563 * - `get_the_generator_comment` 5564 * - `get_the_generator_export` 5565 * - `get_the_generator_html` 5566 * - `get_the_generator_rdf` 5567 * - `get_the_generator_rss2` 5568 * - `get_the_generator_xhtml` 5569 * 5570 * @since 2.5.0 5571 * 5572 * @param string $gen The HTML markup output to wp_head(). 5573 * @param string $type The type of generator. Accepts 'html', 'xhtml', 'atom', 5574 * 'rss2', 'rdf', 'comment', 'export'. 5575 */ 5576 return apply_filters( "get_the_generator_{$type}", $gen, $type ); 5577 } 5578 5579 /** 5580 * Outputs the HTML checked attribute. 5581 * 5582 * Compares the first two arguments and if identical marks as checked. 5583 * 5584 * @since 1.0.0 5585 * 5586 * @param mixed $checked One of the values to compare. 5587 * @param mixed $current Optional. The other value to compare if not just true. 5588 * Default true. 5589 * @param bool $display Optional. Whether to echo or just return the string. 5590 * Default true. 5591 * @return string HTML attribute or empty string. 5592 */ 5593 function checked( $checked, $current = true, $display = true ) { 5594 return __checked_selected_helper( $checked, $current, $display, 'checked' ); 5595 } 5596 5597 /** 5598 * Outputs the HTML selected attribute. 5599 * 5600 * Compares the first two arguments and if identical marks as selected. 5601 * 5602 * @since 1.0.0 5603 * 5604 * @param mixed $selected One of the values to compare. 5605 * @param mixed $current Optional. The other value to compare if not just true. 5606 * Default true. 5607 * @param bool $display Optional. Whether to echo or just return the string. 5608 * Default true. 5609 * @return string HTML attribute or empty string. 5610 */ 5611 function selected( $selected, $current = true, $display = true ) { 5612 return __checked_selected_helper( $selected, $current, $display, 'selected' ); 5613 } 5614 5615 /** 5616 * Outputs the HTML disabled attribute. 5617 * 5618 * Compares the first two arguments and if identical marks as disabled. 5619 * 5620 * @since 3.0.0 5621 * 5622 * @param mixed $disabled One of the values to compare. 5623 * @param mixed $current Optional. The other value to compare if not just true. 5624 * Default true. 5625 * @param bool $display Optional. Whether to echo or just return the string. 5626 * Default true. 5627 * @return string HTML attribute or empty string. 5628 */ 5629 function disabled( $disabled, $current = true, $display = true ) { 5630 return __checked_selected_helper( $disabled, $current, $display, 'disabled' ); 5631 } 5632 5633 /** 5634 * Outputs the HTML readonly attribute. 5635 * 5636 * Compares the first two arguments and if identical marks as readonly. 5637 * 5638 * @since 5.9.0 5639 * 5640 * @param mixed $readonly_value One of the values to compare. 5641 * @param mixed $current Optional. The other value to compare if not just true. 5642 * Default true. 5643 * @param bool $display Optional. Whether to echo or just return the string. 5644 * Default true. 5645 * @return string HTML attribute or empty string. 5646 */ 5647 function wp_readonly( $readonly_value, $current = true, $display = true ) { 5648 return __checked_selected_helper( $readonly_value, $current, $display, 'readonly' ); 5649 } 5650 5651 /* 5652 * Include a compat `readonly()` function on PHP < 8.1. Since PHP 8.1, 5653 * `readonly` is a reserved keyword and cannot be used as a function name. 5654 * In order to avoid PHP parser errors, this function was extracted 5655 * to a separate file and is only included conditionally on PHP < 8.1. 5656 */ 5657 if ( PHP_VERSION_ID < 80100 ) { 5658 require_once __DIR__ . '/php-compat/readonly.php'; 5659 } 5660 5661 /** 5662 * Private helper function for checked, selected, disabled and readonly. 5663 * 5664 * Compares the first two arguments and if identical marks as `$type`. 5665 * 5666 * @since 2.8.0 5667 * @access private 5668 * 5669 * @param mixed $helper One of the values to compare. 5670 * @param mixed $current The other value to compare if not just true. 5671 * @param bool $display Whether to echo or just return the string. 5672 * @param string $type The type of checked|selected|disabled|readonly we are doing. 5673 * @return string HTML attribute or empty string. 5674 */ 5675 function __checked_selected_helper( $helper, $current, $display, $type ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore 5676 if ( (string) $helper === (string) $current ) { 5677 $result = " $type='$type'"; 5678 } else { 5679 $result = ''; 5680 } 5681 5682 if ( $display ) { 5683 echo $result; 5684 } 5685 5686 return $result; 5687 } 5688 5689 /** 5690 * Assigns a visual indicator for required form fields. 5691 * 5692 * @since 6.1.0 5693 * 5694 * @return string Indicator glyph wrapped in a `span` tag. 5695 */ 5696 function wp_required_field_indicator() { 5697 /* translators: Character to identify required form fields. */ 5698 $glyph = __( '*' ); 5699 $indicator = '<span class="required">' . esc_html( $glyph ) . '</span>'; 5700 5701 /** 5702 * Filters the markup for a visual indicator of required form fields. 5703 * 5704 * @since 6.1.0 5705 * 5706 * @param string $indicator Markup for the indicator element. 5707 */ 5708 return apply_filters( 'wp_required_field_indicator', $indicator ); 5709 } 5710 5711 /** 5712 * Creates a message to explain required form fields. 5713 * 5714 * @since 6.1.0 5715 * 5716 * @return string Message text and glyph wrapped in a `span` tag. 5717 */ 5718 function wp_required_field_message() { 5719 $message = sprintf( 5720 '<span class="required-field-message">%s</span>', 5721 /* translators: %s: Asterisk symbol (*). */ 5722 sprintf( __( 'Required fields are marked %s' ), wp_required_field_indicator() ) 5723 ); 5724 5725 /** 5726 * Filters the message to explain required form fields. 5727 * 5728 * @since 6.1.0 5729 * 5730 * @param string $message Message text and glyph wrapped in a `span` tag. 5731 */ 5732 return apply_filters( 'wp_required_field_message', $message ); 5733 } 5734 5735 /** 5736 * Default settings for heartbeat. 5737 * 5738 * Outputs the nonce used in the heartbeat XHR. 5739 * 5740 * @since 3.6.0 5741 * 5742 * @param array $settings 5743 * @return array Heartbeat settings. 5744 */ 5745 function wp_heartbeat_settings( $settings ) { 5746 if ( ! is_admin() ) { 5747 $settings['ajaxurl'] = admin_url( 'admin-ajax.php', 'relative' ); 5748 } 5749 5750 if ( is_user_logged_in() ) { 5751 $settings['nonce'] = wp_create_nonce( 'heartbeat-nonce' ); 5752 } 5753 5754 return $settings; 5755 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sun Oct 25 08:20:35 2026 | Cross-referenced by PHPXref |