[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * These functions can be replaced via plugins. If plugins do not redefine these 4 * functions, then these will be used instead. 5 * 6 * @package WordPress 7 */ 8 9 if ( ! function_exists( 'wp_set_current_user' ) ) : 10 /** 11 * Changes the current user by ID or name. 12 * 13 * Set $id to null and specify a name if you do not know a user's ID. 14 * 15 * Some WordPress functionality is based on the current user and not based on 16 * the signed in user. Therefore, it opens the ability to edit and perform 17 * actions on users who aren't signed in. 18 * 19 * @since 2.0.3 20 * 21 * @global WP_User $current_user The current user object which holds the user data. 22 * 23 * @param int|null $id User ID. 24 * @param string $name User's username. 25 * @return WP_User Current user User object. 26 */ 27 function wp_set_current_user( $id, $name = '' ) { 28 global $current_user; 29 30 // If `$id` matches the current user, there is nothing to do. 31 if ( isset( $current_user ) 32 && ( $current_user instanceof WP_User ) 33 && ( $id === $current_user->ID ) 34 && ( null !== $id ) 35 ) { 36 return $current_user; 37 } 38 39 $current_user = new WP_User( $id, $name ); 40 41 setup_userdata( $current_user->ID ); 42 43 /** 44 * Fires after the current user is set. 45 * 46 * @since 2.0.1 47 */ 48 do_action( 'set_current_user' ); 49 50 return $current_user; 51 } 52 endif; 53 54 if ( ! function_exists( 'wp_get_current_user' ) ) : 55 /** 56 * Retrieves the current user object. 57 * 58 * Will set the current user, if the current user is not set. The current user 59 * will be set to the logged-in person. If no user is logged-in, then it will 60 * set the current user to 0, which is invalid and won't have any permissions. 61 * 62 * @since 2.0.3 63 * 64 * @see _wp_get_current_user() 65 * @global WP_User $current_user Checks if the current user is set. 66 * 67 * @return WP_User Current WP_User instance. 68 */ 69 function wp_get_current_user() { 70 return _wp_get_current_user(); 71 } 72 endif; 73 74 if ( ! function_exists( 'get_userdata' ) ) : 75 /** 76 * Retrieves user info by user ID. 77 * 78 * @since 0.71 79 * 80 * @param int $user_id User ID 81 * @return WP_User|false WP_User object on success, false on failure. 82 */ 83 function get_userdata( $user_id ) { 84 return get_user_by( 'id', $user_id ); 85 } 86 endif; 87 88 if ( ! function_exists( 'get_user_by' ) ) : 89 /** 90 * Retrieves user info by a given field. 91 * 92 * @since 2.8.0 93 * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter. 94 * 95 * @global WP_User $current_user The current user object which holds the user data. 96 * 97 * @param string $field The field to retrieve the user with. id | ID | slug | email | login. 98 * @param int|string $value A value for $field. A user ID, slug, email address, or login name. 99 * @return WP_User|false WP_User object on success, false on failure. 100 */ 101 function get_user_by( $field, $value ) { 102 $userdata = WP_User::get_data_by( $field, $value ); 103 104 if ( ! $userdata ) { 105 return false; 106 } 107 108 $user = new WP_User(); 109 $user->init( $userdata ); 110 111 return $user; 112 } 113 endif; 114 115 if ( ! function_exists( 'cache_users' ) ) : 116 /** 117 * Retrieves info for user lists to prevent multiple queries by get_userdata(). 118 * 119 * @since 3.0.0 120 * 121 * @global wpdb $wpdb WordPress database abstraction object. 122 * 123 * @param int[] $user_ids User ID numbers list 124 */ 125 function cache_users( $user_ids ) { 126 global $wpdb; 127 128 update_meta_cache( 'user', $user_ids ); 129 130 $clean = _get_non_cached_ids( $user_ids, 'users' ); 131 132 if ( empty( $clean ) ) { 133 return; 134 } 135 136 $list = implode( ',', $clean ); 137 138 $users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" ); 139 140 foreach ( $users as $user ) { 141 update_user_caches( $user ); 142 } 143 } 144 endif; 145 146 if ( ! function_exists( 'wp_mail' ) ) : 147 /** 148 * Sends an email, similar to PHP's mail function. 149 * 150 * A true return value does not automatically mean that the user received the 151 * email successfully. It just only means that the method used was able to 152 * process the request without any errors. 153 * 154 * The default content type is `text/plain` which does not allow using HTML. 155 * However, you can set the content type of the email by using the 156 * {@see 'wp_mail_content_type'} filter. 157 * 158 * The default charset is based on the charset used on the blog. The charset can 159 * be set using the {@see 'wp_mail_charset'} filter. 160 * 161 * @since 1.2.1 162 * @since 5.5.0 is_email() is used for email validation, 163 * instead of PHPMailer's default validator. 164 * 165 * @global PHPMailer\PHPMailer\PHPMailer $phpmailer 166 * 167 * @param string|string[] $to Array or comma-separated list of email addresses to send message. 168 * @param string $subject Email subject. 169 * @param string $message Message contents. 170 * @param string|string[] $headers Optional. Additional headers. 171 * @param string|string[] $attachments Optional. Paths to files to attach. 172 * @return bool Whether the email was sent successfully. 173 */ 174 function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { 175 // Compact the input, apply the filters, and extract them back out. 176 177 /** 178 * Filters the wp_mail() arguments. 179 * 180 * @since 2.2.0 181 * 182 * @param array $args { 183 * Array of the `wp_mail()` arguments. 184 * 185 * @type string|string[] $to Array or comma-separated list of email addresses to send message. 186 * @type string $subject Email subject. 187 * @type string $message Message contents. 188 * @type string|string[] $headers Additional headers. 189 * @type string|string[] $attachments Paths to files to attach. 190 * } 191 */ 192 $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ); 193 194 /** 195 * Filters whether to preempt sending an email. 196 * 197 * Returning a non-null value will short-circuit {@see wp_mail()}, returning 198 * that value instead. A boolean return value should be used to indicate whether 199 * the email was successfully sent. 200 * 201 * @since 5.7.0 202 * 203 * @param null|bool $return Short-circuit return value. 204 * @param array $atts { 205 * Array of the `wp_mail()` arguments. 206 * 207 * @type string|string[] $to Array or comma-separated list of email addresses to send message. 208 * @type string $subject Email subject. 209 * @type string $message Message contents. 210 * @type string|string[] $headers Additional headers. 211 * @type string|string[] $attachments Paths to files to attach. 212 * } 213 */ 214 $pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts ); 215 216 if ( null !== $pre_wp_mail ) { 217 return $pre_wp_mail; 218 } 219 220 if ( isset( $atts['to'] ) ) { 221 $to = $atts['to']; 222 } 223 224 if ( ! is_array( $to ) ) { 225 $to = explode( ',', $to ); 226 } 227 228 if ( isset( $atts['subject'] ) ) { 229 $subject = $atts['subject']; 230 } 231 232 if ( isset( $atts['message'] ) ) { 233 $message = $atts['message']; 234 } 235 236 if ( isset( $atts['headers'] ) ) { 237 $headers = $atts['headers']; 238 } 239 240 if ( isset( $atts['attachments'] ) ) { 241 $attachments = $atts['attachments']; 242 } 243 244 if ( ! is_array( $attachments ) ) { 245 $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) ); 246 } 247 global $phpmailer; 248 249 // (Re)create it, if it's gone missing. 250 if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) { 251 require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php'; 252 require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php'; 253 require_once ABSPATH . WPINC . '/PHPMailer/Exception.php'; 254 $phpmailer = new PHPMailer\PHPMailer\PHPMailer( true ); 255 256 $phpmailer::$validator = static function ( $email ) { 257 return (bool) is_email( $email ); 258 }; 259 } 260 261 // Headers. 262 $cc = array(); 263 $bcc = array(); 264 $reply_to = array(); 265 266 if ( empty( $headers ) ) { 267 $headers = array(); 268 } else { 269 if ( ! is_array( $headers ) ) { 270 /* 271 * Explode the headers out, so this function can take 272 * both string headers and an array of headers. 273 */ 274 $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) ); 275 } else { 276 $tempheaders = $headers; 277 } 278 $headers = array(); 279 280 // If it's actually got contents. 281 if ( ! empty( $tempheaders ) ) { 282 // Iterate through the raw headers. 283 foreach ( (array) $tempheaders as $header ) { 284 if ( ! str_contains( $header, ':' ) ) { 285 if ( false !== stripos( $header, 'boundary=' ) ) { 286 $parts = preg_split( '/boundary=/i', trim( $header ) ); 287 $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); 288 } 289 continue; 290 } 291 // Explode them out. 292 list( $name, $content ) = explode( ':', trim( $header ), 2 ); 293 294 // Cleanup crew. 295 $name = trim( $name ); 296 $content = trim( $content ); 297 298 switch ( strtolower( $name ) ) { 299 // Mainly for legacy -- process a "From:" header if it's there. 300 case 'from': 301 $bracket_pos = strpos( $content, '<' ); 302 if ( false !== $bracket_pos ) { 303 // Text before the bracketed email is the "From" name. 304 if ( $bracket_pos > 0 ) { 305 $from_name = substr( $content, 0, $bracket_pos ); 306 $from_name = str_replace( '"', '', $from_name ); 307 $from_name = trim( $from_name ); 308 } 309 310 $from_email = substr( $content, $bracket_pos + 1 ); 311 $from_email = str_replace( '>', '', $from_email ); 312 $from_email = trim( $from_email ); 313 314 // Avoid setting an empty $from_email. 315 } elseif ( '' !== trim( $content ) ) { 316 $from_email = trim( $content ); 317 } 318 break; 319 case 'content-type': 320 if ( str_contains( $content, ';' ) ) { 321 list( $type, $charset_content ) = explode( ';', $content ); 322 $content_type = trim( $type ); 323 if ( false !== stripos( $charset_content, 'charset=' ) ) { 324 $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) ); 325 } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) { 326 $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) ); 327 $charset = ''; 328 } 329 330 // Avoid setting an empty $content_type. 331 } elseif ( '' !== trim( $content ) ) { 332 $content_type = trim( $content ); 333 } 334 break; 335 case 'cc': 336 $cc = array_merge( (array) $cc, explode( ',', $content ) ); 337 break; 338 case 'bcc': 339 $bcc = array_merge( (array) $bcc, explode( ',', $content ) ); 340 break; 341 case 'reply-to': 342 $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) ); 343 break; 344 default: 345 // Add it to our grand headers array. 346 $headers[ trim( $name ) ] = trim( $content ); 347 break; 348 } 349 } 350 } 351 } 352 353 // Empty out the values that may be set. 354 $phpmailer->clearAllRecipients(); 355 $phpmailer->clearAttachments(); 356 $phpmailer->clearCustomHeaders(); 357 $phpmailer->clearReplyTos(); 358 $phpmailer->Body = ''; 359 $phpmailer->AltBody = ''; 360 361 // Set "From" name and email. 362 363 // If we don't have a name from the input headers. 364 if ( ! isset( $from_name ) ) { 365 $from_name = 'WordPress'; 366 } 367 368 /* 369 * If we don't have an email from the input headers, default to wordpress@$sitename 370 * Some hosts will block outgoing mail from this address if it doesn't exist, 371 * but there's no easy alternative. Defaulting to admin_email might appear to be 372 * another option, but some hosts may refuse to relay mail from an unknown domain. 373 * See https://core.trac.wordpress.org/ticket/5007. 374 */ 375 if ( ! isset( $from_email ) ) { 376 // Get the site domain and get rid of www. 377 $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST ); 378 $from_email = 'wordpress@'; 379 380 if ( null !== $sitename ) { 381 if ( str_starts_with( $sitename, 'www.' ) ) { 382 $sitename = substr( $sitename, 4 ); 383 } 384 385 $from_email .= $sitename; 386 } 387 } 388 389 /** 390 * Filters the email address to send from. 391 * 392 * @since 2.2.0 393 * 394 * @param string $from_email Email address to send from. 395 */ 396 $from_email = apply_filters( 'wp_mail_from', $from_email ); 397 398 /** 399 * Filters the name to associate with the "from" email address. 400 * 401 * @since 2.3.0 402 * 403 * @param string $from_name Name associated with the "from" email address. 404 */ 405 $from_name = apply_filters( 'wp_mail_from_name', $from_name ); 406 407 try { 408 $phpmailer->setFrom( $from_email, $from_name, false ); 409 } catch ( PHPMailer\PHPMailer\Exception $e ) { 410 $mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' ); 411 $mail_error_data['phpmailer_exception_code'] = $e->getCode(); 412 413 /** This filter is documented in wp-includes/pluggable.php */ 414 do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) ); 415 416 return false; 417 } 418 419 // Set mail's subject and body. 420 $phpmailer->Subject = $subject; 421 $phpmailer->Body = $message; 422 423 // Set destination addresses, using appropriate methods for handling addresses. 424 $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' ); 425 426 foreach ( $address_headers as $address_header => $addresses ) { 427 if ( empty( $addresses ) ) { 428 continue; 429 } 430 431 foreach ( (array) $addresses as $address ) { 432 try { 433 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>". 434 $recipient_name = ''; 435 436 if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) { 437 if ( count( $matches ) === 3 ) { 438 $recipient_name = $matches[1]; 439 $address = $matches[2]; 440 } 441 } 442 443 switch ( $address_header ) { 444 case 'to': 445 $phpmailer->addAddress( $address, $recipient_name ); 446 break; 447 case 'cc': 448 $phpmailer->addCc( $address, $recipient_name ); 449 break; 450 case 'bcc': 451 $phpmailer->addBcc( $address, $recipient_name ); 452 break; 453 case 'reply_to': 454 $phpmailer->addReplyTo( $address, $recipient_name ); 455 break; 456 } 457 } catch ( PHPMailer\PHPMailer\Exception $e ) { 458 continue; 459 } 460 } 461 } 462 463 // Set to use PHP's mail(). 464 $phpmailer->isMail(); 465 466 // Set Content-Type and charset. 467 468 // If we don't have a Content-Type from the input headers. 469 if ( ! isset( $content_type ) ) { 470 $content_type = 'text/plain'; 471 } 472 473 /** 474 * Filters the wp_mail() content type. 475 * 476 * @since 2.3.0 477 * 478 * @param string $content_type Default wp_mail() content type. 479 */ 480 $content_type = apply_filters( 'wp_mail_content_type', $content_type ); 481 482 $phpmailer->ContentType = $content_type; 483 484 // Set whether it's plaintext, depending on $content_type. 485 if ( 'text/html' === $content_type ) { 486 $phpmailer->isHTML( true ); 487 } 488 489 // If we don't have a charset from the input headers. 490 if ( ! isset( $charset ) ) { 491 $charset = get_bloginfo( 'charset' ); 492 } 493 494 /** 495 * Filters the default wp_mail() charset. 496 * 497 * @since 2.3.0 498 * 499 * @param string $charset Default email charset. 500 */ 501 $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset ); 502 503 // Set custom headers. 504 if ( ! empty( $headers ) ) { 505 foreach ( (array) $headers as $name => $content ) { 506 // Only add custom headers not added automatically by PHPMailer. 507 if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) { 508 try { 509 $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); 510 } catch ( PHPMailer\PHPMailer\Exception $e ) { 511 continue; 512 } 513 } 514 } 515 516 if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) { 517 $phpmailer->addCustomHeader( sprintf( 'Content-Type: %s; boundary="%s"', $content_type, $boundary ) ); 518 } 519 } 520 521 if ( ! empty( $attachments ) ) { 522 foreach ( $attachments as $filename => $attachment ) { 523 $filename = is_string( $filename ) ? $filename : ''; 524 525 try { 526 $phpmailer->addAttachment( $attachment, $filename ); 527 } catch ( PHPMailer\PHPMailer\Exception $e ) { 528 continue; 529 } 530 } 531 } 532 533 /** 534 * Fires after PHPMailer is initialized. 535 * 536 * @since 2.2.0 537 * 538 * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference). 539 */ 540 do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) ); 541 542 $mail_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' ); 543 544 // Send! 545 try { 546 $send = $phpmailer->send(); 547 548 /** 549 * Fires after PHPMailer has successfully sent an email. 550 * 551 * The firing of this action does not necessarily mean that the recipient(s) received the 552 * email successfully. It only means that the `send` method above was able to 553 * process the request without any errors. 554 * 555 * @since 5.9.0 556 * 557 * @param array $mail_data { 558 * An array containing the email recipient(s), subject, message, headers, and attachments. 559 * 560 * @type string[] $to Email addresses to send message. 561 * @type string $subject Email subject. 562 * @type string $message Message contents. 563 * @type string[] $headers Additional headers. 564 * @type string[] $attachments Paths to files to attach. 565 * } 566 */ 567 do_action( 'wp_mail_succeeded', $mail_data ); 568 569 return $send; 570 } catch ( PHPMailer\PHPMailer\Exception $e ) { 571 $mail_data['phpmailer_exception_code'] = $e->getCode(); 572 573 /** 574 * Fires after a PHPMailer\PHPMailer\Exception is caught. 575 * 576 * @since 4.4.0 577 * 578 * @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array 579 * containing the mail recipient, subject, message, headers, and attachments. 580 */ 581 do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) ); 582 583 return false; 584 } 585 } 586 endif; 587 588 if ( ! function_exists( 'wp_authenticate' ) ) : 589 /** 590 * Authenticates a user, confirming the login credentials are valid. 591 * 592 * @since 2.5.0 593 * @since 4.5.0 `$username` now accepts an email address. 594 * 595 * @param string $username User's username or email address. 596 * @param string $password User's password. 597 * @return WP_User|WP_Error WP_User object if the credentials are valid, 598 * otherwise WP_Error. 599 */ 600 function wp_authenticate( $username, $password ) { 601 $username = sanitize_user( $username ); 602 $password = trim( $password ); 603 604 /** 605 * Filters whether a set of user login credentials are valid. 606 * 607 * A WP_User object is returned if the credentials authenticate a user. 608 * WP_Error or null otherwise. 609 * 610 * @since 2.8.0 611 * @since 4.5.0 `$username` now accepts an email address. 612 * 613 * @param null|WP_User|WP_Error $user WP_User if the user is authenticated. 614 * WP_Error or null otherwise. 615 * @param string $username Username or email address. 616 * @param string $password User password. 617 */ 618 $user = apply_filters( 'authenticate', null, $username, $password ); 619 620 if ( null === $user || false === $user ) { 621 /* 622 * TODO: What should the error message be? (Or would these even happen?) 623 * Only needed if all authentication handlers fail to return anything. 624 */ 625 $user = new WP_Error( 'authentication_failed', __( '<strong>Error:</strong> Invalid username, email address or incorrect password.' ) ); 626 } 627 628 $ignore_codes = array( 'empty_username', 'empty_password' ); 629 630 if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes, true ) ) { 631 $error = $user; 632 633 /** 634 * Fires after a user login has failed. 635 * 636 * @since 2.5.0 637 * @since 4.5.0 The value of `$username` can now be an email address. 638 * @since 5.4.0 The `$error` parameter was added. 639 * 640 * @param string $username Username or email address. 641 * @param WP_Error $error A WP_Error object with the authentication failure details. 642 */ 643 do_action( 'wp_login_failed', $username, $error ); 644 } 645 646 return $user; 647 } 648 endif; 649 650 if ( ! function_exists( 'wp_logout' ) ) : 651 /** 652 * Logs the current user out. 653 * 654 * @since 2.5.0 655 */ 656 function wp_logout() { 657 $user_id = get_current_user_id(); 658 659 wp_destroy_current_session(); 660 wp_clear_auth_cookie(); 661 wp_set_current_user( 0 ); 662 663 /** 664 * Fires after a user is logged out. 665 * 666 * @since 1.5.0 667 * @since 5.5.0 Added the `$user_id` parameter. 668 * 669 * @param int $user_id ID of the user that was logged out. 670 */ 671 do_action( 'wp_logout', $user_id ); 672 } 673 endif; 674 675 if ( ! function_exists( 'wp_validate_auth_cookie' ) ) : 676 /** 677 * Validates authentication cookie. 678 * 679 * The checks include making sure that the authentication cookie is set and 680 * pulling in the contents (if $cookie is not used). 681 * 682 * Makes sure the cookie is not expired. Verifies the hash in cookie is what is 683 * should be and compares the two. 684 * 685 * @since 2.5.0 686 * 687 * @global int $login_grace_period 688 * 689 * @param string $cookie Optional. If used, will validate contents instead of cookie's. 690 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'. 691 * @return int|false User ID if valid cookie, false if invalid. 692 */ 693 function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) { 694 $cookie_elements = wp_parse_auth_cookie( $cookie, $scheme ); 695 if ( ! $cookie_elements ) { 696 /** 697 * Fires if an authentication cookie is malformed. 698 * 699 * @since 2.7.0 700 * 701 * @param string $cookie Malformed auth cookie. 702 * @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth', 703 * or 'logged_in'. 704 */ 705 do_action( 'auth_cookie_malformed', $cookie, $scheme ); 706 return false; 707 } 708 709 $scheme = $cookie_elements['scheme']; 710 $username = $cookie_elements['username']; 711 $hmac = $cookie_elements['hmac']; 712 $token = $cookie_elements['token']; 713 $expiration = $cookie_elements['expiration']; 714 715 $expired = (int) $expiration; 716 717 // Allow a grace period for POST and Ajax requests. 718 if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) { 719 $expired += HOUR_IN_SECONDS; 720 } 721 722 // Quick check to see if an honest cookie has expired. 723 if ( $expired < time() ) { 724 /** 725 * Fires once an authentication cookie has expired. 726 * 727 * @since 2.7.0 728 * 729 * @param string[] $cookie_elements { 730 * Authentication cookie components. None of the components should be assumed 731 * to be valid as they come directly from a client-provided cookie value. 732 * 733 * @type string $username User's username. 734 * @type string $expiration The time the cookie expires as a UNIX timestamp. 735 * @type string $token User's session token used. 736 * @type string $hmac The security hash for the cookie. 737 * @type string $scheme The cookie scheme to use. 738 * } 739 */ 740 do_action( 'auth_cookie_expired', $cookie_elements ); 741 return false; 742 } 743 744 $user = get_user_by( 'login', $username ); 745 if ( ! $user ) { 746 /** 747 * Fires if a bad username is entered in the user authentication process. 748 * 749 * @since 2.7.0 750 * 751 * @param string[] $cookie_elements { 752 * Authentication cookie components. None of the components should be assumed 753 * to be valid as they come directly from a client-provided cookie value. 754 * 755 * @type string $username User's username. 756 * @type string $expiration The time the cookie expires as a UNIX timestamp. 757 * @type string $token User's session token used. 758 * @type string $hmac The security hash for the cookie. 759 * @type string $scheme The cookie scheme to use. 760 * } 761 */ 762 do_action( 'auth_cookie_bad_username', $cookie_elements ); 763 return false; 764 } 765 766 $pass_frag = substr( $user->user_pass, 8, 4 ); 767 768 $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme ); 769 770 // If ext/hash is not present, compat.php's hash_hmac() does not support sha256. 771 $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1'; 772 $hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key ); 773 774 if ( ! hash_equals( $hash, $hmac ) ) { 775 /** 776 * Fires if a bad authentication cookie hash is encountered. 777 * 778 * @since 2.7.0 779 * 780 * @param string[] $cookie_elements { 781 * Authentication cookie components. None of the components should be assumed 782 * to be valid as they come directly from a client-provided cookie value. 783 * 784 * @type string $username User's username. 785 * @type string $expiration The time the cookie expires as a UNIX timestamp. 786 * @type string $token User's session token used. 787 * @type string $hmac The security hash for the cookie. 788 * @type string $scheme The cookie scheme to use. 789 * } 790 */ 791 do_action( 'auth_cookie_bad_hash', $cookie_elements ); 792 return false; 793 } 794 795 $manager = WP_Session_Tokens::get_instance( $user->ID ); 796 if ( ! $manager->verify( $token ) ) { 797 /** 798 * Fires if a bad session token is encountered. 799 * 800 * @since 4.0.0 801 * 802 * @param string[] $cookie_elements { 803 * Authentication cookie components. None of the components should be assumed 804 * to be valid as they come directly from a client-provided cookie value. 805 * 806 * @type string $username User's username. 807 * @type string $expiration The time the cookie expires as a UNIX timestamp. 808 * @type string $token User's session token used. 809 * @type string $hmac The security hash for the cookie. 810 * @type string $scheme The cookie scheme to use. 811 * } 812 */ 813 do_action( 'auth_cookie_bad_session_token', $cookie_elements ); 814 return false; 815 } 816 817 // Ajax/POST grace period set above. 818 if ( $expiration < time() ) { 819 $GLOBALS['login_grace_period'] = 1; 820 } 821 822 /** 823 * Fires once an authentication cookie has been validated. 824 * 825 * @since 2.7.0 826 * 827 * @param string[] $cookie_elements { 828 * Authentication cookie components. 829 * 830 * @type string $username User's username. 831 * @type string $expiration The time the cookie expires as a UNIX timestamp. 832 * @type string $token User's session token used. 833 * @type string $hmac The security hash for the cookie. 834 * @type string $scheme The cookie scheme to use. 835 * } 836 * @param WP_User $user User object. 837 */ 838 do_action( 'auth_cookie_valid', $cookie_elements, $user ); 839 840 return $user->ID; 841 } 842 endif; 843 844 if ( ! function_exists( 'wp_generate_auth_cookie' ) ) : 845 /** 846 * Generates authentication cookie contents. 847 * 848 * @since 2.5.0 849 * @since 4.0.0 The `$token` parameter was added. 850 * 851 * @param int $user_id User ID. 852 * @param int $expiration The time the cookie expires as a UNIX timestamp. 853 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'. 854 * Default 'auth'. 855 * @param string $token User's session token to use for this cookie. 856 * @return string Authentication cookie contents. Empty string if user does not exist. 857 */ 858 function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) { 859 $user = get_userdata( $user_id ); 860 if ( ! $user ) { 861 return ''; 862 } 863 864 if ( ! $token ) { 865 $manager = WP_Session_Tokens::get_instance( $user_id ); 866 $token = $manager->create( $expiration ); 867 } 868 869 $pass_frag = substr( $user->user_pass, 8, 4 ); 870 871 $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme ); 872 873 // If ext/hash is not present, compat.php's hash_hmac() does not support sha256. 874 $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1'; 875 $hash = hash_hmac( $algo, $user->user_login . '|' . $expiration . '|' . $token, $key ); 876 877 $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash; 878 879 /** 880 * Filters the authentication cookie. 881 * 882 * @since 2.5.0 883 * @since 4.0.0 The `$token` parameter was added. 884 * 885 * @param string $cookie Authentication cookie. 886 * @param int $user_id User ID. 887 * @param int $expiration The time the cookie expires as a UNIX timestamp. 888 * @param string $scheme Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'. 889 * @param string $token User's session token used. 890 */ 891 return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token ); 892 } 893 endif; 894 895 if ( ! function_exists( 'wp_parse_auth_cookie' ) ) : 896 /** 897 * Parses a cookie into its components. 898 * 899 * @since 2.7.0 900 * @since 4.0.0 The `$token` element was added to the return value. 901 * 902 * @param string $cookie Authentication cookie. 903 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'. 904 * @return string[]|false { 905 * Authentication cookie components. None of the components should be assumed 906 * to be valid as they come directly from a client-provided cookie value. If 907 * the cookie value is malformed, false is returned. 908 * 909 * @type string $username User's username. 910 * @type string $expiration The time the cookie expires as a UNIX timestamp. 911 * @type string $token User's session token used. 912 * @type string $hmac The security hash for the cookie. 913 * @type string $scheme The cookie scheme to use. 914 * } 915 */ 916 function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) { 917 if ( empty( $cookie ) ) { 918 switch ( $scheme ) { 919 case 'auth': 920 $cookie_name = AUTH_COOKIE; 921 break; 922 case 'secure_auth': 923 $cookie_name = SECURE_AUTH_COOKIE; 924 break; 925 case 'logged_in': 926 $cookie_name = LOGGED_IN_COOKIE; 927 break; 928 default: 929 if ( is_ssl() ) { 930 $cookie_name = SECURE_AUTH_COOKIE; 931 $scheme = 'secure_auth'; 932 } else { 933 $cookie_name = AUTH_COOKIE; 934 $scheme = 'auth'; 935 } 936 } 937 938 if ( empty( $_COOKIE[ $cookie_name ] ) ) { 939 return false; 940 } 941 $cookie = $_COOKIE[ $cookie_name ]; 942 } 943 944 $cookie_elements = explode( '|', $cookie ); 945 if ( count( $cookie_elements ) !== 4 ) { 946 return false; 947 } 948 949 list( $username, $expiration, $token, $hmac ) = $cookie_elements; 950 951 return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' ); 952 } 953 endif; 954 955 if ( ! function_exists( 'wp_set_auth_cookie' ) ) : 956 /** 957 * Sets the authentication cookies based on user ID. 958 * 959 * The $remember parameter increases the time that the cookie will be kept. The 960 * default the cookie is kept without remembering is two days. When $remember is 961 * set, the cookies will be kept for 14 days or two weeks. 962 * 963 * @since 2.5.0 964 * @since 4.3.0 Added the `$token` parameter. 965 * 966 * @param int $user_id User ID. 967 * @param bool $remember Whether to remember the user. 968 * @param bool|string $secure Whether the auth cookie should only be sent over HTTPS. Default is an empty 969 * string which means the value of `is_ssl()` will be used. 970 * @param string $token Optional. User's session token to use for this cookie. 971 */ 972 function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) { 973 if ( $remember ) { 974 /** 975 * Filters the duration of the authentication cookie expiration period. 976 * 977 * @since 2.8.0 978 * 979 * @param int $length Duration of the expiration period in seconds. 980 * @param int $user_id User ID. 981 * @param bool $remember Whether to remember the user login. Default false. 982 */ 983 $expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember ); 984 985 /* 986 * Ensure the browser will continue to send the cookie after the expiration time is reached. 987 * Needed for the login grace period in wp_validate_auth_cookie(). 988 */ 989 $expire = $expiration + ( 12 * HOUR_IN_SECONDS ); 990 } else { 991 /** This filter is documented in wp-includes/pluggable.php */ 992 $expiration = time() + apply_filters( 'auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember ); 993 $expire = 0; 994 } 995 996 if ( '' === $secure ) { 997 $secure = is_ssl(); 998 } 999 1000 // Front-end cookie is secure when the auth cookie is secure and the site's home URL uses HTTPS. 1001 $secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME ); 1002 1003 /** 1004 * Filters whether the auth cookie should only be sent over HTTPS. 1005 * 1006 * @since 3.1.0 1007 * 1008 * @param bool $secure Whether the cookie should only be sent over HTTPS. 1009 * @param int $user_id User ID. 1010 */ 1011 $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id ); 1012 1013 /** 1014 * Filters whether the logged in cookie should only be sent over HTTPS. 1015 * 1016 * @since 3.1.0 1017 * 1018 * @param bool $secure_logged_in_cookie Whether the logged in cookie should only be sent over HTTPS. 1019 * @param int $user_id User ID. 1020 * @param bool $secure Whether the auth cookie should only be sent over HTTPS. 1021 */ 1022 $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure ); 1023 1024 if ( $secure ) { 1025 $auth_cookie_name = SECURE_AUTH_COOKIE; 1026 $scheme = 'secure_auth'; 1027 } else { 1028 $auth_cookie_name = AUTH_COOKIE; 1029 $scheme = 'auth'; 1030 } 1031 1032 if ( '' === $token ) { 1033 $manager = WP_Session_Tokens::get_instance( $user_id ); 1034 $token = $manager->create( $expiration ); 1035 } 1036 1037 $auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token ); 1038 $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token ); 1039 1040 /** 1041 * Fires immediately before the authentication cookie is set. 1042 * 1043 * @since 2.5.0 1044 * @since 4.9.0 The `$token` parameter was added. 1045 * 1046 * @param string $auth_cookie Authentication cookie value. 1047 * @param int $expire The time the login grace period expires as a UNIX timestamp. 1048 * Default is 12 hours past the cookie's expiration time. 1049 * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp. 1050 * Default is 14 days from now. 1051 * @param int $user_id User ID. 1052 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'. 1053 * @param string $token User's session token to use for this cookie. 1054 */ 1055 do_action( 'set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token ); 1056 1057 /** 1058 * Fires immediately before the logged-in authentication cookie is set. 1059 * 1060 * @since 2.6.0 1061 * @since 4.9.0 The `$token` parameter was added. 1062 * 1063 * @param string $logged_in_cookie The logged-in cookie value. 1064 * @param int $expire The time the login grace period expires as a UNIX timestamp. 1065 * Default is 12 hours past the cookie's expiration time. 1066 * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp. 1067 * Default is 14 days from now. 1068 * @param int $user_id User ID. 1069 * @param string $scheme Authentication scheme. Default 'logged_in'. 1070 * @param string $token User's session token to use for this cookie. 1071 */ 1072 do_action( 'set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token ); 1073 1074 /** 1075 * Allows preventing auth cookies from actually being sent to the client. 1076 * 1077 * @since 4.7.4 1078 * @since 6.2.0 The `$expire`, `$expiration`, `$user_id`, `$scheme`, and `$token` parameters were added. 1079 * 1080 * @param bool $send Whether to send auth cookies to the client. Default true. 1081 * @param int $expire The time the login grace period expires as a UNIX timestamp. 1082 * Default is 12 hours past the cookie's expiration time. Zero when clearing cookies. 1083 * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp. 1084 * Default is 14 days from now. Zero when clearing cookies. 1085 * @param int $user_id User ID. Zero when clearing cookies. 1086 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'. 1087 * Empty string when clearing cookies. 1088 * @param string $token User's session token to use for this cookie. Empty string when clearing cookies. 1089 */ 1090 if ( ! apply_filters( 'send_auth_cookies', true, $expire, $expiration, $user_id, $scheme, $token ) ) { 1091 return; 1092 } 1093 1094 setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true ); 1095 setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true ); 1096 setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true ); 1097 if ( COOKIEPATH !== SITECOOKIEPATH ) { 1098 setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true ); 1099 } 1100 } 1101 endif; 1102 1103 if ( ! function_exists( 'wp_clear_auth_cookie' ) ) : 1104 /** 1105 * Removes all of the cookies associated with authentication. 1106 * 1107 * @since 2.5.0 1108 */ 1109 function wp_clear_auth_cookie() { 1110 /** 1111 * Fires just before the authentication cookies are cleared. 1112 * 1113 * @since 2.7.0 1114 */ 1115 do_action( 'clear_auth_cookie' ); 1116 1117 /** This filter is documented in wp-includes/pluggable.php */ 1118 if ( ! apply_filters( 'send_auth_cookies', true, 0, 0, 0, '', '' ) ) { 1119 return; 1120 } 1121 1122 // Auth cookies. 1123 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN ); 1124 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN ); 1125 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN ); 1126 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN ); 1127 setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1128 setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN ); 1129 1130 // Settings cookies. 1131 setcookie( 'wp-settings-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH ); 1132 setcookie( 'wp-settings-time-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH ); 1133 1134 // Old cookies. 1135 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1136 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN ); 1137 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1138 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN ); 1139 1140 // Even older cookies. 1141 setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1142 setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1143 setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN ); 1144 setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN ); 1145 1146 // Post password cookie. 1147 setcookie( 'wp-postpass_' . COOKIEHASH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); 1148 } 1149 endif; 1150 1151 if ( ! function_exists( 'is_user_logged_in' ) ) : 1152 /** 1153 * Determines whether the current visitor is a logged in user. 1154 * 1155 * For more information on this and similar theme functions, check out 1156 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 1157 * Conditional Tags} article in the Theme Developer Handbook. 1158 * 1159 * @since 2.0.0 1160 * 1161 * @return bool True if user is logged in, false if not logged in. 1162 */ 1163 function is_user_logged_in() { 1164 $user = wp_get_current_user(); 1165 1166 return $user->exists(); 1167 } 1168 endif; 1169 1170 if ( ! function_exists( 'auth_redirect' ) ) : 1171 /** 1172 * Checks if a user is logged in, if not it redirects them to the login page. 1173 * 1174 * When this code is called from a page, it checks to see if the user viewing the page is logged in. 1175 * If the user is not logged in, they are redirected to the login page. The user is redirected 1176 * in such a way that, upon logging in, they will be sent directly to the page they were originally 1177 * trying to access. 1178 * 1179 * @since 1.5.0 1180 */ 1181 function auth_redirect() { 1182 $secure = ( is_ssl() || force_ssl_admin() ); 1183 1184 /** 1185 * Filters whether to use a secure authentication redirect. 1186 * 1187 * @since 3.1.0 1188 * 1189 * @param bool $secure Whether to use a secure authentication redirect. Default false. 1190 */ 1191 $secure = apply_filters( 'secure_auth_redirect', $secure ); 1192 1193 // If https is required and request is http, redirect. 1194 if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { 1195 if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { 1196 wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); 1197 exit; 1198 } else { 1199 wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); 1200 exit; 1201 } 1202 } 1203 1204 /** 1205 * Filters the authentication redirect scheme. 1206 * 1207 * @since 2.9.0 1208 * 1209 * @param string $scheme Authentication redirect scheme. Default empty. 1210 */ 1211 $scheme = apply_filters( 'auth_redirect_scheme', '' ); 1212 1213 $user_id = wp_validate_auth_cookie( '', $scheme ); 1214 if ( $user_id ) { 1215 /** 1216 * Fires before the authentication redirect. 1217 * 1218 * @since 2.8.0 1219 * 1220 * @param int $user_id User ID. 1221 */ 1222 do_action( 'auth_redirect', $user_id ); 1223 1224 // If the user wants ssl but the session is not ssl, redirect. 1225 if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { 1226 if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { 1227 wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); 1228 exit; 1229 } else { 1230 wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); 1231 exit; 1232 } 1233 } 1234 1235 return; // The cookie is good, so we're done. 1236 } 1237 1238 // The cookie is no good, so force login. 1239 nocache_headers(); 1240 1241 if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) { 1242 $redirect = wp_get_referer(); 1243 } else { 1244 $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); 1245 } 1246 1247 $login_url = wp_login_url( $redirect, true ); 1248 1249 wp_redirect( $login_url ); 1250 exit; 1251 } 1252 endif; 1253 1254 if ( ! function_exists( 'check_admin_referer' ) ) : 1255 /** 1256 * Ensures intent by verifying that a user was referred from another admin page with the correct security nonce. 1257 * 1258 * This function ensures the user intends to perform a given action, which helps protect against clickjacking style 1259 * attacks. It verifies intent, not authorization, therefore it does not verify the user's capabilities. This should 1260 * be performed with `current_user_can()` or similar. 1261 * 1262 * If the nonce value is invalid, the function will exit with an "Are You Sure?" style message. 1263 * 1264 * @since 1.2.0 1265 * @since 2.5.0 The `$query_arg` parameter was added. 1266 * 1267 * @param int|string $action The nonce action. 1268 * @param string $query_arg Optional. Key to check for nonce in `$_REQUEST`. Default '_wpnonce'. 1269 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago, 1270 * 2 if the nonce is valid and generated between 12-24 hours ago. 1271 * False if the nonce is invalid. 1272 */ 1273 function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) { 1274 if ( -1 === $action ) { 1275 _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '3.2.0' ); 1276 } 1277 1278 $adminurl = strtolower( admin_url() ); 1279 $referer = strtolower( wp_get_referer() ); 1280 $result = isset( $_REQUEST[ $query_arg ] ) ? wp_verify_nonce( $_REQUEST[ $query_arg ], $action ) : false; 1281 1282 /** 1283 * Fires once the admin request has been validated or not. 1284 * 1285 * @since 1.5.1 1286 * 1287 * @param string $action The nonce action. 1288 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between 1289 * 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago. 1290 */ 1291 do_action( 'check_admin_referer', $action, $result ); 1292 1293 if ( ! $result && ! ( -1 === $action && str_starts_with( $referer, $adminurl ) ) ) { 1294 wp_nonce_ays( $action ); 1295 die(); 1296 } 1297 1298 return $result; 1299 } 1300 endif; 1301 1302 if ( ! function_exists( 'check_ajax_referer' ) ) : 1303 /** 1304 * Verifies the Ajax request to prevent processing requests external of the blog. 1305 * 1306 * @since 2.0.3 1307 * 1308 * @param int|string $action Action nonce. 1309 * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false, 1310 * `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce' 1311 * (in that order). Default false. 1312 * @param bool $stop Optional. Whether to stop early when the nonce cannot be verified. 1313 * Default true. 1314 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago, 1315 * 2 if the nonce is valid and generated between 12-24 hours ago. 1316 * False if the nonce is invalid. 1317 */ 1318 function check_ajax_referer( $action = -1, $query_arg = false, $stop = true ) { 1319 if ( -1 === $action ) { 1320 _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '4.7.0' ); 1321 } 1322 1323 $nonce = ''; 1324 1325 if ( $query_arg && isset( $_REQUEST[ $query_arg ] ) ) { 1326 $nonce = $_REQUEST[ $query_arg ]; 1327 } elseif ( isset( $_REQUEST['_ajax_nonce'] ) ) { 1328 $nonce = $_REQUEST['_ajax_nonce']; 1329 } elseif ( isset( $_REQUEST['_wpnonce'] ) ) { 1330 $nonce = $_REQUEST['_wpnonce']; 1331 } 1332 1333 $result = wp_verify_nonce( $nonce, $action ); 1334 1335 /** 1336 * Fires once the Ajax request has been validated or not. 1337 * 1338 * @since 2.1.0 1339 * 1340 * @param string $action The Ajax nonce action. 1341 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between 1342 * 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago. 1343 */ 1344 do_action( 'check_ajax_referer', $action, $result ); 1345 1346 if ( $stop && false === $result ) { 1347 if ( wp_doing_ajax() ) { 1348 wp_die( -1, 403 ); 1349 } else { 1350 die( '-1' ); 1351 } 1352 } 1353 1354 return $result; 1355 } 1356 endif; 1357 1358 if ( ! function_exists( 'wp_redirect' ) ) : 1359 /** 1360 * Redirects to another page. 1361 * 1362 * Note: wp_redirect() does not exit automatically, and should almost always be 1363 * followed by a call to `exit;`: 1364 * 1365 * wp_redirect( $url ); 1366 * exit; 1367 * 1368 * Exiting can also be selectively manipulated by using wp_redirect() as a conditional 1369 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters: 1370 * 1371 * if ( wp_redirect( $url ) ) { 1372 * exit; 1373 * } 1374 * 1375 * @since 1.5.1 1376 * @since 5.1.0 The `$x_redirect_by` parameter was added. 1377 * @since 5.4.0 On invalid status codes, wp_die() is called. 1378 * 1379 * @global bool $is_IIS 1380 * 1381 * @param string $location The path or URL to redirect to. 1382 * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). 1383 * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'. 1384 * @return bool False if the redirect was canceled, true otherwise. 1385 */ 1386 function wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) { 1387 global $is_IIS; 1388 1389 /** 1390 * Filters the redirect location. 1391 * 1392 * @since 2.1.0 1393 * 1394 * @param string $location The path or URL to redirect to. 1395 * @param int $status The HTTP response status code to use. 1396 */ 1397 $location = apply_filters( 'wp_redirect', $location, $status ); 1398 1399 /** 1400 * Filters the redirect HTTP response status code to use. 1401 * 1402 * @since 2.3.0 1403 * 1404 * @param int $status The HTTP response status code to use. 1405 * @param string $location The path or URL to redirect to. 1406 */ 1407 $status = apply_filters( 'wp_redirect_status', $status, $location ); 1408 1409 if ( ! $location ) { 1410 return false; 1411 } 1412 1413 if ( $status < 300 || 399 < $status ) { 1414 wp_die( __( 'HTTP redirect status code must be a redirection code, 3xx.' ) ); 1415 } 1416 1417 $location = wp_sanitize_redirect( $location ); 1418 1419 if ( ! $is_IIS && 'cgi-fcgi' !== PHP_SAPI ) { 1420 status_header( $status ); // This causes problems on IIS and some FastCGI setups. 1421 } 1422 1423 /** 1424 * Filters the X-Redirect-By header. 1425 * 1426 * Allows applications to identify themselves when they're doing a redirect. 1427 * 1428 * @since 5.1.0 1429 * 1430 * @param string|false $x_redirect_by The application doing the redirect or false to omit the header. 1431 * @param int $status Status code to use. 1432 * @param string $location The path to redirect to. 1433 */ 1434 $x_redirect_by = apply_filters( 'x_redirect_by', $x_redirect_by, $status, $location ); 1435 if ( is_string( $x_redirect_by ) ) { 1436 header( "X-Redirect-By: $x_redirect_by" ); 1437 } 1438 1439 header( "Location: $location", true, $status ); 1440 1441 return true; 1442 } 1443 endif; 1444 1445 if ( ! function_exists( 'wp_sanitize_redirect' ) ) : 1446 /** 1447 * Sanitizes a URL for use in a redirect. 1448 * 1449 * @since 2.3.0 1450 * 1451 * @param string $location The path to redirect to. 1452 * @return string Redirect-sanitized URL. 1453 */ 1454 function wp_sanitize_redirect( $location ) { 1455 // Encode spaces. 1456 $location = str_replace( ' ', '%20', $location ); 1457 1458 $regex = '/ 1459 ( 1460 (?: [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx 1461 | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2 1462 | [\xE1-\xEC][\x80-\xBF]{2} 1463 | \xED[\x80-\x9F][\x80-\xBF] 1464 | [\xEE-\xEF][\x80-\xBF]{2} 1465 | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3 1466 | [\xF1-\xF3][\x80-\xBF]{3} 1467 | \xF4[\x80-\x8F][\x80-\xBF]{2} 1468 ){1,40} # ...one or more times 1469 )/x'; 1470 $location = preg_replace_callback( $regex, '_wp_sanitize_utf8_in_redirect', $location ); 1471 $location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location ); 1472 $location = wp_kses_no_null( $location ); 1473 1474 // Remove %0D and %0A from location. 1475 $strip = array( '%0d', '%0a', '%0D', '%0A' ); 1476 return _deep_replace( $strip, $location ); 1477 } 1478 1479 /** 1480 * URL encodes UTF-8 characters in a URL. 1481 * 1482 * @ignore 1483 * @since 4.2.0 1484 * @access private 1485 * 1486 * @see wp_sanitize_redirect() 1487 * 1488 * @param array $matches RegEx matches against the redirect location. 1489 * @return string URL-encoded version of the first RegEx match. 1490 */ 1491 function _wp_sanitize_utf8_in_redirect( $matches ) { 1492 return urlencode( $matches[0] ); 1493 } 1494 endif; 1495 1496 if ( ! function_exists( 'wp_safe_redirect' ) ) : 1497 /** 1498 * Performs a safe (local) redirect, using wp_redirect(). 1499 * 1500 * Checks whether the $location is using an allowed host, if it has an absolute 1501 * path. A plugin can therefore set or remove allowed host(s) to or from the 1502 * list. 1503 * 1504 * If the host is not allowed, then the redirect defaults to wp-admin on the siteurl 1505 * instead. This prevents malicious redirects which redirect to another host, 1506 * but only used in a few places. 1507 * 1508 * Note: wp_safe_redirect() does not exit automatically, and should almost always be 1509 * followed by a call to `exit;`: 1510 * 1511 * wp_safe_redirect( $url ); 1512 * exit; 1513 * 1514 * Exiting can also be selectively manipulated by using wp_safe_redirect() as a conditional 1515 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters: 1516 * 1517 * if ( wp_safe_redirect( $url ) ) { 1518 * exit; 1519 * } 1520 * 1521 * @since 2.3.0 1522 * @since 5.1.0 The return value from wp_redirect() is now passed on, and the `$x_redirect_by` parameter was added. 1523 * 1524 * @param string $location The path or URL to redirect to. 1525 * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). 1526 * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'. 1527 * @return bool False if the redirect was canceled, true otherwise. 1528 */ 1529 function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) { 1530 1531 // Need to look at the URL the way it will end up in wp_redirect(). 1532 $location = wp_sanitize_redirect( $location ); 1533 1534 /** 1535 * Filters the redirect fallback URL for when the provided redirect is not safe (local). 1536 * 1537 * @since 4.3.0 1538 * 1539 * @param string $fallback_url The fallback URL to use by default. 1540 * @param int $status The HTTP response status code to use. 1541 */ 1542 $fallback_url = apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ); 1543 1544 $location = wp_validate_redirect( $location, $fallback_url ); 1545 1546 return wp_redirect( $location, $status, $x_redirect_by ); 1547 } 1548 endif; 1549 1550 if ( ! function_exists( 'wp_validate_redirect' ) ) : 1551 /** 1552 * Validates a URL for use in a redirect. 1553 * 1554 * Checks whether the $location is using an allowed host, if it has an absolute 1555 * path. A plugin can therefore set or remove allowed host(s) to or from the 1556 * list. 1557 * 1558 * If the host is not allowed, then the redirect is to $fallback_url supplied. 1559 * 1560 * @since 2.8.1 1561 * 1562 * @param string $location The redirect to validate. 1563 * @param string $fallback_url The value to return if $location is not allowed. 1564 * @return string Redirect-sanitized URL. 1565 */ 1566 function wp_validate_redirect( $location, $fallback_url = '' ) { 1567 $location = wp_sanitize_redirect( trim( $location, " \t\n\r\0\x08\x0B" ) ); 1568 // Browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'. 1569 if ( str_starts_with( $location, '//' ) ) { 1570 $location = 'http:' . $location; 1571 } 1572 1573 /* 1574 * In PHP 5 parse_url() may fail if the URL query part contains 'http://'. 1575 * See https://bugs.php.net/bug.php?id=38143 1576 */ 1577 $cut = strpos( $location, '?' ); 1578 $test = $cut ? substr( $location, 0, $cut ) : $location; 1579 1580 $lp = parse_url( $test ); 1581 1582 // Give up if malformed URL. 1583 if ( false === $lp ) { 1584 return $fallback_url; 1585 } 1586 1587 // Allow only 'http' and 'https' schemes. No 'data:', etc. 1588 if ( isset( $lp['scheme'] ) && ! ( 'http' === $lp['scheme'] || 'https' === $lp['scheme'] ) ) { 1589 return $fallback_url; 1590 } 1591 1592 if ( ! isset( $lp['host'] ) && ! empty( $lp['path'] ) && '/' !== $lp['path'][0] ) { 1593 $path = ''; 1594 if ( ! empty( $_SERVER['REQUEST_URI'] ) ) { 1595 $path = dirname( parse_url( 'http://placeholder' . $_SERVER['REQUEST_URI'], PHP_URL_PATH ) . '?' ); 1596 $path = wp_normalize_path( $path ); 1597 } 1598 $location = '/' . ltrim( $path . '/', '/' ) . $location; 1599 } 1600 1601 /* 1602 * Reject if certain components are set but host is not. 1603 * This catches URLs like https:host.com for which parse_url() does not set the host field. 1604 */ 1605 if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) { 1606 return $fallback_url; 1607 } 1608 1609 // Reject malformed components parse_url() can return on odd inputs. 1610 foreach ( array( 'user', 'pass', 'host' ) as $component ) { 1611 if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) { 1612 return $fallback_url; 1613 } 1614 } 1615 1616 $wpp = parse_url( home_url() ); 1617 1618 /** 1619 * Filters the list of allowed hosts to redirect to. 1620 * 1621 * @since 2.3.0 1622 * 1623 * @param string[] $hosts An array of allowed host names. 1624 * @param string $host The host name of the redirect destination; empty string if not set. 1625 */ 1626 $allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array( $wpp['host'] ), isset( $lp['host'] ) ? $lp['host'] : '' ); 1627 1628 if ( isset( $lp['host'] ) && ( ! in_array( $lp['host'], $allowed_hosts, true ) && strtolower( $wpp['host'] ) !== $lp['host'] ) ) { 1629 $location = $fallback_url; 1630 } 1631 1632 return $location; 1633 } 1634 endif; 1635 1636 if ( ! function_exists( 'wp_notify_postauthor' ) ) : 1637 /** 1638 * Notifies an author (and/or others) of a comment/trackback/pingback on a post. 1639 * 1640 * @since 1.0.0 1641 * 1642 * @param int|WP_Comment $comment_id Comment ID or WP_Comment object. 1643 * @param string $deprecated Not used. 1644 * @return bool True on completion. False if no email addresses were specified. 1645 */ 1646 function wp_notify_postauthor( $comment_id, $deprecated = null ) { 1647 if ( null !== $deprecated ) { 1648 _deprecated_argument( __FUNCTION__, '3.8.0' ); 1649 } 1650 1651 $comment = get_comment( $comment_id ); 1652 if ( empty( $comment ) || empty( $comment->comment_post_ID ) ) { 1653 return false; 1654 } 1655 1656 $post = get_post( $comment->comment_post_ID ); 1657 $author = get_userdata( $post->post_author ); 1658 1659 // Who to notify? By default, just the post author, but others can be added. 1660 $emails = array(); 1661 if ( $author ) { 1662 $emails[] = $author->user_email; 1663 } 1664 1665 /** 1666 * Filters the list of email addresses to receive a comment notification. 1667 * 1668 * By default, only post authors are notified of comments. This filter allows 1669 * others to be added. 1670 * 1671 * @since 3.7.0 1672 * 1673 * @param string[] $emails An array of email addresses to receive a comment notification. 1674 * @param string $comment_id The comment ID as a numeric string. 1675 */ 1676 $emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID ); 1677 $emails = array_filter( $emails ); 1678 1679 // If there are no addresses to send the comment to, bail. 1680 if ( ! count( $emails ) ) { 1681 return false; 1682 } 1683 1684 // Facilitate unsetting below without knowing the keys. 1685 $emails = array_flip( $emails ); 1686 1687 /** 1688 * Filters whether to notify comment authors of their comments on their own posts. 1689 * 1690 * By default, comment authors aren't notified of their comments on their own 1691 * posts. This filter allows you to override that. 1692 * 1693 * @since 3.8.0 1694 * 1695 * @param bool $notify Whether to notify the post author of their own comment. 1696 * Default false. 1697 * @param string $comment_id The comment ID as a numeric string. 1698 */ 1699 $notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID ); 1700 1701 // The comment was left by the author. 1702 if ( $author && ! $notify_author && (int) $comment->user_id === (int) $post->post_author ) { 1703 unset( $emails[ $author->user_email ] ); 1704 } 1705 1706 // The author moderated a comment on their own post. 1707 if ( $author && ! $notify_author && get_current_user_id() === (int) $post->post_author ) { 1708 unset( $emails[ $author->user_email ] ); 1709 } 1710 1711 // The post author is no longer a member of the blog. 1712 if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) { 1713 unset( $emails[ $author->user_email ] ); 1714 } 1715 1716 // If there's no email to send the comment to, bail, otherwise flip array back around for use below. 1717 if ( ! count( $emails ) ) { 1718 return false; 1719 } else { 1720 $emails = array_flip( $emails ); 1721 } 1722 1723 $comment_author_domain = ''; 1724 if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) { 1725 $comment_author_domain = gethostbyaddr( $comment->comment_author_IP ); 1726 } 1727 1728 /* 1729 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). 1730 * We want to reverse this for the plain text arena of emails. 1731 */ 1732 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 1733 $comment_content = wp_specialchars_decode( $comment->comment_content ); 1734 1735 $wp_email = 'wordpress@' . preg_replace( '#^www\.#', '', wp_parse_url( network_home_url(), PHP_URL_HOST ) ); 1736 1737 if ( '' === $comment->comment_author ) { 1738 $from = "From: \"$blogname\" <$wp_email>"; 1739 if ( '' !== $comment->comment_author_email ) { 1740 $reply_to = "Reply-To: $comment->comment_author_email"; 1741 } 1742 } else { 1743 $from = "From: \"$comment->comment_author\" <$wp_email>"; 1744 if ( '' !== $comment->comment_author_email ) { 1745 $reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>"; 1746 } 1747 } 1748 1749 $message_headers = "$from\n" 1750 . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n"; 1751 1752 if ( isset( $reply_to ) ) { 1753 $message_headers .= $reply_to . "\n"; 1754 } 1755 1756 /** 1757 * Filters the comment notification email headers. 1758 * 1759 * @since 1.5.2 1760 * 1761 * @param string $message_headers Headers for the comment notification email. 1762 * @param string $comment_id Comment ID as a numeric string. 1763 */ 1764 $message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID ); 1765 1766 foreach ( $emails as $email ) { 1767 $user = get_user_by( 'email', $email ); 1768 1769 if ( $user ) { 1770 $switched_locale = switch_to_user_locale( $user->ID ); 1771 } else { 1772 $switched_locale = switch_to_locale( get_locale() ); 1773 } 1774 1775 switch ( $comment->comment_type ) { 1776 case 'trackback': 1777 /* translators: %s: Post title. */ 1778 $notify_message = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n"; 1779 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */ 1780 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1781 /* translators: %s: Trackback/pingback/comment author URL. */ 1782 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1783 /* translators: %s: Comment text. */ 1784 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n"; 1785 $notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n"; 1786 /* translators: Trackback notification email subject. 1: Site title, 2: Post title. */ 1787 $subject = sprintf( __( '[%1$s] Trackback: "%2$s"' ), $blogname, $post->post_title ); 1788 break; 1789 1790 case 'pingback': 1791 /* translators: %s: Post title. */ 1792 $notify_message = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n"; 1793 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */ 1794 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1795 /* translators: %s: Trackback/pingback/comment author URL. */ 1796 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1797 /* translators: %s: Comment text. */ 1798 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n"; 1799 $notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n"; 1800 /* translators: Pingback notification email subject. 1: Site title, 2: Post title. */ 1801 $subject = sprintf( __( '[%1$s] Pingback: "%2$s"' ), $blogname, $post->post_title ); 1802 break; 1803 1804 default: // Comments. 1805 /* translators: %s: Post title. */ 1806 $notify_message = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n"; 1807 /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */ 1808 $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1809 /* translators: %s: Comment author email. */ 1810 $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n"; 1811 /* translators: %s: Trackback/pingback/comment author URL. */ 1812 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1813 1814 if ( $comment->comment_parent && user_can( $post->post_author, 'edit_comment', $comment->comment_parent ) ) { 1815 /* translators: Comment moderation. %s: Parent comment edit URL. */ 1816 $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n"; 1817 } 1818 1819 /* translators: %s: Comment text. */ 1820 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n"; 1821 $notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n"; 1822 /* translators: Comment notification email subject. 1: Site title, 2: Post title. */ 1823 $subject = sprintf( __( '[%1$s] Comment: "%2$s"' ), $blogname, $post->post_title ); 1824 break; 1825 } 1826 1827 $notify_message .= get_permalink( $comment->comment_post_ID ) . "#comments\r\n\r\n"; 1828 /* translators: %s: Comment URL. */ 1829 $notify_message .= sprintf( __( 'Permalink: %s' ), get_comment_link( $comment ) ) . "\r\n"; 1830 1831 if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) { 1832 if ( EMPTY_TRASH_DAYS ) { 1833 /* translators: Comment moderation. %s: Comment action URL. */ 1834 $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n"; 1835 } else { 1836 /* translators: Comment moderation. %s: Comment action URL. */ 1837 $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n"; 1838 } 1839 /* translators: Comment moderation. %s: Comment action URL. */ 1840 $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n"; 1841 } 1842 1843 /** 1844 * Filters the comment notification email text. 1845 * 1846 * @since 1.5.2 1847 * 1848 * @param string $notify_message The comment notification email text. 1849 * @param string $comment_id Comment ID as a numeric string. 1850 */ 1851 $notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID ); 1852 1853 /** 1854 * Filters the comment notification email subject. 1855 * 1856 * @since 1.5.2 1857 * 1858 * @param string $subject The comment notification email subject. 1859 * @param string $comment_id Comment ID as a numeric string. 1860 */ 1861 $subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID ); 1862 1863 wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers ); 1864 1865 if ( $switched_locale ) { 1866 restore_previous_locale(); 1867 } 1868 } 1869 1870 return true; 1871 } 1872 endif; 1873 1874 if ( ! function_exists( 'wp_notify_moderator' ) ) : 1875 /** 1876 * Notifies the moderator of the site about a new comment that is awaiting approval. 1877 * 1878 * @since 1.0.0 1879 * 1880 * @global wpdb $wpdb WordPress database abstraction object. 1881 * 1882 * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator 1883 * should be notified, overriding the site setting. 1884 * 1885 * @param int $comment_id Comment ID. 1886 * @return true Always returns true. 1887 */ 1888 function wp_notify_moderator( $comment_id ) { 1889 global $wpdb; 1890 1891 $maybe_notify = get_option( 'moderation_notify' ); 1892 1893 /** 1894 * Filters whether to send the site moderator email notifications, overriding the site setting. 1895 * 1896 * @since 4.4.0 1897 * 1898 * @param bool $maybe_notify Whether to notify blog moderator. 1899 * @param int $comment_id The ID of the comment for the notification. 1900 */ 1901 $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id ); 1902 1903 if ( ! $maybe_notify ) { 1904 return true; 1905 } 1906 1907 $comment = get_comment( $comment_id ); 1908 $post = get_post( $comment->comment_post_ID ); 1909 $user = get_userdata( $post->post_author ); 1910 // Send to the administration and to the post author if the author can modify the comment. 1911 $emails = array( get_option( 'admin_email' ) ); 1912 if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) { 1913 if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) { 1914 $emails[] = $user->user_email; 1915 } 1916 } 1917 1918 $comment_author_domain = ''; 1919 if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) { 1920 $comment_author_domain = gethostbyaddr( $comment->comment_author_IP ); 1921 } 1922 1923 $comments_waiting = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" ); 1924 1925 /* 1926 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). 1927 * We want to reverse this for the plain text arena of emails. 1928 */ 1929 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 1930 $comment_content = wp_specialchars_decode( $comment->comment_content ); 1931 1932 $message_headers = ''; 1933 1934 /** 1935 * Filters the list of recipients for comment moderation emails. 1936 * 1937 * @since 3.7.0 1938 * 1939 * @param string[] $emails List of email addresses to notify for comment moderation. 1940 * @param int $comment_id Comment ID. 1941 */ 1942 $emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id ); 1943 1944 /** 1945 * Filters the comment moderation email headers. 1946 * 1947 * @since 2.8.0 1948 * 1949 * @param string $message_headers Headers for the comment moderation email. 1950 * @param int $comment_id Comment ID. 1951 */ 1952 $message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id ); 1953 1954 foreach ( $emails as $email ) { 1955 $user = get_user_by( 'email', $email ); 1956 1957 if ( $user ) { 1958 $switched_locale = switch_to_user_locale( $user->ID ); 1959 } else { 1960 $switched_locale = switch_to_locale( get_locale() ); 1961 } 1962 1963 switch ( $comment->comment_type ) { 1964 case 'trackback': 1965 /* translators: %s: Post title. */ 1966 $notify_message = sprintf( __( 'A new trackback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n"; 1967 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n"; 1968 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */ 1969 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1970 /* translators: %s: Trackback/pingback/comment author URL. */ 1971 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1972 $notify_message .= __( 'Trackback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n"; 1973 break; 1974 1975 case 'pingback': 1976 /* translators: %s: Post title. */ 1977 $notify_message = sprintf( __( 'A new pingback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n"; 1978 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n"; 1979 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */ 1980 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1981 /* translators: %s: Trackback/pingback/comment author URL. */ 1982 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1983 $notify_message .= __( 'Pingback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n"; 1984 break; 1985 1986 default: // Comments. 1987 /* translators: %s: Post title. */ 1988 $notify_message = sprintf( __( 'A new comment on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n"; 1989 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n"; 1990 /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */ 1991 $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n"; 1992 /* translators: %s: Comment author email. */ 1993 $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n"; 1994 /* translators: %s: Trackback/pingback/comment author URL. */ 1995 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n"; 1996 1997 if ( $comment->comment_parent ) { 1998 /* translators: Comment moderation. %s: Parent comment edit URL. */ 1999 $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n"; 2000 } 2001 2002 /* translators: %s: Comment text. */ 2003 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n"; 2004 break; 2005 } 2006 2007 /* translators: Comment moderation. %s: Comment action URL. */ 2008 $notify_message .= sprintf( __( 'Approve it: %s' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n"; 2009 2010 if ( EMPTY_TRASH_DAYS ) { 2011 /* translators: Comment moderation. %s: Comment action URL. */ 2012 $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" ) ) . "\r\n"; 2013 } else { 2014 /* translators: Comment moderation. %s: Comment action URL. */ 2015 $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" ) ) . "\r\n"; 2016 } 2017 2018 /* translators: Comment moderation. %s: Comment action URL. */ 2019 $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" ) ) . "\r\n"; 2020 2021 $notify_message .= sprintf( 2022 /* translators: Comment moderation. %s: Number of comments awaiting approval. */ 2023 _n( 2024 'Currently %s comment is waiting for approval. Please visit the moderation panel:', 2025 'Currently %s comments are waiting for approval. Please visit the moderation panel:', 2026 $comments_waiting 2027 ), 2028 number_format_i18n( $comments_waiting ) 2029 ) . "\r\n"; 2030 $notify_message .= admin_url( 'edit-comments.php?comment_status=moderated#wpbody-content' ) . "\r\n"; 2031 2032 /* translators: Comment moderation notification email subject. 1: Site title, 2: Post title. */ 2033 $subject = sprintf( __( '[%1$s] Please moderate: "%2$s"' ), $blogname, $post->post_title ); 2034 2035 /** 2036 * Filters the comment moderation email text. 2037 * 2038 * @since 1.5.2 2039 * 2040 * @param string $notify_message Text of the comment moderation email. 2041 * @param int $comment_id Comment ID. 2042 */ 2043 $notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id ); 2044 2045 /** 2046 * Filters the comment moderation email subject. 2047 * 2048 * @since 1.5.2 2049 * 2050 * @param string $subject Subject of the comment moderation email. 2051 * @param int $comment_id Comment ID. 2052 */ 2053 $subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id ); 2054 2055 wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers ); 2056 2057 if ( $switched_locale ) { 2058 restore_previous_locale(); 2059 } 2060 } 2061 2062 return true; 2063 } 2064 endif; 2065 2066 if ( ! function_exists( 'wp_password_change_notification' ) ) : 2067 /** 2068 * Notifies the blog admin of a user changing password, normally via email. 2069 * 2070 * @since 2.7.0 2071 * 2072 * @param WP_User $user User object. 2073 */ 2074 function wp_password_change_notification( $user ) { 2075 /* 2076 * Send a copy of password change notification to the admin, 2077 * but check to see if it's the admin whose password we're changing, and skip this. 2078 */ 2079 if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) { 2080 2081 $admin_user = get_user_by( 'email', get_option( 'admin_email' ) ); 2082 2083 if ( $admin_user ) { 2084 $switched_locale = switch_to_user_locale( $admin_user->ID ); 2085 } else { 2086 $switched_locale = switch_to_locale( get_locale() ); 2087 } 2088 2089 /* translators: %s: User name. */ 2090 $message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n"; 2091 /* 2092 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). 2093 * We want to reverse this for the plain text arena of emails. 2094 */ 2095 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 2096 2097 $wp_password_change_notification_email = array( 2098 'to' => get_option( 'admin_email' ), 2099 /* translators: Password change notification email subject. %s: Site title. */ 2100 'subject' => __( '[%s] Password Changed' ), 2101 'message' => $message, 2102 'headers' => '', 2103 ); 2104 2105 /** 2106 * Filters the contents of the password change notification email sent to the site admin. 2107 * 2108 * @since 4.9.0 2109 * 2110 * @param array $wp_password_change_notification_email { 2111 * Used to build wp_mail(). 2112 * 2113 * @type string $to The intended recipient - site admin email address. 2114 * @type string $subject The subject of the email. 2115 * @type string $message The body of the email. 2116 * @type string $headers The headers of the email. 2117 * } 2118 * @param WP_User $user User object for user whose password was changed. 2119 * @param string $blogname The site title. 2120 */ 2121 $wp_password_change_notification_email = apply_filters( 'wp_password_change_notification_email', $wp_password_change_notification_email, $user, $blogname ); 2122 2123 wp_mail( 2124 $wp_password_change_notification_email['to'], 2125 wp_specialchars_decode( sprintf( $wp_password_change_notification_email['subject'], $blogname ) ), 2126 $wp_password_change_notification_email['message'], 2127 $wp_password_change_notification_email['headers'] 2128 ); 2129 2130 if ( $switched_locale ) { 2131 restore_previous_locale(); 2132 } 2133 } 2134 } 2135 endif; 2136 2137 if ( ! function_exists( 'wp_new_user_notification' ) ) : 2138 /** 2139 * Emails login credentials to a newly-registered user. 2140 * 2141 * A new user registration notification is also sent to admin email. 2142 * 2143 * @since 2.0.0 2144 * @since 4.3.0 The `$plaintext_pass` parameter was changed to `$notify`. 2145 * @since 4.3.1 The `$plaintext_pass` parameter was deprecated. `$notify` added as a third parameter. 2146 * @since 4.6.0 The `$notify` parameter accepts 'user' for sending notification only to the user created. 2147 * 2148 * @param int $user_id User ID. 2149 * @param null $deprecated Not used (argument deprecated). 2150 * @param string $notify Optional. Type of notification that should happen. Accepts 'admin' or an empty 2151 * string (admin only), 'user', or 'both' (admin and user). Default empty. 2152 */ 2153 function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) { 2154 if ( null !== $deprecated ) { 2155 _deprecated_argument( __FUNCTION__, '4.3.1' ); 2156 } 2157 2158 // Accepts only 'user', 'admin' , 'both' or default '' as $notify. 2159 if ( ! in_array( $notify, array( 'user', 'admin', 'both', '' ), true ) ) { 2160 return; 2161 } 2162 2163 $user = get_userdata( $user_id ); 2164 2165 /* 2166 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). 2167 * We want to reverse this for the plain text arena of emails. 2168 */ 2169 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 2170 2171 /** 2172 * Filters whether the admin is notified of a new user registration. 2173 * 2174 * @since 6.1.0 2175 * 2176 * @param bool $send Whether to send the email. Default true. 2177 * @param WP_User $user User object for new user. 2178 */ 2179 $send_notification_to_admin = apply_filters( 'wp_send_new_user_notification_to_admin', true, $user ); 2180 2181 if ( 'user' !== $notify && true === $send_notification_to_admin ) { 2182 2183 $admin_user = get_user_by( 'email', get_option( 'admin_email' ) ); 2184 2185 if ( $admin_user ) { 2186 $switched_locale = switch_to_user_locale( $admin_user->ID ); 2187 } else { 2188 $switched_locale = switch_to_locale( get_locale() ); 2189 } 2190 2191 /* translators: %s: Site title. */ 2192 $message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n"; 2193 /* translators: %s: User login. */ 2194 $message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; 2195 /* translators: %s: User email address. */ 2196 $message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n"; 2197 2198 $wp_new_user_notification_email_admin = array( 2199 'to' => get_option( 'admin_email' ), 2200 /* translators: New user registration notification email subject. %s: Site title. */ 2201 'subject' => __( '[%s] New User Registration' ), 2202 'message' => $message, 2203 'headers' => '', 2204 ); 2205 2206 /** 2207 * Filters the contents of the new user notification email sent to the site admin. 2208 * 2209 * @since 4.9.0 2210 * 2211 * @param array $wp_new_user_notification_email_admin { 2212 * Used to build wp_mail(). 2213 * 2214 * @type string $to The intended recipient - site admin email address. 2215 * @type string $subject The subject of the email. 2216 * @type string $message The body of the email. 2217 * @type string $headers The headers of the email. 2218 * } 2219 * @param WP_User $user User object for new user. 2220 * @param string $blogname The site title. 2221 */ 2222 $wp_new_user_notification_email_admin = apply_filters( 'wp_new_user_notification_email_admin', $wp_new_user_notification_email_admin, $user, $blogname ); 2223 2224 wp_mail( 2225 $wp_new_user_notification_email_admin['to'], 2226 wp_specialchars_decode( sprintf( $wp_new_user_notification_email_admin['subject'], $blogname ) ), 2227 $wp_new_user_notification_email_admin['message'], 2228 $wp_new_user_notification_email_admin['headers'] 2229 ); 2230 2231 if ( $switched_locale ) { 2232 restore_previous_locale(); 2233 } 2234 } 2235 2236 /** 2237 * Filters whether the user is notified of their new user registration. 2238 * 2239 * @since 6.1.0 2240 * 2241 * @param bool $send Whether to send the email. Default true. 2242 * @param WP_User $user User object for new user. 2243 */ 2244 $send_notification_to_user = apply_filters( 'wp_send_new_user_notification_to_user', true, $user ); 2245 2246 // `$deprecated` was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification. 2247 if ( 'admin' === $notify || true !== $send_notification_to_user || ( empty( $deprecated ) && empty( $notify ) ) ) { 2248 return; 2249 } 2250 2251 $key = get_password_reset_key( $user ); 2252 if ( is_wp_error( $key ) ) { 2253 return; 2254 } 2255 2256 $switched_locale = switch_to_user_locale( $user_id ); 2257 2258 /* translators: %s: User login. */ 2259 $message = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; 2260 $message .= __( 'To set your password, visit the following address:' ) . "\r\n\r\n"; 2261 2262 /* 2263 * Since some user login names end in a period, this could produce ambiguous URLs that 2264 * end in a period. To avoid the ambiguity, ensure that the login is not the last query 2265 * arg in the URL. If moving it to the end, a trailing period will need to be escaped. 2266 * 2267 * @see https://core.trac.wordpress.org/tickets/42957 2268 */ 2269 $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user->user_login ) . "&key=$key&action=rp", 'login' ) . "\r\n\r\n"; 2270 2271 $message .= wp_login_url() . "\r\n"; 2272 2273 $wp_new_user_notification_email = array( 2274 'to' => $user->user_email, 2275 /* translators: Login details notification email subject. %s: Site title. */ 2276 'subject' => __( '[%s] Login Details' ), 2277 'message' => $message, 2278 'headers' => '', 2279 ); 2280 2281 /** 2282 * Filters the contents of the new user notification email sent to the new user. 2283 * 2284 * @since 4.9.0 2285 * 2286 * @param array $wp_new_user_notification_email { 2287 * Used to build wp_mail(). 2288 * 2289 * @type string $to The intended recipient - New user email address. 2290 * @type string $subject The subject of the email. 2291 * @type string $message The body of the email. 2292 * @type string $headers The headers of the email. 2293 * } 2294 * @param WP_User $user User object for new user. 2295 * @param string $blogname The site title. 2296 */ 2297 $wp_new_user_notification_email = apply_filters( 'wp_new_user_notification_email', $wp_new_user_notification_email, $user, $blogname ); 2298 2299 wp_mail( 2300 $wp_new_user_notification_email['to'], 2301 wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ), 2302 $wp_new_user_notification_email['message'], 2303 $wp_new_user_notification_email['headers'] 2304 ); 2305 2306 if ( $switched_locale ) { 2307 restore_previous_locale(); 2308 } 2309 } 2310 endif; 2311 2312 if ( ! function_exists( 'wp_nonce_tick' ) ) : 2313 /** 2314 * Returns the time-dependent variable for nonce creation. 2315 * 2316 * A nonce has a lifespan of two ticks. Nonces in their second tick may be 2317 * updated, e.g. by autosave. 2318 * 2319 * @since 2.5.0 2320 * @since 6.1.0 Added `$action` argument. 2321 * 2322 * @param string|int $action Optional. The nonce action. Default -1. 2323 * @return float Float value rounded up to the next highest integer. 2324 */ 2325 function wp_nonce_tick( $action = -1 ) { 2326 /** 2327 * Filters the lifespan of nonces in seconds. 2328 * 2329 * @since 2.5.0 2330 * @since 6.1.0 Added `$action` argument to allow for more targeted filters. 2331 * 2332 * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day. 2333 * @param string|int $action The nonce action, or -1 if none was provided. 2334 */ 2335 $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS, $action ); 2336 2337 return ceil( time() / ( $nonce_life / 2 ) ); 2338 } 2339 endif; 2340 2341 if ( ! function_exists( 'wp_verify_nonce' ) ) : 2342 /** 2343 * Verifies that a correct security nonce was used with time limit. 2344 * 2345 * A nonce is valid for 24 hours (by default). 2346 * 2347 * @since 2.0.3 2348 * 2349 * @param string $nonce Nonce value that was used for verification, usually via a form field. 2350 * @param string|int $action Should give context to what is taking place and be the same when nonce was created. 2351 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago, 2352 * 2 if the nonce is valid and generated between 12-24 hours ago. 2353 * False if the nonce is invalid. 2354 */ 2355 function wp_verify_nonce( $nonce, $action = -1 ) { 2356 $nonce = (string) $nonce; 2357 $user = wp_get_current_user(); 2358 $uid = (int) $user->ID; 2359 if ( ! $uid ) { 2360 /** 2361 * Filters whether the user who generated the nonce is logged out. 2362 * 2363 * @since 3.5.0 2364 * 2365 * @param int $uid ID of the nonce-owning user. 2366 * @param string|int $action The nonce action, or -1 if none was provided. 2367 */ 2368 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); 2369 } 2370 2371 if ( empty( $nonce ) ) { 2372 return false; 2373 } 2374 2375 $token = wp_get_session_token(); 2376 $i = wp_nonce_tick( $action ); 2377 2378 // Nonce generated 0-12 hours ago. 2379 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 2380 if ( hash_equals( $expected, $nonce ) ) { 2381 return 1; 2382 } 2383 2384 // Nonce generated 12-24 hours ago. 2385 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 2386 if ( hash_equals( $expected, $nonce ) ) { 2387 return 2; 2388 } 2389 2390 /** 2391 * Fires when nonce verification fails. 2392 * 2393 * @since 4.4.0 2394 * 2395 * @param string $nonce The invalid nonce. 2396 * @param string|int $action The nonce action. 2397 * @param WP_User $user The current user object. 2398 * @param string $token The user's session token. 2399 */ 2400 do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token ); 2401 2402 // Invalid nonce. 2403 return false; 2404 } 2405 endif; 2406 2407 if ( ! function_exists( 'wp_create_nonce' ) ) : 2408 /** 2409 * Creates a cryptographic token tied to a specific action, user, user session, 2410 * and window of time. 2411 * 2412 * @since 2.0.3 2413 * @since 4.0.0 Session tokens were integrated with nonce creation. 2414 * 2415 * @param string|int $action Scalar value to add context to the nonce. 2416 * @return string The token. 2417 */ 2418 function wp_create_nonce( $action = -1 ) { 2419 $user = wp_get_current_user(); 2420 $uid = (int) $user->ID; 2421 if ( ! $uid ) { 2422 /** This filter is documented in wp-includes/pluggable.php */ 2423 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); 2424 } 2425 2426 $token = wp_get_session_token(); 2427 $i = wp_nonce_tick( $action ); 2428 2429 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 2430 } 2431 endif; 2432 2433 if ( ! function_exists( 'wp_salt' ) ) : 2434 /** 2435 * Returns a salt to add to hashes. 2436 * 2437 * Salts are created using secret keys. Secret keys are located in two places: 2438 * in the database and in the wp-config.php file. The secret key in the database 2439 * is randomly generated and will be appended to the secret keys in wp-config.php. 2440 * 2441 * The secret keys in wp-config.php should be updated to strong, random keys to maximize 2442 * security. Below is an example of how the secret key constants are defined. 2443 * Do not paste this example directly into wp-config.php. Instead, have a 2444 * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just 2445 * for you. 2446 * 2447 * define('AUTH_KEY', ' Xakm<o xQy rw4EMsLKM-?!T+,PFF})H4lzcW57AF0U@N@< >M%G4Yt>f`z]MON'); 2448 * define('SECURE_AUTH_KEY', 'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~'); 2449 * define('LOGGED_IN_KEY', '|i|Ux`9<p-h$aFf(qnT:sDO:D1P^wZ$$/Ra@miTJi9G;ddp_<q}6H1)o|a +&JCM'); 2450 * define('NONCE_KEY', '%:R{[P|,s.KuMltH5}cI;/k<Gx~j!f0I)m_sIyu+&NJZ)-iO>z7X>QYR0Z_XnZ@|'); 2451 * define('AUTH_SALT', 'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW'); 2452 * define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W<I`.JjPi)<Bmf1v,HpGe}T1:Xt7n'); 2453 * define('LOGGED_IN_SALT', '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm'); 2454 * define('NONCE_SALT', 'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT'); 2455 * 2456 * Salting passwords helps against tools which has stored hashed values of 2457 * common dictionary strings. The added values makes it harder to crack. 2458 * 2459 * @since 2.5.0 2460 * 2461 * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php 2462 * 2463 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). 2464 * @return string Salt value 2465 */ 2466 function wp_salt( $scheme = 'auth' ) { 2467 static $cached_salts = array(); 2468 if ( isset( $cached_salts[ $scheme ] ) ) { 2469 /** 2470 * Filters the WordPress salt. 2471 * 2472 * @since 2.5.0 2473 * 2474 * @param string $cached_salt Cached salt for the given scheme. 2475 * @param string $scheme Authentication scheme. Values include 'auth', 2476 * 'secure_auth', 'logged_in', and 'nonce'. 2477 */ 2478 return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme ); 2479 } 2480 2481 static $duplicated_keys; 2482 if ( null === $duplicated_keys ) { 2483 $duplicated_keys = array(); 2484 2485 foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) { 2486 foreach ( array( 'KEY', 'SALT' ) as $second ) { 2487 if ( ! defined( "{$first}_{$second}" ) ) { 2488 continue; 2489 } 2490 $value = constant( "{$first}_{$second}" ); 2491 $duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] ); 2492 } 2493 } 2494 2495 $duplicated_keys['put your unique phrase here'] = true; 2496 2497 /* 2498 * translators: This string should only be translated if wp-config-sample.php is localized. 2499 * You can check the localized release package or 2500 * https://i18n.svn.wordpress.org/<locale code>/branches/<wp version>/dist/wp-config-sample.php 2501 */ 2502 $duplicated_keys[ __( 'put your unique phrase here' ) ] = true; 2503 } 2504 2505 /* 2506 * Determine which options to prime. 2507 * 2508 * If the salt keys are undefined, use a duplicate value or the 2509 * default `put your unique phrase here` value the salt will be 2510 * generated via `wp_generate_password()` and stored as a site 2511 * option. These options will be primed to avoid repeated 2512 * database requests for undefined salts. 2513 */ 2514 $options_to_prime = array(); 2515 foreach ( array( 'auth', 'secure_auth', 'logged_in', 'nonce' ) as $key ) { 2516 foreach ( array( 'key', 'salt' ) as $second ) { 2517 $const = strtoupper( "{$key}_{$second}" ); 2518 if ( ! defined( $const ) || true === $duplicated_keys[ constant( $const ) ] ) { 2519 $options_to_prime[] = "{$key}_{$second}"; 2520 } 2521 } 2522 } 2523 2524 if ( ! empty( $options_to_prime ) ) { 2525 /* 2526 * Also prime `secret_key` used for undefined salting schemes. 2527 * 2528 * If the scheme is unknown, the default value for `secret_key` will be 2529 * used too for the salt. This should rarely happen, so the option is only 2530 * primed if other salts are undefined. 2531 * 2532 * At this point of execution it is known that a database call will be made 2533 * to prime salts, so the `secret_key` option can be primed regardless of the 2534 * constants status. 2535 */ 2536 $options_to_prime[] = 'secret_key'; 2537 wp_prime_site_option_caches( $options_to_prime ); 2538 } 2539 2540 $values = array( 2541 'key' => '', 2542 'salt' => '', 2543 ); 2544 if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) { 2545 $values['key'] = SECRET_KEY; 2546 } 2547 if ( 'auth' === $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) { 2548 $values['salt'] = SECRET_SALT; 2549 } 2550 2551 if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ), true ) ) { 2552 foreach ( array( 'key', 'salt' ) as $type ) { 2553 $const = strtoupper( "{$scheme}_{$type}" ); 2554 if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) { 2555 $values[ $type ] = constant( $const ); 2556 } elseif ( ! $values[ $type ] ) { 2557 $values[ $type ] = get_site_option( "{$scheme}_{$type}" ); 2558 if ( ! $values[ $type ] ) { 2559 $values[ $type ] = wp_generate_password( 64, true, true ); 2560 update_site_option( "{$scheme}_{$type}", $values[ $type ] ); 2561 } 2562 } 2563 } 2564 } else { 2565 if ( ! $values['key'] ) { 2566 $values['key'] = get_site_option( 'secret_key' ); 2567 if ( ! $values['key'] ) { 2568 $values['key'] = wp_generate_password( 64, true, true ); 2569 update_site_option( 'secret_key', $values['key'] ); 2570 } 2571 } 2572 $values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] ); 2573 } 2574 2575 $cached_salts[ $scheme ] = $values['key'] . $values['salt']; 2576 2577 /** This filter is documented in wp-includes/pluggable.php */ 2578 return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme ); 2579 } 2580 endif; 2581 2582 if ( ! function_exists( 'wp_hash' ) ) : 2583 /** 2584 * Gets hash of given string. 2585 * 2586 * @since 2.0.3 2587 * 2588 * @param string $data Plain text to hash. 2589 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). 2590 * @return string Hash of $data. 2591 */ 2592 function wp_hash( $data, $scheme = 'auth' ) { 2593 $salt = wp_salt( $scheme ); 2594 2595 return hash_hmac( 'md5', $data, $salt ); 2596 } 2597 endif; 2598 2599 if ( ! function_exists( 'wp_hash_password' ) ) : 2600 /** 2601 * Creates a hash of a plain text password. 2602 * 2603 * For integration with other applications, this function can be overwritten to 2604 * instead use the other package password hashing algorithm. 2605 * 2606 * @since 2.5.0 2607 * 2608 * @global PasswordHash $wp_hasher PHPass object. 2609 * 2610 * @param string $password Plain text user password to hash. 2611 * @return string The hash string of the password. 2612 */ 2613 function wp_hash_password( $password ) { 2614 global $wp_hasher; 2615 2616 if ( empty( $wp_hasher ) ) { 2617 require_once ABSPATH . WPINC . '/class-phpass.php'; 2618 // By default, use the portable hash from phpass. 2619 $wp_hasher = new PasswordHash( 8, true ); 2620 } 2621 2622 return $wp_hasher->HashPassword( trim( $password ) ); 2623 } 2624 endif; 2625 2626 if ( ! function_exists( 'wp_check_password' ) ) : 2627 /** 2628 * Checks a plaintext password against a hashed password. 2629 * 2630 * Maintains compatibility between old version and the new cookie authentication 2631 * protocol using PHPass library. The $hash parameter is the encrypted password 2632 * and the function compares the plain text password when encrypted similarly 2633 * against the already encrypted password to see if they match. 2634 * 2635 * For integration with other applications, this function can be overwritten to 2636 * instead use the other package password hashing algorithm. 2637 * 2638 * @since 2.5.0 2639 * 2640 * @global PasswordHash $wp_hasher PHPass object used for checking the password 2641 * against the $hash + $password. 2642 * @uses PasswordHash::CheckPassword 2643 * 2644 * @param string $password Plaintext user's password. 2645 * @param string $hash Hash of the user's password to check against. 2646 * @param string|int $user_id Optional. User ID. 2647 * @return bool False, if the $password does not match the hashed password. 2648 */ 2649 function wp_check_password( $password, $hash, $user_id = '' ) { 2650 global $wp_hasher; 2651 2652 // If the hash is still md5... 2653 if ( strlen( $hash ) <= 32 ) { 2654 $check = hash_equals( $hash, md5( $password ) ); 2655 if ( $check && $user_id ) { 2656 // Rehash using new hash. 2657 wp_set_password( $password, $user_id ); 2658 $hash = wp_hash_password( $password ); 2659 } 2660 2661 /** 2662 * Filters whether the plaintext password matches the encrypted password. 2663 * 2664 * @since 2.5.0 2665 * 2666 * @param bool $check Whether the passwords match. 2667 * @param string $password The plaintext password. 2668 * @param string $hash The hashed password. 2669 * @param string|int $user_id User ID. Can be empty. 2670 */ 2671 return apply_filters( 'check_password', $check, $password, $hash, $user_id ); 2672 } 2673 2674 /* 2675 * If the stored hash is longer than an MD5, 2676 * presume the new style phpass portable hash. 2677 */ 2678 if ( empty( $wp_hasher ) ) { 2679 require_once ABSPATH . WPINC . '/class-phpass.php'; 2680 // By default, use the portable hash from phpass. 2681 $wp_hasher = new PasswordHash( 8, true ); 2682 } 2683 2684 $check = $wp_hasher->CheckPassword( $password, $hash ); 2685 2686 /** This filter is documented in wp-includes/pluggable.php */ 2687 return apply_filters( 'check_password', $check, $password, $hash, $user_id ); 2688 } 2689 endif; 2690 2691 if ( ! function_exists( 'wp_generate_password' ) ) : 2692 /** 2693 * Generates a random password drawn from the defined set of characters. 2694 * 2695 * Uses wp_rand() to create passwords with far less predictability 2696 * than similar native PHP functions like `rand()` or `mt_rand()`. 2697 * 2698 * @since 2.5.0 2699 * 2700 * @param int $length Optional. The length of password to generate. Default 12. 2701 * @param bool $special_chars Optional. Whether to include standard special characters. 2702 * Default true. 2703 * @param bool $extra_special_chars Optional. Whether to include other special characters. 2704 * Used when generating secret keys and salts. Default false. 2705 * @return string The random password. 2706 */ 2707 function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) { 2708 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 2709 if ( $special_chars ) { 2710 $chars .= '!@#$%^&*()'; 2711 } 2712 if ( $extra_special_chars ) { 2713 $chars .= '-_ []{}<>~`+=,.;:/?|'; 2714 } 2715 2716 $password = ''; 2717 for ( $i = 0; $i < $length; $i++ ) { 2718 $password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 ); 2719 } 2720 2721 /** 2722 * Filters the randomly-generated password. 2723 * 2724 * @since 3.0.0 2725 * @since 5.3.0 Added the `$length`, `$special_chars`, and `$extra_special_chars` parameters. 2726 * 2727 * @param string $password The generated password. 2728 * @param int $length The length of password to generate. 2729 * @param bool $special_chars Whether to include standard special characters. 2730 * @param bool $extra_special_chars Whether to include other special characters. 2731 */ 2732 return apply_filters( 'random_password', $password, $length, $special_chars, $extra_special_chars ); 2733 } 2734 endif; 2735 2736 if ( ! function_exists( 'wp_rand' ) ) : 2737 /** 2738 * Generates a random non-negative number. 2739 * 2740 * @since 2.6.2 2741 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available. 2742 * @since 6.1.0 Returns zero instead of a random number if both `$min` and `$max` are zero. 2743 * 2744 * @global string $rnd_value 2745 * 2746 * @param int $min Optional. Lower limit for the generated number. 2747 * Accepts positive integers or zero. Defaults to 0. 2748 * @param int $max Optional. Upper limit for the generated number. 2749 * Accepts positive integers. Defaults to 4294967295. 2750 * @return int A random non-negative number between min and max. 2751 */ 2752 function wp_rand( $min = null, $max = null ) { 2753 global $rnd_value; 2754 2755 /* 2756 * Some misconfigured 32-bit environments (Entropy PHP, for example) 2757 * truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats. 2758 */ 2759 $max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff 2760 2761 if ( null === $min ) { 2762 $min = 0; 2763 } 2764 2765 if ( null === $max ) { 2766 $max = $max_random_number; 2767 } 2768 2769 // We only handle ints, floats are truncated to their integer value. 2770 $min = (int) $min; 2771 $max = (int) $max; 2772 2773 // Use PHP's CSPRNG, or a compatible method. 2774 static $use_random_int_functionality = true; 2775 if ( $use_random_int_functionality ) { 2776 try { 2777 // wp_rand() can accept arguments in either order, PHP cannot. 2778 $_max = max( $min, $max ); 2779 $_min = min( $min, $max ); 2780 $val = random_int( $_min, $_max ); 2781 if ( false !== $val ) { 2782 return absint( $val ); 2783 } else { 2784 $use_random_int_functionality = false; 2785 } 2786 } catch ( Error $e ) { 2787 $use_random_int_functionality = false; 2788 } catch ( Exception $e ) { 2789 $use_random_int_functionality = false; 2790 } 2791 } 2792 2793 /* 2794 * Reset $rnd_value after 14 uses. 2795 * 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value. 2796 */ 2797 if ( strlen( $rnd_value ) < 8 ) { 2798 if ( defined( 'WP_SETUP_CONFIG' ) ) { 2799 static $seed = ''; 2800 } else { 2801 $seed = get_transient( 'random_seed' ); 2802 } 2803 $rnd_value = md5( uniqid( microtime() . mt_rand(), true ) . $seed ); 2804 $rnd_value .= sha1( $rnd_value ); 2805 $rnd_value .= sha1( $rnd_value . $seed ); 2806 $seed = md5( $seed . $rnd_value ); 2807 if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) { 2808 set_transient( 'random_seed', $seed ); 2809 } 2810 } 2811 2812 // Take the first 8 digits for our value. 2813 $value = substr( $rnd_value, 0, 8 ); 2814 2815 // Strip the first eight, leaving the remainder for the next call to wp_rand(). 2816 $rnd_value = substr( $rnd_value, 8 ); 2817 2818 $value = abs( hexdec( $value ) ); 2819 2820 // Reduce the value to be within the min - max range. 2821 $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 ); 2822 2823 return abs( (int) $value ); 2824 } 2825 endif; 2826 2827 if ( ! function_exists( 'wp_set_password' ) ) : 2828 /** 2829 * Updates the user's password with a new hashed one. 2830 * 2831 * For integration with other applications, this function can be overwritten to 2832 * instead use the other package password checking algorithm. 2833 * 2834 * Please note: This function should be used sparingly and is really only meant for single-time 2835 * application. Leveraging this improperly in a plugin or theme could result in an endless loop 2836 * of password resets if precautions are not taken to ensure it does not execute on every page load. 2837 * 2838 * @since 2.5.0 2839 * 2840 * @global wpdb $wpdb WordPress database abstraction object. 2841 * 2842 * @param string $password The plaintext new user password. 2843 * @param int $user_id User ID. 2844 */ 2845 function wp_set_password( $password, $user_id ) { 2846 global $wpdb; 2847 2848 $old_user_data = get_userdata( $user_id ); 2849 2850 $hash = wp_hash_password( $password ); 2851 $wpdb->update( 2852 $wpdb->users, 2853 array( 2854 'user_pass' => $hash, 2855 'user_activation_key' => '', 2856 ), 2857 array( 'ID' => $user_id ) 2858 ); 2859 2860 clean_user_cache( $user_id ); 2861 2862 /** 2863 * Fires after the user password is set. 2864 * 2865 * @since 6.2.0 2866 * @since 6.7.0 The `$old_user_data` parameter was added. 2867 * 2868 * @param string $password The plaintext password just set. 2869 * @param int $user_id The ID of the user whose password was just set. 2870 * @param WP_User $old_user_data Object containing user's data prior to update. 2871 */ 2872 do_action( 'wp_set_password', $password, $user_id, $old_user_data ); 2873 } 2874 endif; 2875 2876 if ( ! function_exists( 'get_avatar' ) ) : 2877 /** 2878 * Retrieves the avatar `<img>` tag for a user, email address, MD5 hash, comment, or post. 2879 * 2880 * @since 2.5.0 2881 * @since 4.2.0 Added the optional `$args` parameter. 2882 * @since 5.5.0 Added the `loading` argument. 2883 * @since 6.1.0 Added the `decoding` argument. 2884 * @since 6.3.0 Added the `fetchpriority` argument. 2885 * 2886 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, 2887 * user email, WP_User object, WP_Post object, or WP_Comment object. 2888 * @param int $size Optional. Height and width of the avatar in pixels. Default 96. 2889 * @param string $default_value URL for the default image or a default type. Accepts: 2890 * - '404' (return a 404 instead of a default image) 2891 * - 'retro' (a 8-bit arcade-style pixelated face) 2892 * - 'robohash' (a robot) 2893 * - 'monsterid' (a monster) 2894 * - 'wavatar' (a cartoon face) 2895 * - 'identicon' (the "quilt", a geometric pattern) 2896 * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man) 2897 * - 'blank' (transparent GIF) 2898 * - 'gravatar_default' (the Gravatar logo) 2899 * Default is the value of the 'avatar_default' option, 2900 * with a fallback of 'mystery'. 2901 * @param string $alt Optional. Alternative text to use in the avatar image tag. 2902 * Default empty. 2903 * @param array $args { 2904 * Optional. Extra arguments to retrieve the avatar. 2905 * 2906 * @type int $height Display height of the avatar in pixels. Defaults to $size. 2907 * @type int $width Display width of the avatar in pixels. Defaults to $size. 2908 * @type bool $force_default Whether to always show the default image, never the Gravatar. 2909 * Default false. 2910 * @type string $rating What rating to display avatars up to. Accepts: 2911 * - 'G' (suitable for all audiences) 2912 * - 'PG' (possibly offensive, usually for audiences 13 and above) 2913 * - 'R' (intended for adult audiences above 17) 2914 * - 'X' (even more mature than above) 2915 * Default is the value of the 'avatar_rating' option. 2916 * @type string $scheme URL scheme to use. See set_url_scheme() for accepted values. 2917 * Default null. 2918 * @type array|string $class Array or string of additional classes to add to the img element. 2919 * Default null. 2920 * @type bool $force_display Whether to always show the avatar - ignores the show_avatars option. 2921 * Default false. 2922 * @type string $loading Value for the `loading` attribute. 2923 * Default null. 2924 * @type string $fetchpriority Value for the `fetchpriority` attribute. 2925 * Default null. 2926 * @type string $decoding Value for the `decoding` attribute. 2927 * Default null. 2928 * @type string $extra_attr HTML attributes to insert in the IMG element. Is not sanitized. 2929 * Default empty. 2930 * } 2931 * @return string|false `<img>` tag for the user's avatar. False on failure. 2932 */ 2933 function get_avatar( $id_or_email, $size = 96, $default_value = '', $alt = '', $args = null ) { 2934 $defaults = array( 2935 // get_avatar_data() args. 2936 'size' => 96, 2937 'height' => null, 2938 'width' => null, 2939 'default' => get_option( 'avatar_default', 'mystery' ), 2940 'force_default' => false, 2941 'rating' => get_option( 'avatar_rating' ), 2942 'scheme' => null, 2943 'alt' => '', 2944 'class' => null, 2945 'force_display' => false, 2946 'loading' => null, 2947 'fetchpriority' => null, 2948 'decoding' => null, 2949 'extra_attr' => '', 2950 ); 2951 2952 if ( empty( $args ) ) { 2953 $args = array(); 2954 } 2955 2956 $args['size'] = (int) $size; 2957 $args['default'] = $default_value; 2958 $args['alt'] = $alt; 2959 2960 $args = wp_parse_args( $args, $defaults ); 2961 2962 if ( empty( $args['height'] ) ) { 2963 $args['height'] = $args['size']; 2964 } 2965 if ( empty( $args['width'] ) ) { 2966 $args['width'] = $args['size']; 2967 } 2968 2969 // Update args with loading optimized attributes. 2970 $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' ); 2971 2972 $args = array_merge( $args, $loading_optimization_attr ); 2973 2974 if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) { 2975 $id_or_email = get_comment( $id_or_email ); 2976 } 2977 2978 /** 2979 * Allows the HTML for a user's avatar to be returned early. 2980 * 2981 * Returning a non-null value will effectively short-circuit get_avatar(), passing 2982 * the value through the {@see 'get_avatar'} filter and returning early. 2983 * 2984 * @since 4.2.0 2985 * 2986 * @param string|null $avatar HTML for the user's avatar. Default null. 2987 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, 2988 * user email, WP_User object, WP_Post object, or WP_Comment object. 2989 * @param array $args Arguments passed to get_avatar_url(), after processing. 2990 */ 2991 $avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args ); 2992 2993 if ( ! is_null( $avatar ) ) { 2994 /** This filter is documented in wp-includes/pluggable.php */ 2995 return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args ); 2996 } 2997 2998 if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) { 2999 return false; 3000 } 3001 3002 $url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) ); 3003 3004 $args = get_avatar_data( $id_or_email, $args ); 3005 3006 $url = $args['url']; 3007 3008 if ( ! $url || is_wp_error( $url ) ) { 3009 return false; 3010 } 3011 3012 $class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' ); 3013 3014 if ( ! $args['found_avatar'] || $args['force_default'] ) { 3015 $class[] = 'avatar-default'; 3016 } 3017 3018 if ( $args['class'] ) { 3019 if ( is_array( $args['class'] ) ) { 3020 $class = array_merge( $class, $args['class'] ); 3021 } else { 3022 $class[] = $args['class']; 3023 } 3024 } 3025 3026 // Add `loading`, `fetchpriority`, and `decoding` attributes. 3027 $extra_attr = $args['extra_attr']; 3028 3029 if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true ) 3030 && ! preg_match( '/\bloading\s*=/', $extra_attr ) 3031 ) { 3032 if ( ! empty( $extra_attr ) ) { 3033 $extra_attr .= ' '; 3034 } 3035 3036 $extra_attr .= "loading='{$args['loading']}'"; 3037 } 3038 3039 if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true ) 3040 && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr ) 3041 ) { 3042 if ( ! empty( $extra_attr ) ) { 3043 $extra_attr .= ' '; 3044 } 3045 3046 $extra_attr .= "fetchpriority='{$args['fetchpriority']}'"; 3047 } 3048 3049 if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ), true ) 3050 && ! preg_match( '/\bdecoding\s*=/', $extra_attr ) 3051 ) { 3052 if ( ! empty( $extra_attr ) ) { 3053 $extra_attr .= ' '; 3054 } 3055 3056 $extra_attr .= "decoding='{$args['decoding']}'"; 3057 } 3058 3059 $avatar = sprintf( 3060 "<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>", 3061 esc_attr( $args['alt'] ), 3062 esc_url( $url ), 3063 esc_url( $url2x ) . ' 2x', 3064 esc_attr( implode( ' ', $class ) ), 3065 (int) $args['height'], 3066 (int) $args['width'], 3067 $extra_attr 3068 ); 3069 3070 /** 3071 * Filters the HTML for a user's avatar. 3072 * 3073 * @since 2.5.0 3074 * @since 4.2.0 Added the `$args` parameter. 3075 * 3076 * @param string $avatar HTML for the user's avatar. 3077 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, 3078 * user email, WP_User object, WP_Post object, or WP_Comment object. 3079 * @param int $size Height and width of the avatar in pixels. 3080 * @param string $default_value URL for the default image or a default type. Accepts: 3081 * - '404' (return a 404 instead of a default image) 3082 * - 'retro' (a 8-bit arcade-style pixelated face) 3083 * - 'robohash' (a robot) 3084 * - 'monsterid' (a monster) 3085 * - 'wavatar' (a cartoon face) 3086 * - 'identicon' (the "quilt", a geometric pattern) 3087 * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man) 3088 * - 'blank' (transparent GIF) 3089 * - 'gravatar_default' (the Gravatar logo) 3090 * @param string $alt Alternative text to use in the avatar image tag. 3091 * @param array $args Arguments passed to get_avatar_data(), after processing. 3092 */ 3093 return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args ); 3094 } 3095 endif; 3096 3097 if ( ! function_exists( 'wp_text_diff' ) ) : 3098 /** 3099 * Displays a human readable HTML representation of the difference between two strings. 3100 * 3101 * The Diff is available for getting the changes between versions. The output is 3102 * HTML, so the primary use is for displaying the changes. If the two strings 3103 * are equivalent, then an empty string will be returned. 3104 * 3105 * @since 2.6.0 3106 * 3107 * @see wp_parse_args() Used to change defaults to user defined settings. 3108 * @uses Text_Diff 3109 * @uses WP_Text_Diff_Renderer_Table 3110 * 3111 * @param string $left_string "old" (left) version of string. 3112 * @param string $right_string "new" (right) version of string. 3113 * @param string|array $args { 3114 * Associative array of options to pass to WP_Text_Diff_Renderer_Table(). 3115 * 3116 * @type string $title Titles the diff in a manner compatible 3117 * with the output. Default empty. 3118 * @type string $title_left Change the HTML to the left of the title. 3119 * Default empty. 3120 * @type string $title_right Change the HTML to the right of the title. 3121 * Default empty. 3122 * @type bool $show_split_view True for split view (two columns), false for 3123 * un-split view (single column). Default true. 3124 * } 3125 * @return string Empty string if strings are equivalent or HTML with differences. 3126 */ 3127 function wp_text_diff( $left_string, $right_string, $args = null ) { 3128 $defaults = array( 3129 'title' => '', 3130 'title_left' => '', 3131 'title_right' => '', 3132 'show_split_view' => true, 3133 ); 3134 $args = wp_parse_args( $args, $defaults ); 3135 3136 if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) ) { 3137 require ABSPATH . WPINC . '/wp-diff.php'; 3138 } 3139 3140 $left_string = normalize_whitespace( $left_string ); 3141 $right_string = normalize_whitespace( $right_string ); 3142 3143 $left_lines = explode( "\n", $left_string ); 3144 $right_lines = explode( "\n", $right_string ); 3145 $text_diff = new Text_Diff( $left_lines, $right_lines ); 3146 $renderer = new WP_Text_Diff_Renderer_Table( $args ); 3147 $diff = $renderer->render( $text_diff ); 3148 3149 if ( ! $diff ) { 3150 return ''; 3151 } 3152 3153 $is_split_view = ! empty( $args['show_split_view'] ); 3154 $is_split_view_class = $is_split_view ? ' is-split-view' : ''; 3155 3156 $r = "<table class='diff$is_split_view_class'>\n"; 3157 3158 if ( $args['title'] ) { 3159 $r .= "<caption class='diff-title'>$args[title]</caption>\n"; 3160 } 3161 3162 if ( $args['title_left'] || $args['title_right'] ) { 3163 $r .= '<thead>'; 3164 } 3165 3166 if ( $args['title_left'] || $args['title_right'] ) { 3167 $th_or_td_left = empty( $args['title_left'] ) ? 'td' : 'th'; 3168 $th_or_td_right = empty( $args['title_right'] ) ? 'td' : 'th'; 3169 3170 $r .= "<tr class='diff-sub-title'>\n"; 3171 $r .= "\t<$th_or_td_left>$args[title_left]</$th_or_td_left>\n"; 3172 if ( $is_split_view ) { 3173 $r .= "\t<$th_or_td_right>$args[title_right]</$th_or_td_right>\n"; 3174 } 3175 $r .= "</tr>\n"; 3176 } 3177 3178 if ( $args['title_left'] || $args['title_right'] ) { 3179 $r .= "</thead>\n"; 3180 } 3181 3182 $r .= "<tbody>\n$diff\n</tbody>\n"; 3183 $r .= '</table>'; 3184 3185 return $r; 3186 } 3187 endif;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Dec 21 08:20:01 2024 | Cross-referenced by PHPXref |