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