[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Core User API 4 * 5 * @package WordPress 6 * @subpackage Users 7 */ 8 9 /** 10 * Authenticates and logs a user in with 'remember' capability. 11 * 12 * The credentials is an array that has 'user_login', 'user_password', and 13 * 'remember' indices. If the credentials is not given, then the log in form 14 * will be assumed and used if set. 15 * 16 * The various authentication cookies will be set by this function and will be 17 * set for a longer period depending on if the 'remember' credential is set to 18 * true. 19 * 20 * Note: wp_signon() doesn't handle setting the current user. This means that if the 21 * function is called before the {@see 'init'} hook is fired, is_user_logged_in() will 22 * evaluate as false until that point. If is_user_logged_in() is needed in conjunction 23 * with wp_signon(), wp_set_current_user() should be called explicitly. 24 * 25 * @since 2.5.0 26 * 27 * @global string $auth_secure_cookie 28 * 29 * @param array $credentials Optional. User info in order to sign on. 30 * @param string|bool $secure_cookie Optional. Whether to use secure cookie. 31 * @return WP_User|WP_Error WP_User on success, WP_Error on failure. 32 */ 33 function wp_signon( $credentials = array(), $secure_cookie = '' ) { 34 if ( empty( $credentials ) ) { 35 $credentials = array(); // Back-compat for plugins passing an empty string. 36 37 if ( ! empty( $_POST['log'] ) ) { 38 $credentials['user_login'] = wp_unslash( $_POST['log'] ); 39 } 40 if ( ! empty( $_POST['pwd'] ) ) { 41 $credentials['user_password'] = $_POST['pwd']; 42 } 43 if ( ! empty( $_POST['rememberme'] ) ) { 44 $credentials['remember'] = $_POST['rememberme']; 45 } 46 } 47 48 if ( ! empty( $credentials['remember'] ) ) { 49 $credentials['remember'] = true; 50 } else { 51 $credentials['remember'] = false; 52 } 53 54 /** 55 * Fires before the user is authenticated. 56 * 57 * The variables passed to the callbacks are passed by reference, 58 * and can be modified by callback functions. 59 * 60 * @since 1.5.1 61 * 62 * @todo Decide whether to deprecate the wp_authenticate action. 63 * 64 * @param string $user_login Username (passed by reference). 65 * @param string $user_password User password (passed by reference). 66 */ 67 do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) ); 68 69 if ( '' === $secure_cookie ) { 70 $secure_cookie = is_ssl(); 71 } 72 73 /** 74 * Filters whether to use a secure sign-on cookie. 75 * 76 * @since 3.1.0 77 * 78 * @param bool $secure_cookie Whether to use a secure sign-on cookie. 79 * @param array $credentials { 80 * Array of entered sign-on data. 81 * 82 * @type string $user_login Username. 83 * @type string $user_password Password entered. 84 * @type bool $remember Whether to 'remember' the user. Increases the time 85 * that the cookie will be kept. Default false. 86 * } 87 */ 88 $secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials ); 89 90 global $auth_secure_cookie; // XXX ugly hack to pass this to wp_authenticate_cookie(). 91 $auth_secure_cookie = $secure_cookie; 92 93 add_filter( 'authenticate', 'wp_authenticate_cookie', 30, 3 ); 94 95 $user = wp_authenticate( $credentials['user_login'], $credentials['user_password'] ); 96 97 if ( is_wp_error( $user ) ) { 98 return $user; 99 } 100 101 wp_set_auth_cookie( $user->ID, $credentials['remember'], $secure_cookie ); 102 /** 103 * Fires after the user has successfully logged in. 104 * 105 * @since 1.5.0 106 * 107 * @param string $user_login Username. 108 * @param WP_User $user WP_User object of the logged-in user. 109 */ 110 do_action( 'wp_login', $user->user_login, $user ); 111 return $user; 112 } 113 114 /** 115 * Authenticate a user, confirming the username and password are valid. 116 * 117 * @since 2.8.0 118 * 119 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null. 120 * @param string $username Username for authentication. 121 * @param string $password Password for authentication. 122 * @return WP_User|WP_Error WP_User on success, WP_Error on failure. 123 */ 124 function wp_authenticate_username_password( $user, $username, $password ) { 125 if ( $user instanceof WP_User ) { 126 return $user; 127 } 128 129 if ( empty( $username ) || empty( $password ) ) { 130 if ( is_wp_error( $user ) ) { 131 return $user; 132 } 133 134 $error = new WP_Error(); 135 136 if ( empty( $username ) ) { 137 $error->add( 'empty_username', __( '<strong>Error</strong>: The username field is empty.' ) ); 138 } 139 140 if ( empty( $password ) ) { 141 $error->add( 'empty_password', __( '<strong>Error</strong>: The password field is empty.' ) ); 142 } 143 144 return $error; 145 } 146 147 $user = get_user_by( 'login', $username ); 148 149 if ( ! $user ) { 150 return new WP_Error( 151 'invalid_username', 152 __( 'Unknown username. Check again or try your email address.' ) 153 ); 154 } 155 156 /** 157 * Filters whether the given user can be authenticated with the provided $password. 158 * 159 * @since 2.5.0 160 * 161 * @param WP_User|WP_Error $user WP_User or WP_Error object if a previous 162 * callback failed authentication. 163 * @param string $password Password to check against the user. 164 */ 165 $user = apply_filters( 'wp_authenticate_user', $user, $password ); 166 if ( is_wp_error( $user ) ) { 167 return $user; 168 } 169 170 if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) { 171 return new WP_Error( 172 'incorrect_password', 173 sprintf( 174 /* translators: %s: User name. */ 175 __( '<strong>Error</strong>: The password you entered for the username %s is incorrect.' ), 176 '<strong>' . $username . '</strong>' 177 ) . 178 ' <a href="' . wp_lostpassword_url() . '">' . 179 __( 'Lost your password?' ) . 180 '</a>' 181 ); 182 } 183 184 return $user; 185 } 186 187 /** 188 * Authenticates a user using the email and password. 189 * 190 * @since 4.5.0 191 * 192 * @param WP_User|WP_Error|null $user WP_User or WP_Error object if a previous 193 * callback failed authentication. 194 * @param string $email Email address for authentication. 195 * @param string $password Password for authentication. 196 * @return WP_User|WP_Error WP_User on success, WP_Error on failure. 197 */ 198 function wp_authenticate_email_password( $user, $email, $password ) { 199 if ( $user instanceof WP_User ) { 200 return $user; 201 } 202 203 if ( empty( $email ) || empty( $password ) ) { 204 if ( is_wp_error( $user ) ) { 205 return $user; 206 } 207 208 $error = new WP_Error(); 209 210 if ( empty( $email ) ) { 211 // Uses 'empty_username' for back-compat with wp_signon(). 212 $error->add( 'empty_username', __( '<strong>Error</strong>: The email field is empty.' ) ); 213 } 214 215 if ( empty( $password ) ) { 216 $error->add( 'empty_password', __( '<strong>Error</strong>: The password field is empty.' ) ); 217 } 218 219 return $error; 220 } 221 222 if ( ! is_email( $email ) ) { 223 return $user; 224 } 225 226 $user = get_user_by( 'email', $email ); 227 228 if ( ! $user ) { 229 return new WP_Error( 230 'invalid_email', 231 __( 'Unknown email address. Check again or try your username.' ) 232 ); 233 } 234 235 /** This filter is documented in wp-includes/user.php */ 236 $user = apply_filters( 'wp_authenticate_user', $user, $password ); 237 238 if ( is_wp_error( $user ) ) { 239 return $user; 240 } 241 242 if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) { 243 return new WP_Error( 244 'incorrect_password', 245 sprintf( 246 /* translators: %s: Email address. */ 247 __( '<strong>Error</strong>: The password you entered for the email address %s is incorrect.' ), 248 '<strong>' . $email . '</strong>' 249 ) . 250 ' <a href="' . wp_lostpassword_url() . '">' . 251 __( 'Lost your password?' ) . 252 '</a>' 253 ); 254 } 255 256 return $user; 257 } 258 259 /** 260 * Authenticate the user using the WordPress auth cookie. 261 * 262 * @since 2.8.0 263 * 264 * @global string $auth_secure_cookie 265 * 266 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null. 267 * @param string $username Username. If not empty, cancels the cookie authentication. 268 * @param string $password Password. If not empty, cancels the cookie authentication. 269 * @return WP_User|WP_Error WP_User on success, WP_Error on failure. 270 */ 271 function wp_authenticate_cookie( $user, $username, $password ) { 272 if ( $user instanceof WP_User ) { 273 return $user; 274 } 275 276 if ( empty( $username ) && empty( $password ) ) { 277 $user_id = wp_validate_auth_cookie(); 278 if ( $user_id ) { 279 return new WP_User( $user_id ); 280 } 281 282 global $auth_secure_cookie; 283 284 if ( $auth_secure_cookie ) { 285 $auth_cookie = SECURE_AUTH_COOKIE; 286 } else { 287 $auth_cookie = AUTH_COOKIE; 288 } 289 290 if ( ! empty( $_COOKIE[ $auth_cookie ] ) ) { 291 return new WP_Error( 'expired_session', __( 'Please log in again.' ) ); 292 } 293 294 // If the cookie is not set, be silent. 295 } 296 297 return $user; 298 } 299 300 /** 301 * Authenticates the user using an application password. 302 * 303 * @since 5.6.0 304 * 305 * @param WP_User|WP_Error|null $input_user WP_User or WP_Error object if a previous 306 * callback failed authentication. 307 * @param string $username Username for authentication. 308 * @param string $password Password for authentication. 309 * @return WP_User|WP_Error|null WP_User on success, WP_Error on failure, null if 310 * null is passed in and this isn't an API request. 311 */ 312 function wp_authenticate_application_password( $input_user, $username, $password ) { 313 if ( $input_user instanceof WP_User ) { 314 return $input_user; 315 } 316 317 if ( ! WP_Application_Passwords::is_in_use() ) { 318 return $input_user; 319 } 320 321 $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ); 322 323 /** 324 * Filters whether this is an API request that Application Passwords can be used on. 325 * 326 * By default, Application Passwords is available for the REST API and XML-RPC. 327 * 328 * @since 5.6.0 329 * 330 * @param bool $is_api_request If this is an acceptable API request. 331 */ 332 $is_api_request = apply_filters( 'application_password_is_api_request', $is_api_request ); 333 334 if ( ! $is_api_request ) { 335 return $input_user; 336 } 337 338 $error = null; 339 $user = get_user_by( 'login', $username ); 340 341 if ( ! $user && is_email( $username ) ) { 342 $user = get_user_by( 'email', $username ); 343 } 344 345 // If the login name is invalid, short circuit. 346 if ( ! $user ) { 347 if ( is_email( $username ) ) { 348 $error = new WP_Error( 349 'invalid_email', 350 __( 'Unknown email address. Check again or try your username.' ) 351 ); 352 } else { 353 $error = new WP_Error( 354 'invalid_username', 355 __( 'Unknown username. Check again or try your email address.' ) 356 ); 357 } 358 } elseif ( ! wp_is_application_passwords_available() ) { 359 $error = new WP_Error( 360 'application_passwords_disabled', 361 __( 'Application passwords are not available.' ) 362 ); 363 } elseif ( ! wp_is_application_passwords_available_for_user( $user ) ) { 364 $error = new WP_Error( 365 'application_passwords_disabled_for_user', 366 __( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' ) 367 ); 368 } 369 370 if ( $error ) { 371 /** 372 * Fires when an application password failed to authenticate the user. 373 * 374 * @since 5.6.0 375 * 376 * @param WP_Error $error The authentication error. 377 */ 378 do_action( 'application_password_failed_authentication', $error ); 379 380 return $error; 381 } 382 383 /* 384 * Strip out anything non-alphanumeric. This is so passwords can be used with 385 * or without spaces to indicate the groupings for readability. 386 * 387 * Generated application passwords are exclusively alphanumeric. 388 */ 389 $password = preg_replace( '/[^a-z\d]/i', '', $password ); 390 391 $hashed_passwords = WP_Application_Passwords::get_user_application_passwords( $user->ID ); 392 393 foreach ( $hashed_passwords as $key => $item ) { 394 if ( ! wp_check_password( $password, $item['password'], $user->ID ) ) { 395 continue; 396 } 397 398 $error = new WP_Error(); 399 400 /** 401 * Fires when an application password has been successfully checked as valid. 402 * 403 * This allows for plugins to add additional constraints to prevent an application password from being used. 404 * 405 * @since 5.6.0 406 * 407 * @param WP_Error $error The error object. 408 * @param WP_User $user The user authenticating. 409 * @param array $item The details about the application password. 410 * @param string $password The raw supplied password. 411 */ 412 do_action( 'wp_authenticate_application_password_errors', $error, $user, $item, $password ); 413 414 if ( is_wp_error( $error ) && $error->has_errors() ) { 415 /** This action is documented in wp-includes/user.php */ 416 do_action( 'application_password_failed_authentication', $error ); 417 418 return $error; 419 } 420 421 WP_Application_Passwords::record_application_password_usage( $user->ID, $item['uuid'] ); 422 423 /** 424 * Fires after an application password was used for authentication. 425 * 426 * @since 5.6.0 427 * 428 * @param WP_User $user The user who was authenticated. 429 * @param array $item The application password used. 430 */ 431 do_action( 'application_password_did_authenticate', $user, $item ); 432 433 return $user; 434 } 435 436 $error = new WP_Error( 437 'incorrect_password', 438 __( 'The provided password is an invalid application password.' ) 439 ); 440 441 /** This action is documented in wp-includes/user.php */ 442 do_action( 'application_password_failed_authentication', $error ); 443 444 return $error; 445 } 446 447 /** 448 * Validates the application password credentials passed via Basic Authentication. 449 * 450 * @since 5.6.0 451 * 452 * @param int|false $input_user User ID if one has been determined, false otherwise. 453 * @return int|false The authenticated user ID if successful, false otherwise. 454 */ 455 function wp_validate_application_password( $input_user ) { 456 // Don't authenticate twice. 457 if ( ! empty( $input_user ) ) { 458 return $input_user; 459 } 460 461 if ( ! wp_is_application_passwords_available() ) { 462 return $input_user; 463 } 464 465 // Both $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] must be set in order to attempt authentication. 466 if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) { 467 return $input_user; 468 } 469 470 $authenticated = wp_authenticate_application_password( null, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ); 471 472 if ( $authenticated instanceof WP_User ) { 473 return $authenticated->ID; 474 } 475 476 // If it wasn't a user what got returned, just pass on what we had received originally. 477 return $input_user; 478 } 479 480 /** 481 * For Multisite blogs, check if the authenticated user has been marked as a 482 * spammer, or if the user's primary blog has been marked as spam. 483 * 484 * @since 3.7.0 485 * 486 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null. 487 * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer. 488 */ 489 function wp_authenticate_spam_check( $user ) { 490 if ( $user instanceof WP_User && is_multisite() ) { 491 /** 492 * Filters whether the user has been marked as a spammer. 493 * 494 * @since 3.7.0 495 * 496 * @param bool $spammed Whether the user is considered a spammer. 497 * @param WP_User $user User to check against. 498 */ 499 $spammed = apply_filters( 'check_is_user_spammed', is_user_spammy( $user ), $user ); 500 501 if ( $spammed ) { 502 return new WP_Error( 'spammer_account', __( '<strong>Error</strong>: Your account has been marked as a spammer.' ) ); 503 } 504 } 505 return $user; 506 } 507 508 /** 509 * Validates the logged-in cookie. 510 * 511 * Checks the logged-in cookie if the previous auth cookie could not be 512 * validated and parsed. 513 * 514 * This is a callback for the {@see 'determine_current_user'} filter, rather than API. 515 * 516 * @since 3.9.0 517 * 518 * @param int|false $user_id The user ID (or false) as received from 519 * the `determine_current_user` filter. 520 * @return int|false User ID if validated, false otherwise. If a user ID from 521 * an earlier filter callback is received, that value is returned. 522 */ 523 function wp_validate_logged_in_cookie( $user_id ) { 524 if ( $user_id ) { 525 return $user_id; 526 } 527 528 if ( is_blog_admin() || is_network_admin() || empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) { 529 return false; 530 } 531 532 return wp_validate_auth_cookie( $_COOKIE[ LOGGED_IN_COOKIE ], 'logged_in' ); 533 } 534 535 /** 536 * Number of posts user has written. 537 * 538 * @since 3.0.0 539 * @since 4.1.0 Added `$post_type` argument. 540 * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array 541 * of post types to `$post_type`. 542 * 543 * @global wpdb $wpdb WordPress database abstraction object. 544 * 545 * @param int $userid User ID. 546 * @param array|string $post_type Optional. Single post type or array of post types to count the number of posts for. Default 'post'. 547 * @param bool $public_only Optional. Whether to only return counts for public posts. Default false. 548 * @return string Number of posts the user has written in this post type. 549 */ 550 function count_user_posts( $userid, $post_type = 'post', $public_only = false ) { 551 global $wpdb; 552 553 $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only ); 554 555 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" ); 556 557 /** 558 * Filters the number of posts a user has written. 559 * 560 * @since 2.7.0 561 * @since 4.1.0 Added `$post_type` argument. 562 * @since 4.3.1 Added `$public_only` argument. 563 * 564 * @param int $count The user's post count. 565 * @param int $userid User ID. 566 * @param string|array $post_type Single post type or array of post types to count the number of posts for. 567 * @param bool $public_only Whether to limit counted posts to public posts. 568 */ 569 return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only ); 570 } 571 572 /** 573 * Number of posts written by a list of users. 574 * 575 * @since 3.0.0 576 * 577 * @global wpdb $wpdb WordPress database abstraction object. 578 * 579 * @param int[] $users Array of user IDs. 580 * @param string|string[] $post_type Optional. Single post type or array of post types to check. Defaults to 'post'. 581 * @param bool $public_only Optional. Only return counts for public posts. Defaults to false. 582 * @return string[] Amount of posts each user has written, as strings, keyed by user ID. 583 */ 584 function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) { 585 global $wpdb; 586 587 $count = array(); 588 if ( empty( $users ) || ! is_array( $users ) ) { 589 return $count; 590 } 591 592 $userlist = implode( ',', array_map( 'absint', $users ) ); 593 $where = get_posts_by_author_sql( $post_type, true, null, $public_only ); 594 595 $result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N ); 596 foreach ( $result as $row ) { 597 $count[ $row[0] ] = $row[1]; 598 } 599 600 foreach ( $users as $id ) { 601 if ( ! isset( $count[ $id ] ) ) { 602 $count[ $id ] = 0; 603 } 604 } 605 606 return $count; 607 } 608 609 // 610 // User option functions. 611 // 612 613 /** 614 * Get the current user's ID 615 * 616 * @since MU (3.0.0) 617 * 618 * @return int The current user's ID, or 0 if no user is logged in. 619 */ 620 function get_current_user_id() { 621 if ( ! function_exists( 'wp_get_current_user' ) ) { 622 return 0; 623 } 624 $user = wp_get_current_user(); 625 return ( isset( $user->ID ) ? (int) $user->ID : 0 ); 626 } 627 628 /** 629 * Retrieve user option that can be either per Site or per Network. 630 * 631 * If the user ID is not given, then the current user will be used instead. If 632 * the user ID is given, then the user data will be retrieved. The filter for 633 * the result, will also pass the original option name and finally the user data 634 * object as the third parameter. 635 * 636 * The option will first check for the per site name and then the per Network name. 637 * 638 * @since 2.0.0 639 * 640 * @global wpdb $wpdb WordPress database abstraction object. 641 * 642 * @param string $option User option name. 643 * @param int $user Optional. User ID. 644 * @param string $deprecated Use get_option() to check for an option in the options table. 645 * @return mixed User option value on success, false on failure. 646 */ 647 function get_user_option( $option, $user = 0, $deprecated = '' ) { 648 global $wpdb; 649 650 if ( ! empty( $deprecated ) ) { 651 _deprecated_argument( __FUNCTION__, '3.0.0' ); 652 } 653 654 if ( empty( $user ) ) { 655 $user = get_current_user_id(); 656 } 657 658 $user = get_userdata( $user ); 659 if ( ! $user ) { 660 return false; 661 } 662 663 $prefix = $wpdb->get_blog_prefix(); 664 if ( $user->has_prop( $prefix . $option ) ) { // Blog-specific. 665 $result = $user->get( $prefix . $option ); 666 } elseif ( $user->has_prop( $option ) ) { // User-specific and cross-blog. 667 $result = $user->get( $option ); 668 } else { 669 $result = false; 670 } 671 672 /** 673 * Filters a specific user option value. 674 * 675 * The dynamic portion of the hook name, `$option`, refers to the user option name. 676 * 677 * @since 2.5.0 678 * 679 * @param mixed $result Value for the user's option. 680 * @param string $option Name of the option being retrieved. 681 * @param WP_User $user WP_User object of the user whose option is being retrieved. 682 */ 683 return apply_filters( "get_user_option_{$option}", $result, $option, $user ); 684 } 685 686 /** 687 * Update user option with global blog capability. 688 * 689 * User options are just like user metadata except that they have support for 690 * global blog options. If the 'global' parameter is false, which it is by default 691 * it will prepend the WordPress table prefix to the option name. 692 * 693 * Deletes the user option if $newvalue is empty. 694 * 695 * @since 2.0.0 696 * 697 * @global wpdb $wpdb WordPress database abstraction object. 698 * 699 * @param int $user_id User ID. 700 * @param string $option_name User option name. 701 * @param mixed $newvalue User option value. 702 * @param bool $global Optional. Whether option name is global or blog specific. 703 * Default false (blog specific). 704 * @return int|bool User meta ID if the option didn't exist, true on successful update, 705 * false on failure. 706 */ 707 function update_user_option( $user_id, $option_name, $newvalue, $global = false ) { 708 global $wpdb; 709 710 if ( ! $global ) { 711 $option_name = $wpdb->get_blog_prefix() . $option_name; 712 } 713 714 return update_user_meta( $user_id, $option_name, $newvalue ); 715 } 716 717 /** 718 * Delete user option with global blog capability. 719 * 720 * User options are just like user metadata except that they have support for 721 * global blog options. If the 'global' parameter is false, which it is by default 722 * it will prepend the WordPress table prefix to the option name. 723 * 724 * @since 3.0.0 725 * 726 * @global wpdb $wpdb WordPress database abstraction object. 727 * 728 * @param int $user_id User ID 729 * @param string $option_name User option name. 730 * @param bool $global Optional. Whether option name is global or blog specific. 731 * Default false (blog specific). 732 * @return bool True on success, false on failure. 733 */ 734 function delete_user_option( $user_id, $option_name, $global = false ) { 735 global $wpdb; 736 737 if ( ! $global ) { 738 $option_name = $wpdb->get_blog_prefix() . $option_name; 739 } 740 return delete_user_meta( $user_id, $option_name ); 741 } 742 743 /** 744 * Retrieve list of users matching criteria. 745 * 746 * @since 3.1.0 747 * 748 * @see WP_User_Query 749 * 750 * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query(). 751 * for more information on accepted arguments. 752 * @return array List of users. 753 */ 754 function get_users( $args = array() ) { 755 756 $args = wp_parse_args( $args ); 757 $args['count_total'] = false; 758 759 $user_search = new WP_User_Query( $args ); 760 761 return (array) $user_search->get_results(); 762 } 763 764 /** 765 * Get the sites a user belongs to. 766 * 767 * @since 3.0.0 768 * @since 4.7.0 Converted to use `get_sites()`. 769 * 770 * @global wpdb $wpdb WordPress database abstraction object. 771 * 772 * @param int $user_id User ID 773 * @param bool $all Whether to retrieve all sites, or only sites that are not 774 * marked as deleted, archived, or spam. 775 * @return object[] A list of the user's sites. An empty array if the user doesn't exist 776 * or belongs to no sites. 777 */ 778 function get_blogs_of_user( $user_id, $all = false ) { 779 global $wpdb; 780 781 $user_id = (int) $user_id; 782 783 // Logged out users can't have sites. 784 if ( empty( $user_id ) ) { 785 return array(); 786 } 787 788 /** 789 * Filters the list of a user's sites before it is populated. 790 * 791 * Returning a non-null value from the filter will effectively short circuit 792 * get_blogs_of_user(), returning that value instead. 793 * 794 * @since 4.6.0 795 * 796 * @param null|object[] $sites An array of site objects of which the user is a member. 797 * @param int $user_id User ID. 798 * @param bool $all Whether the returned array should contain all sites, including 799 * those marked 'deleted', 'archived', or 'spam'. Default false. 800 */ 801 $sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all ); 802 803 if ( null !== $sites ) { 804 return $sites; 805 } 806 807 $keys = get_user_meta( $user_id ); 808 if ( empty( $keys ) ) { 809 return array(); 810 } 811 812 if ( ! is_multisite() ) { 813 $site_id = get_current_blog_id(); 814 $sites = array( $site_id => new stdClass ); 815 $sites[ $site_id ]->userblog_id = $site_id; 816 $sites[ $site_id ]->blogname = get_option( 'blogname' ); 817 $sites[ $site_id ]->domain = ''; 818 $sites[ $site_id ]->path = ''; 819 $sites[ $site_id ]->site_id = 1; 820 $sites[ $site_id ]->siteurl = get_option( 'siteurl' ); 821 $sites[ $site_id ]->archived = 0; 822 $sites[ $site_id ]->spam = 0; 823 $sites[ $site_id ]->deleted = 0; 824 return $sites; 825 } 826 827 $site_ids = array(); 828 829 if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) { 830 $site_ids[] = 1; 831 unset( $keys[ $wpdb->base_prefix . 'capabilities' ] ); 832 } 833 834 $keys = array_keys( $keys ); 835 836 foreach ( $keys as $key ) { 837 if ( 'capabilities' !== substr( $key, -12 ) ) { 838 continue; 839 } 840 if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) ) { 841 continue; 842 } 843 $site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key ); 844 if ( ! is_numeric( $site_id ) ) { 845 continue; 846 } 847 848 $site_ids[] = (int) $site_id; 849 } 850 851 $sites = array(); 852 853 if ( ! empty( $site_ids ) ) { 854 $args = array( 855 'number' => '', 856 'site__in' => $site_ids, 857 'update_site_meta_cache' => false, 858 ); 859 if ( ! $all ) { 860 $args['archived'] = 0; 861 $args['spam'] = 0; 862 $args['deleted'] = 0; 863 } 864 865 $_sites = get_sites( $args ); 866 867 foreach ( $_sites as $site ) { 868 $sites[ $site->id ] = (object) array( 869 'userblog_id' => $site->id, 870 'blogname' => $site->blogname, 871 'domain' => $site->domain, 872 'path' => $site->path, 873 'site_id' => $site->network_id, 874 'siteurl' => $site->siteurl, 875 'archived' => $site->archived, 876 'mature' => $site->mature, 877 'spam' => $site->spam, 878 'deleted' => $site->deleted, 879 ); 880 } 881 } 882 883 /** 884 * Filters the list of sites a user belongs to. 885 * 886 * @since MU (3.0.0) 887 * 888 * @param object[] $sites An array of site objects belonging to the user. 889 * @param int $user_id User ID. 890 * @param bool $all Whether the returned sites array should contain all sites, including 891 * those marked 'deleted', 'archived', or 'spam'. Default false. 892 */ 893 return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all ); 894 } 895 896 /** 897 * Find out whether a user is a member of a given blog. 898 * 899 * @since MU (3.0.0) 900 * 901 * @global wpdb $wpdb WordPress database abstraction object. 902 * 903 * @param int $user_id Optional. The unique ID of the user. Defaults to the current user. 904 * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site. 905 * @return bool 906 */ 907 function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) { 908 global $wpdb; 909 910 $user_id = (int) $user_id; 911 $blog_id = (int) $blog_id; 912 913 if ( empty( $user_id ) ) { 914 $user_id = get_current_user_id(); 915 } 916 917 // Technically not needed, but does save calls to get_site() and get_user_meta() 918 // in the event that the function is called when a user isn't logged in. 919 if ( empty( $user_id ) ) { 920 return false; 921 } else { 922 $user = get_userdata( $user_id ); 923 if ( ! $user instanceof WP_User ) { 924 return false; 925 } 926 } 927 928 if ( ! is_multisite() ) { 929 return true; 930 } 931 932 if ( empty( $blog_id ) ) { 933 $blog_id = get_current_blog_id(); 934 } 935 936 $blog = get_site( $blog_id ); 937 938 if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) { 939 return false; 940 } 941 942 $keys = get_user_meta( $user_id ); 943 if ( empty( $keys ) ) { 944 return false; 945 } 946 947 // No underscore before capabilities in $base_capabilities_key. 948 $base_capabilities_key = $wpdb->base_prefix . 'capabilities'; 949 $site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities'; 950 951 if ( isset( $keys[ $base_capabilities_key ] ) && 1 == $blog_id ) { 952 return true; 953 } 954 955 if ( isset( $keys[ $site_capabilities_key ] ) ) { 956 return true; 957 } 958 959 return false; 960 } 961 962 /** 963 * Adds meta data to a user. 964 * 965 * @since 3.0.0 966 * 967 * @param int $user_id User ID. 968 * @param string $meta_key Metadata name. 969 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. 970 * @param bool $unique Optional. Whether the same key should not be added. 971 * Default false. 972 * @return int|false Meta ID on success, false on failure. 973 */ 974 function add_user_meta( $user_id, $meta_key, $meta_value, $unique = false ) { 975 return add_metadata( 'user', $user_id, $meta_key, $meta_value, $unique ); 976 } 977 978 /** 979 * Remove metadata matching criteria from a user. 980 * 981 * You can match based on the key, or key and value. Removing based on key and 982 * value, will keep from removing duplicate metadata with the same key. It also 983 * allows removing all metadata matching key, if needed. 984 * 985 * @since 3.0.0 986 * 987 * @link https://developer.wordpress.org/reference/functions/delete_user_meta/ 988 * 989 * @param int $user_id User ID 990 * @param string $meta_key Metadata name. 991 * @param mixed $meta_value Optional. Metadata value. If provided, 992 * rows will only be removed that match the value. 993 * Must be serializable if non-scalar. Default empty. 994 * @return bool True on success, false on failure. 995 */ 996 function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) { 997 return delete_metadata( 'user', $user_id, $meta_key, $meta_value ); 998 } 999 1000 /** 1001 * Retrieve user meta field for a user. 1002 * 1003 * @since 3.0.0 1004 * 1005 * @link https://developer.wordpress.org/reference/functions/get_user_meta/ 1006 * 1007 * @param int $user_id User ID. 1008 * @param string $key Optional. The meta key to retrieve. By default, 1009 * returns data for all keys. 1010 * @param bool $single Optional. Whether to return a single value. 1011 * This parameter has no effect if $key is not specified. 1012 * Default false. 1013 * @return mixed An array if $single is false. The value of meta data field 1014 * if $single is true. False for an invalid $user_id. 1015 */ 1016 function get_user_meta( $user_id, $key = '', $single = false ) { 1017 return get_metadata( 'user', $user_id, $key, $single ); 1018 } 1019 1020 /** 1021 * Update user meta field based on user ID. 1022 * 1023 * Use the $prev_value parameter to differentiate between meta fields with the 1024 * same key and user ID. 1025 * 1026 * If the meta field for the user does not exist, it will be added. 1027 * 1028 * @since 3.0.0 1029 * 1030 * @link https://developer.wordpress.org/reference/functions/update_user_meta/ 1031 * 1032 * @param int $user_id User ID. 1033 * @param string $meta_key Metadata key. 1034 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. 1035 * @param mixed $prev_value Optional. Previous value to check before updating. 1036 * If specified, only update existing metadata entries with 1037 * this value. Otherwise, update all entries. Default empty. 1038 * @return int|bool Meta ID if the key didn't exist, true on successful update, 1039 * false on failure or if the value passed to the function 1040 * is the same as the one that is already in the database. 1041 */ 1042 function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) { 1043 return update_metadata( 'user', $user_id, $meta_key, $meta_value, $prev_value ); 1044 } 1045 1046 /** 1047 * Count number of users who have each of the user roles. 1048 * 1049 * Assumes there are neither duplicated nor orphaned capabilities meta_values. 1050 * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query() 1051 * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users. 1052 * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257. 1053 * 1054 * @since 3.0.0 1055 * @since 4.4.0 The number of users with no role is now included in the `none` element. 1056 * @since 4.9.0 The `$site_id` parameter was added to support multisite. 1057 * 1058 * @global wpdb $wpdb WordPress database abstraction object. 1059 * 1060 * @param string $strategy Optional. The computational strategy to use when counting the users. 1061 * Accepts either 'time' or 'memory'. Default 'time'. 1062 * @param int|null $site_id Optional. The site ID to count users for. Defaults to the current site. 1063 * @return array { 1064 * User counts. 1065 * 1066 * @type int $total_users Total number of users on the site. 1067 * @type int[] $avail_roles Array of user counts keyed by user role. 1068 * } 1069 */ 1070 function count_users( $strategy = 'time', $site_id = null ) { 1071 global $wpdb; 1072 1073 // Initialize. 1074 if ( ! $site_id ) { 1075 $site_id = get_current_blog_id(); 1076 } 1077 1078 /** 1079 * Filters the user count before queries are run. 1080 * 1081 * Return a non-null value to cause count_users() to return early. 1082 * 1083 * @since 5.1.0 1084 * 1085 * @param null|string $result The value to return instead. Default null to continue with the query. 1086 * @param string $strategy Optional. The computational strategy to use when counting the users. 1087 * Accepts either 'time' or 'memory'. Default 'time'. 1088 * @param int|null $site_id Optional. The site ID to count users for. Defaults to the current site. 1089 */ 1090 $pre = apply_filters( 'pre_count_users', null, $strategy, $site_id ); 1091 1092 if ( null !== $pre ) { 1093 return $pre; 1094 } 1095 1096 $blog_prefix = $wpdb->get_blog_prefix( $site_id ); 1097 $result = array(); 1098 1099 if ( 'time' === $strategy ) { 1100 if ( is_multisite() && get_current_blog_id() != $site_id ) { 1101 switch_to_blog( $site_id ); 1102 $avail_roles = wp_roles()->get_names(); 1103 restore_current_blog(); 1104 } else { 1105 $avail_roles = wp_roles()->get_names(); 1106 } 1107 1108 // Build a CPU-intensive query that will return concise information. 1109 $select_count = array(); 1110 foreach ( $avail_roles as $this_role => $name ) { 1111 $select_count[] = $wpdb->prepare( 'COUNT(NULLIF(`meta_value` LIKE %s, false))', '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%' ); 1112 } 1113 $select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))"; 1114 $select_count = implode( ', ', $select_count ); 1115 1116 // Add the meta_value index to the selection list, then run the query. 1117 $row = $wpdb->get_row( 1118 " 1119 SELECT {$select_count}, COUNT(*) 1120 FROM {$wpdb->usermeta} 1121 INNER JOIN {$wpdb->users} ON user_id = ID 1122 WHERE meta_key = '{$blog_prefix}capabilities' 1123 ", 1124 ARRAY_N 1125 ); 1126 1127 // Run the previous loop again to associate results with role names. 1128 $col = 0; 1129 $role_counts = array(); 1130 foreach ( $avail_roles as $this_role => $name ) { 1131 $count = (int) $row[ $col++ ]; 1132 if ( $count > 0 ) { 1133 $role_counts[ $this_role ] = $count; 1134 } 1135 } 1136 1137 $role_counts['none'] = (int) $row[ $col++ ]; 1138 1139 // Get the meta_value index from the end of the result set. 1140 $total_users = (int) $row[ $col ]; 1141 1142 $result['total_users'] = $total_users; 1143 $result['avail_roles'] =& $role_counts; 1144 } else { 1145 $avail_roles = array( 1146 'none' => 0, 1147 ); 1148 1149 $users_of_blog = $wpdb->get_col( 1150 " 1151 SELECT meta_value 1152 FROM {$wpdb->usermeta} 1153 INNER JOIN {$wpdb->users} ON user_id = ID 1154 WHERE meta_key = '{$blog_prefix}capabilities' 1155 " 1156 ); 1157 1158 foreach ( $users_of_blog as $caps_meta ) { 1159 $b_roles = maybe_unserialize( $caps_meta ); 1160 if ( ! is_array( $b_roles ) ) { 1161 continue; 1162 } 1163 if ( empty( $b_roles ) ) { 1164 $avail_roles['none']++; 1165 } 1166 foreach ( $b_roles as $b_role => $val ) { 1167 if ( isset( $avail_roles[ $b_role ] ) ) { 1168 $avail_roles[ $b_role ]++; 1169 } else { 1170 $avail_roles[ $b_role ] = 1; 1171 } 1172 } 1173 } 1174 1175 $result['total_users'] = count( $users_of_blog ); 1176 $result['avail_roles'] =& $avail_roles; 1177 } 1178 1179 return $result; 1180 } 1181 1182 // 1183 // Private helper functions. 1184 // 1185 1186 /** 1187 * Set up global user vars. 1188 * 1189 * Used by wp_set_current_user() for back compat. Might be deprecated in the future. 1190 * 1191 * @since 2.0.4 1192 * 1193 * @global string $user_login The user username for logging in 1194 * @global WP_User $userdata User data. 1195 * @global int $user_level The level of the user 1196 * @global int $user_ID The ID of the user 1197 * @global string $user_email The email address of the user 1198 * @global string $user_url The url in the user's profile 1199 * @global string $user_identity The display name of the user 1200 * 1201 * @param int $for_user_id Optional. User ID to set up global data. Default 0. 1202 */ 1203 function setup_userdata( $for_user_id = 0 ) { 1204 global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity; 1205 1206 if ( ! $for_user_id ) { 1207 $for_user_id = get_current_user_id(); 1208 } 1209 $user = get_userdata( $for_user_id ); 1210 1211 if ( ! $user ) { 1212 $user_ID = 0; 1213 $user_level = 0; 1214 $userdata = null; 1215 $user_login = ''; 1216 $user_email = ''; 1217 $user_url = ''; 1218 $user_identity = ''; 1219 return; 1220 } 1221 1222 $user_ID = (int) $user->ID; 1223 $user_level = (int) $user->user_level; 1224 $userdata = $user; 1225 $user_login = $user->user_login; 1226 $user_email = $user->user_email; 1227 $user_url = $user->user_url; 1228 $user_identity = $user->display_name; 1229 } 1230 1231 /** 1232 * Create dropdown HTML content of users. 1233 * 1234 * The content can either be displayed, which it is by default or retrieved by 1235 * setting the 'echo' argument. The 'include' and 'exclude' arguments do not 1236 * need to be used; all users will be displayed in that case. Only one can be 1237 * used, either 'include' or 'exclude', but not both. 1238 * 1239 * The available arguments are as follows: 1240 * 1241 * @since 2.3.0 1242 * @since 4.5.0 Added the 'display_name_with_login' value for 'show'. 1243 * @since 4.7.0 Added the `$role`, `$role__in`, and `$role__not_in` parameters. 1244 * 1245 * @param array|string $args { 1246 * Optional. Array or string of arguments to generate a drop-down of users. 1247 * See WP_User_Query::prepare_query() for additional available arguments. 1248 * 1249 * @type string $show_option_all Text to show as the drop-down default (all). 1250 * Default empty. 1251 * @type string $show_option_none Text to show as the drop-down default when no 1252 * users were found. Default empty. 1253 * @type int|string $option_none_value Value to use for $show_option_non when no users 1254 * were found. Default -1. 1255 * @type string $hide_if_only_one_author Whether to skip generating the drop-down 1256 * if only one user was found. Default empty. 1257 * @type string $orderby Field to order found users by. Accepts user fields. 1258 * Default 'display_name'. 1259 * @type string $order Whether to order users in ascending or descending 1260 * order. Accepts 'ASC' (ascending) or 'DESC' (descending). 1261 * Default 'ASC'. 1262 * @type int[]|string $include Array or comma-separated list of user IDs to include. 1263 * Default empty. 1264 * @type int[]|string $exclude Array or comma-separated list of user IDs to exclude. 1265 * Default empty. 1266 * @type bool|int $multi Whether to skip the ID attribute on the 'select' element. 1267 * Accepts 1|true or 0|false. Default 0|false. 1268 * @type string $show User data to display. If the selected item is empty 1269 * then the 'user_login' will be displayed in parentheses. 1270 * Accepts any user field, or 'display_name_with_login' to show 1271 * the display name with user_login in parentheses. 1272 * Default 'display_name'. 1273 * @type int|bool $echo Whether to echo or return the drop-down. Accepts 1|true (echo) 1274 * or 0|false (return). Default 1|true. 1275 * @type int $selected Which user ID should be selected. Default 0. 1276 * @type bool $include_selected Whether to always include the selected user ID in the drop- 1277 * down. Default false. 1278 * @type string $name Name attribute of select element. Default 'user'. 1279 * @type string $id ID attribute of the select element. Default is the value of $name. 1280 * @type string $class Class attribute of the select element. Default empty. 1281 * @type int $blog_id ID of blog (Multisite only). Default is ID of the current blog. 1282 * @type string $who Which type of users to query. Accepts only an empty string or 1283 * 'authors'. Default empty. 1284 * @type string|array $role An array or a comma-separated list of role names that users must 1285 * match to be included in results. Note that this is an inclusive 1286 * list: users must match *each* role. Default empty. 1287 * @type string[] $role__in An array of role names. Matched users must have at least one of 1288 * these roles. Default empty array. 1289 * @type string[] $role__not_in An array of role names to exclude. Users matching one or more of 1290 * these roles will not be included in results. Default empty array. 1291 * } 1292 * @return string HTML dropdown list of users. 1293 */ 1294 function wp_dropdown_users( $args = '' ) { 1295 $defaults = array( 1296 'show_option_all' => '', 1297 'show_option_none' => '', 1298 'hide_if_only_one_author' => '', 1299 'orderby' => 'display_name', 1300 'order' => 'ASC', 1301 'include' => '', 1302 'exclude' => '', 1303 'multi' => 0, 1304 'show' => 'display_name', 1305 'echo' => 1, 1306 'selected' => 0, 1307 'name' => 'user', 1308 'class' => '', 1309 'id' => '', 1310 'blog_id' => get_current_blog_id(), 1311 'who' => '', 1312 'include_selected' => false, 1313 'option_none_value' => -1, 1314 'role' => '', 1315 'role__in' => array(), 1316 'role__not_in' => array(), 1317 ); 1318 1319 $defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0; 1320 1321 $parsed_args = wp_parse_args( $args, $defaults ); 1322 1323 $query_args = wp_array_slice_assoc( $parsed_args, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who', 'role', 'role__in', 'role__not_in' ) ); 1324 1325 $fields = array( 'ID', 'user_login' ); 1326 1327 $show = ! empty( $parsed_args['show'] ) ? $parsed_args['show'] : 'display_name'; 1328 if ( 'display_name_with_login' === $show ) { 1329 $fields[] = 'display_name'; 1330 } else { 1331 $fields[] = $show; 1332 } 1333 1334 $query_args['fields'] = $fields; 1335 1336 $show_option_all = $parsed_args['show_option_all']; 1337 $show_option_none = $parsed_args['show_option_none']; 1338 $option_none_value = $parsed_args['option_none_value']; 1339 1340 /** 1341 * Filters the query arguments for the list of users in the dropdown. 1342 * 1343 * @since 4.4.0 1344 * 1345 * @param array $query_args The query arguments for get_users(). 1346 * @param array $parsed_args The arguments passed to wp_dropdown_users() combined with the defaults. 1347 */ 1348 $query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $parsed_args ); 1349 1350 $users = get_users( $query_args ); 1351 1352 $output = ''; 1353 if ( ! empty( $users ) && ( empty( $parsed_args['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) { 1354 $name = esc_attr( $parsed_args['name'] ); 1355 if ( $parsed_args['multi'] && ! $parsed_args['id'] ) { 1356 $id = ''; 1357 } else { 1358 $id = $parsed_args['id'] ? " id='" . esc_attr( $parsed_args['id'] ) . "'" : " id='$name'"; 1359 } 1360 $output = "<select name='{$name}'{$id} class='" . $parsed_args['class'] . "'>\n"; 1361 1362 if ( $show_option_all ) { 1363 $output .= "\t<option value='0'>$show_option_all</option>\n"; 1364 } 1365 1366 if ( $show_option_none ) { 1367 $_selected = selected( $option_none_value, $parsed_args['selected'], false ); 1368 $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n"; 1369 } 1370 1371 if ( $parsed_args['include_selected'] && ( $parsed_args['selected'] > 0 ) ) { 1372 $found_selected = false; 1373 $parsed_args['selected'] = (int) $parsed_args['selected']; 1374 1375 foreach ( (array) $users as $user ) { 1376 $user->ID = (int) $user->ID; 1377 if ( $user->ID === $parsed_args['selected'] ) { 1378 $found_selected = true; 1379 } 1380 } 1381 1382 if ( ! $found_selected ) { 1383 $selected_user = get_userdata( $parsed_args['selected'] ); 1384 if ( $selected_user ) { 1385 $users[] = $selected_user; 1386 } 1387 } 1388 } 1389 1390 foreach ( (array) $users as $user ) { 1391 if ( 'display_name_with_login' === $show ) { 1392 /* translators: 1: User's display name, 2: User login. */ 1393 $display = sprintf( _x( '%1$s (%2$s)', 'user dropdown' ), $user->display_name, $user->user_login ); 1394 } elseif ( ! empty( $user->$show ) ) { 1395 $display = $user->$show; 1396 } else { 1397 $display = '(' . $user->user_login . ')'; 1398 } 1399 1400 $_selected = selected( $user->ID, $parsed_args['selected'], false ); 1401 $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n"; 1402 } 1403 1404 $output .= '</select>'; 1405 } 1406 1407 /** 1408 * Filters the wp_dropdown_users() HTML output. 1409 * 1410 * @since 2.3.0 1411 * 1412 * @param string $output HTML output generated by wp_dropdown_users(). 1413 */ 1414 $html = apply_filters( 'wp_dropdown_users', $output ); 1415 1416 if ( $parsed_args['echo'] ) { 1417 echo $html; 1418 } 1419 return $html; 1420 } 1421 1422 /** 1423 * Sanitize user field based on context. 1424 * 1425 * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The 1426 * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display' 1427 * when calling filters. 1428 * 1429 * @since 2.3.0 1430 * 1431 * @param string $field The user Object field name. 1432 * @param mixed $value The user Object value. 1433 * @param int $user_id User ID. 1434 * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display', 1435 * 'attribute' and 'js'. 1436 * @return mixed Sanitized value. 1437 */ 1438 function sanitize_user_field( $field, $value, $user_id, $context ) { 1439 $int_fields = array( 'ID' ); 1440 if ( in_array( $field, $int_fields, true ) ) { 1441 $value = (int) $value; 1442 } 1443 1444 if ( 'raw' === $context ) { 1445 return $value; 1446 } 1447 1448 if ( ! is_string( $value ) && ! is_numeric( $value ) ) { 1449 return $value; 1450 } 1451 1452 $prefixed = false !== strpos( $field, 'user_' ); 1453 1454 if ( 'edit' === $context ) { 1455 if ( $prefixed ) { 1456 1457 /** This filter is documented in wp-includes/post.php */ 1458 $value = apply_filters( "edit_{$field}", $value, $user_id ); 1459 } else { 1460 1461 /** 1462 * Filters a user field value in the 'edit' context. 1463 * 1464 * The dynamic portion of the hook name, `$field`, refers to the prefixed user 1465 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc. 1466 * 1467 * @since 2.9.0 1468 * 1469 * @param mixed $value Value of the prefixed user field. 1470 * @param int $user_id User ID. 1471 */ 1472 $value = apply_filters( "edit_user_{$field}", $value, $user_id ); 1473 } 1474 1475 if ( 'description' === $field ) { 1476 $value = esc_html( $value ); // textarea_escaped? 1477 } else { 1478 $value = esc_attr( $value ); 1479 } 1480 } elseif ( 'db' === $context ) { 1481 if ( $prefixed ) { 1482 /** This filter is documented in wp-includes/post.php */ 1483 $value = apply_filters( "pre_{$field}", $value ); 1484 } else { 1485 1486 /** 1487 * Filters the value of a user field in the 'db' context. 1488 * 1489 * The dynamic portion of the hook name, `$field`, refers to the prefixed user 1490 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc. 1491 * 1492 * @since 2.9.0 1493 * 1494 * @param mixed $value Value of the prefixed user field. 1495 */ 1496 $value = apply_filters( "pre_user_{$field}", $value ); 1497 } 1498 } else { 1499 // Use display filters by default. 1500 if ( $prefixed ) { 1501 1502 /** This filter is documented in wp-includes/post.php */ 1503 $value = apply_filters( "{$field}", $value, $user_id, $context ); 1504 } else { 1505 1506 /** 1507 * Filters the value of a user field in a standard context. 1508 * 1509 * The dynamic portion of the hook name, `$field`, refers to the prefixed user 1510 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc. 1511 * 1512 * @since 2.9.0 1513 * 1514 * @param mixed $value The user object value to sanitize. 1515 * @param int $user_id User ID. 1516 * @param string $context The context to filter within. 1517 */ 1518 $value = apply_filters( "user_{$field}", $value, $user_id, $context ); 1519 } 1520 } 1521 1522 if ( 'user_url' === $field ) { 1523 $value = esc_url( $value ); 1524 } 1525 1526 if ( 'attribute' === $context ) { 1527 $value = esc_attr( $value ); 1528 } elseif ( 'js' === $context ) { 1529 $value = esc_js( $value ); 1530 } 1531 return $value; 1532 } 1533 1534 /** 1535 * Update all user caches 1536 * 1537 * @since 3.0.0 1538 * 1539 * @param object|WP_User $user User object or database row to be cached 1540 * @return void|false Void on success, false on failure. 1541 */ 1542 function update_user_caches( $user ) { 1543 if ( $user instanceof WP_User ) { 1544 if ( ! $user->exists() ) { 1545 return false; 1546 } 1547 1548 $user = $user->data; 1549 } 1550 1551 wp_cache_add( $user->ID, $user, 'users' ); 1552 wp_cache_add( $user->user_login, $user->ID, 'userlogins' ); 1553 wp_cache_add( $user->user_email, $user->ID, 'useremail' ); 1554 wp_cache_add( $user->user_nicename, $user->ID, 'userslugs' ); 1555 } 1556 1557 /** 1558 * Clean all user caches 1559 * 1560 * @since 3.0.0 1561 * @since 4.4.0 'clean_user_cache' action was added. 1562 * 1563 * @param WP_User|int $user User object or ID to be cleaned from the cache 1564 */ 1565 function clean_user_cache( $user ) { 1566 if ( is_numeric( $user ) ) { 1567 $user = new WP_User( $user ); 1568 } 1569 1570 if ( ! $user->exists() ) { 1571 return; 1572 } 1573 1574 wp_cache_delete( $user->ID, 'users' ); 1575 wp_cache_delete( $user->user_login, 'userlogins' ); 1576 wp_cache_delete( $user->user_email, 'useremail' ); 1577 wp_cache_delete( $user->user_nicename, 'userslugs' ); 1578 1579 /** 1580 * Fires immediately after the given user's cache is cleaned. 1581 * 1582 * @since 4.4.0 1583 * 1584 * @param int $user_id User ID. 1585 * @param WP_User $user User object. 1586 */ 1587 do_action( 'clean_user_cache', $user->ID, $user ); 1588 } 1589 1590 /** 1591 * Determines whether the given username exists. 1592 * 1593 * For more information on this and similar theme functions, check out 1594 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 1595 * Conditional Tags} article in the Theme Developer Handbook. 1596 * 1597 * @since 2.0.0 1598 * 1599 * @param string $username The username to check for existence. 1600 * @return int|false The user ID on success, false on failure. 1601 */ 1602 function username_exists( $username ) { 1603 $user = get_user_by( 'login', $username ); 1604 if ( $user ) { 1605 $user_id = $user->ID; 1606 } else { 1607 $user_id = false; 1608 } 1609 1610 /** 1611 * Filters whether the given username exists. 1612 * 1613 * @since 4.9.0 1614 * 1615 * @param int|false $user_id The user ID associated with the username, 1616 * or false if the username does not exist. 1617 * @param string $username The username to check for existence. 1618 */ 1619 return apply_filters( 'username_exists', $user_id, $username ); 1620 } 1621 1622 /** 1623 * Determines whether the given email exists. 1624 * 1625 * For more information on this and similar theme functions, check out 1626 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 1627 * Conditional Tags} article in the Theme Developer Handbook. 1628 * 1629 * @since 2.1.0 1630 * 1631 * @param string $email The email to check for existence. 1632 * @return int|false The user ID on success, false on failure. 1633 */ 1634 function email_exists( $email ) { 1635 $user = get_user_by( 'email', $email ); 1636 if ( $user ) { 1637 $user_id = $user->ID; 1638 } else { 1639 $user_id = false; 1640 } 1641 1642 /** 1643 * Filters whether the given email exists. 1644 * 1645 * @since 5.6.0 1646 * 1647 * @param int|false $user_id The user ID associated with the email, 1648 * or false if the email does not exist. 1649 * @param string $email The email to check for existence. 1650 */ 1651 return apply_filters( 'email_exists', $user_id, $email ); 1652 } 1653 1654 /** 1655 * Checks whether a username is valid. 1656 * 1657 * @since 2.0.1 1658 * @since 4.4.0 Empty sanitized usernames are now considered invalid. 1659 * 1660 * @param string $username Username. 1661 * @return bool Whether username given is valid. 1662 */ 1663 function validate_username( $username ) { 1664 $sanitized = sanitize_user( $username, true ); 1665 $valid = ( $sanitized == $username && ! empty( $sanitized ) ); 1666 1667 /** 1668 * Filters whether the provided username is valid. 1669 * 1670 * @since 2.0.1 1671 * 1672 * @param bool $valid Whether given username is valid. 1673 * @param string $username Username to check. 1674 */ 1675 return apply_filters( 'validate_username', $valid, $username ); 1676 } 1677 1678 /** 1679 * Insert a user into the database. 1680 * 1681 * Most of the `$userdata` array fields have filters associated with the values. Exceptions are 1682 * 'ID', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 1683 * 'user_registered', 'user_activation_key', 'spam', and 'role'. The filters have the prefix 1684 * 'pre_user_' followed by the field name. An example using 'description' would have the filter 1685 * called 'pre_user_description' that can be hooked into. 1686 * 1687 * @since 2.0.0 1688 * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact 1689 * methods for new installations. See wp_get_user_contact_methods(). 1690 * @since 4.7.0 The user's locale can be passed to `$userdata`. 1691 * @since 5.3.0 The `user_activation_key` field can be passed to `$userdata`. 1692 * @since 5.3.0 The `spam` field can be passed to `$userdata` (Multisite only). 1693 * 1694 * @global wpdb $wpdb WordPress database abstraction object. 1695 * 1696 * @param array|object|WP_User $userdata { 1697 * An array, object, or WP_User object of user data arguments. 1698 * 1699 * @type int $ID User ID. If supplied, the user will be updated. 1700 * @type string $user_pass The plain-text user password. 1701 * @type string $user_login The user's login username. 1702 * @type string $user_nicename The URL-friendly user name. 1703 * @type string $user_url The user URL. 1704 * @type string $user_email The user email address. 1705 * @type string $display_name The user's display name. 1706 * Default is the user's username. 1707 * @type string $nickname The user's nickname. 1708 * Default is the user's username. 1709 * @type string $first_name The user's first name. For new users, will be used 1710 * to build the first part of the user's display name 1711 * if `$display_name` is not specified. 1712 * @type string $last_name The user's last name. For new users, will be used 1713 * to build the second part of the user's display name 1714 * if `$display_name` is not specified. 1715 * @type string $description The user's biographical description. 1716 * @type string $rich_editing Whether to enable the rich-editor for the user. 1717 * Accepts 'true' or 'false' as a string literal, 1718 * not boolean. Default 'true'. 1719 * @type string $syntax_highlighting Whether to enable the rich code editor for the user. 1720 * Accepts 'true' or 'false' as a string literal, 1721 * not boolean. Default 'true'. 1722 * @type string $comment_shortcuts Whether to enable comment moderation keyboard 1723 * shortcuts for the user. Accepts 'true' or 'false' 1724 * as a string literal, not boolean. Default 'false'. 1725 * @type string $admin_color Admin color scheme for the user. Default 'fresh'. 1726 * @type bool $use_ssl Whether the user should always access the admin over 1727 * https. Default false. 1728 * @type string $user_registered Date the user registered. Format is 'Y-m-d H:i:s'. 1729 * @type string $user_activation_key Password reset key. Default empty. 1730 * @type bool $spam Multisite only. Whether the user is marked as spam. 1731 * Default false. 1732 * @type string $show_admin_bar_front Whether to display the Admin Bar for the user 1733 * on the site's front end. Accepts 'true' or 'false' 1734 * as a string literal, not boolean. Default 'true'. 1735 * @type string $role User's role. 1736 * @type string $locale User's locale. Default empty. 1737 * } 1738 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not 1739 * be created. 1740 */ 1741 function wp_insert_user( $userdata ) { 1742 global $wpdb; 1743 1744 if ( $userdata instanceof stdClass ) { 1745 $userdata = get_object_vars( $userdata ); 1746 } elseif ( $userdata instanceof WP_User ) { 1747 $userdata = $userdata->to_array(); 1748 } 1749 1750 // Are we updating or creating? 1751 if ( ! empty( $userdata['ID'] ) ) { 1752 $ID = (int) $userdata['ID']; 1753 $update = true; 1754 $old_user_data = get_userdata( $ID ); 1755 1756 if ( ! $old_user_data ) { 1757 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) ); 1758 } 1759 1760 // Hashed in wp_update_user(), plaintext if called directly. 1761 $user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass; 1762 } else { 1763 $update = false; 1764 // Hash the password. 1765 $user_pass = wp_hash_password( $userdata['user_pass'] ); 1766 } 1767 1768 $sanitized_user_login = sanitize_user( $userdata['user_login'], true ); 1769 1770 /** 1771 * Filters a username after it has been sanitized. 1772 * 1773 * This filter is called before the user is created or updated. 1774 * 1775 * @since 2.0.3 1776 * 1777 * @param string $sanitized_user_login Username after it has been sanitized. 1778 */ 1779 $pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login ); 1780 1781 // Remove any non-printable chars from the login string to see if we have ended up with an empty username. 1782 $user_login = trim( $pre_user_login ); 1783 1784 // user_login must be between 0 and 60 characters. 1785 if ( empty( $user_login ) ) { 1786 return new WP_Error( 'empty_user_login', __( 'Cannot create a user with an empty login name.' ) ); 1787 } elseif ( mb_strlen( $user_login ) > 60 ) { 1788 return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) ); 1789 } 1790 1791 if ( ! $update && username_exists( $user_login ) ) { 1792 return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) ); 1793 } 1794 1795 /** 1796 * Filters the list of disallowed usernames. 1797 * 1798 * @since 4.4.0 1799 * 1800 * @param array $usernames Array of disallowed usernames. 1801 */ 1802 $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() ); 1803 1804 if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ), true ) ) { 1805 return new WP_Error( 'invalid_username', __( 'Sorry, that username is not allowed.' ) ); 1806 } 1807 1808 /* 1809 * If a nicename is provided, remove unsafe user characters before using it. 1810 * Otherwise build a nicename from the user_login. 1811 */ 1812 if ( ! empty( $userdata['user_nicename'] ) ) { 1813 $user_nicename = sanitize_user( $userdata['user_nicename'], true ); 1814 if ( mb_strlen( $user_nicename ) > 50 ) { 1815 return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) ); 1816 } 1817 } else { 1818 $user_nicename = mb_substr( $user_login, 0, 50 ); 1819 } 1820 1821 $user_nicename = sanitize_title( $user_nicename ); 1822 1823 /** 1824 * Filters a user's nicename before the user is created or updated. 1825 * 1826 * @since 2.0.3 1827 * 1828 * @param string $user_nicename The user's nicename. 1829 */ 1830 $user_nicename = apply_filters( 'pre_user_nicename', $user_nicename ); 1831 1832 $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $user_nicename, $user_login ) ); 1833 1834 if ( $user_nicename_check ) { 1835 $suffix = 2; 1836 while ( $user_nicename_check ) { 1837 // user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix. 1838 $base_length = 49 - mb_strlen( $suffix ); 1839 $alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix"; 1840 $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $alt_user_nicename, $user_login ) ); 1841 $suffix++; 1842 } 1843 $user_nicename = $alt_user_nicename; 1844 } 1845 1846 $raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email']; 1847 1848 /** 1849 * Filters a user's email before the user is created or updated. 1850 * 1851 * @since 2.0.3 1852 * 1853 * @param string $raw_user_email The user's email. 1854 */ 1855 $user_email = apply_filters( 'pre_user_email', $raw_user_email ); 1856 1857 /* 1858 * If there is no update, just check for `email_exists`. If there is an update, 1859 * check if current email and new email are the same, and check `email_exists` 1860 * accordingly. 1861 */ 1862 if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) ) 1863 && ! defined( 'WP_IMPORTING' ) 1864 && email_exists( $user_email ) 1865 ) { 1866 return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) ); 1867 } 1868 1869 $raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url']; 1870 1871 /** 1872 * Filters a user's URL before the user is created or updated. 1873 * 1874 * @since 2.0.3 1875 * 1876 * @param string $raw_user_url The user's URL. 1877 */ 1878 $user_url = apply_filters( 'pre_user_url', $raw_user_url ); 1879 1880 $user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered']; 1881 1882 $user_activation_key = empty( $userdata['user_activation_key'] ) ? '' : $userdata['user_activation_key']; 1883 1884 if ( ! empty( $userdata['spam'] ) && ! is_multisite() ) { 1885 return new WP_Error( 'no_spam', __( 'Sorry, marking a user as spam is only supported on Multisite.' ) ); 1886 } 1887 1888 $spam = empty( $userdata['spam'] ) ? 0 : (bool) $userdata['spam']; 1889 1890 // Store values to save in user meta. 1891 $meta = array(); 1892 1893 $nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname']; 1894 1895 /** 1896 * Filters a user's nickname before the user is created or updated. 1897 * 1898 * @since 2.0.3 1899 * 1900 * @param string $nickname The user's nickname. 1901 */ 1902 $meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname ); 1903 1904 $first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name']; 1905 1906 /** 1907 * Filters a user's first name before the user is created or updated. 1908 * 1909 * @since 2.0.3 1910 * 1911 * @param string $first_name The user's first name. 1912 */ 1913 $meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name ); 1914 1915 $last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name']; 1916 1917 /** 1918 * Filters a user's last name before the user is created or updated. 1919 * 1920 * @since 2.0.3 1921 * 1922 * @param string $last_name The user's last name. 1923 */ 1924 $meta['last_name'] = apply_filters( 'pre_user_last_name', $last_name ); 1925 1926 if ( empty( $userdata['display_name'] ) ) { 1927 if ( $update ) { 1928 $display_name = $user_login; 1929 } elseif ( $meta['first_name'] && $meta['last_name'] ) { 1930 /* translators: 1: User's first name, 2: Last name. */ 1931 $display_name = sprintf( _x( '%1$s %2$s', 'Display name based on first name and last name' ), $meta['first_name'], $meta['last_name'] ); 1932 } elseif ( $meta['first_name'] ) { 1933 $display_name = $meta['first_name']; 1934 } elseif ( $meta['last_name'] ) { 1935 $display_name = $meta['last_name']; 1936 } else { 1937 $display_name = $user_login; 1938 } 1939 } else { 1940 $display_name = $userdata['display_name']; 1941 } 1942 1943 /** 1944 * Filters a user's display name before the user is created or updated. 1945 * 1946 * @since 2.0.3 1947 * 1948 * @param string $display_name The user's display name. 1949 */ 1950 $display_name = apply_filters( 'pre_user_display_name', $display_name ); 1951 1952 $description = empty( $userdata['description'] ) ? '' : $userdata['description']; 1953 1954 /** 1955 * Filters a user's description before the user is created or updated. 1956 * 1957 * @since 2.0.3 1958 * 1959 * @param string $description The user's description. 1960 */ 1961 $meta['description'] = apply_filters( 'pre_user_description', $description ); 1962 1963 $meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing']; 1964 1965 $meta['syntax_highlighting'] = empty( $userdata['syntax_highlighting'] ) ? 'true' : $userdata['syntax_highlighting']; 1966 1967 $meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true'; 1968 1969 $admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color']; 1970 $meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color ); 1971 1972 $meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : (bool) $userdata['use_ssl']; 1973 1974 $meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front']; 1975 1976 $meta['locale'] = isset( $userdata['locale'] ) ? $userdata['locale'] : ''; 1977 1978 $compacted = compact( 'user_pass', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_activation_key', 'display_name' ); 1979 $data = wp_unslash( $compacted ); 1980 1981 if ( ! $update ) { 1982 $data = $data + compact( 'user_login' ); 1983 } 1984 1985 if ( is_multisite() ) { 1986 $data = $data + compact( 'spam' ); 1987 } 1988 1989 /** 1990 * Filters user data before the record is created or updated. 1991 * 1992 * It only includes data in the users table, not any user metadata. 1993 * 1994 * @since 4.9.0 1995 * 1996 * @param array $data { 1997 * Values and keys for the user. 1998 * 1999 * @type string $user_login The user's login. Only included if $update == false 2000 * @type string $user_pass The user's password. 2001 * @type string $user_email The user's email. 2002 * @type string $user_url The user's url. 2003 * @type string $user_nicename The user's nice name. Defaults to a URL-safe version of user's login 2004 * @type string $display_name The user's display name. 2005 * @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to 2006 * the current UTC timestamp. 2007 * } 2008 * @param bool $update Whether the user is being updated rather than created. 2009 * @param int|null $id ID of the user to be updated, or NULL if the user is being created. 2010 */ 2011 $data = apply_filters( 'wp_pre_insert_user_data', $data, $update, $update ? (int) $ID : null ); 2012 2013 if ( empty( $data ) || ! is_array( $data ) ) { 2014 return new WP_Error( 'empty_data', __( 'Not enough data to create this user.' ) ); 2015 } 2016 2017 if ( $update ) { 2018 if ( $user_email !== $old_user_data->user_email || $user_pass !== $old_user_data->user_pass ) { 2019 $data['user_activation_key'] = ''; 2020 } 2021 $wpdb->update( $wpdb->users, $data, compact( 'ID' ) ); 2022 $user_id = (int) $ID; 2023 } else { 2024 $wpdb->insert( $wpdb->users, $data ); 2025 $user_id = (int) $wpdb->insert_id; 2026 } 2027 2028 $user = new WP_User( $user_id ); 2029 2030 /** 2031 * Filters a user's meta values and keys immediately after the user is created or updated 2032 * and before any user meta is inserted or updated. 2033 * 2034 * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`. 2035 * 2036 * @since 4.4.0 2037 * 2038 * @param array $meta { 2039 * Default meta values and keys for the user. 2040 * 2041 * @type string $nickname The user's nickname. Default is the user's username. 2042 * @type string $first_name The user's first name. 2043 * @type string $last_name The user's last name. 2044 * @type string $description The user's description. 2045 * @type string $rich_editing Whether to enable the rich-editor for the user. Default 'true'. 2046 * @type string $syntax_highlighting Whether to enable the rich code editor for the user. Default 'true'. 2047 * @type string $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default 'false'. 2048 * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'. 2049 * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL 2050 * is not forced. 2051 * @type string $show_admin_bar_front Whether to show the admin bar on the front end for the user. 2052 * Default 'true'. 2053 * @type string $locale User's locale. Default empty. 2054 * } 2055 * @param WP_User $user User object. 2056 * @param bool $update Whether the user is being updated rather than created. 2057 */ 2058 $meta = apply_filters( 'insert_user_meta', $meta, $user, $update ); 2059 2060 // Update user meta. 2061 foreach ( $meta as $key => $value ) { 2062 update_user_meta( $user_id, $key, $value ); 2063 } 2064 2065 foreach ( wp_get_user_contact_methods( $user ) as $key => $value ) { 2066 if ( isset( $userdata[ $key ] ) ) { 2067 update_user_meta( $user_id, $key, $userdata[ $key ] ); 2068 } 2069 } 2070 2071 if ( isset( $userdata['role'] ) ) { 2072 $user->set_role( $userdata['role'] ); 2073 } elseif ( ! $update ) { 2074 $user->set_role( get_option( 'default_role' ) ); 2075 } 2076 2077 clean_user_cache( $user_id ); 2078 2079 if ( $update ) { 2080 /** 2081 * Fires immediately after an existing user is updated. 2082 * 2083 * @since 2.0.0 2084 * 2085 * @param int $user_id User ID. 2086 * @param WP_User $old_user_data Object containing user's data prior to update. 2087 */ 2088 do_action( 'profile_update', $user_id, $old_user_data ); 2089 2090 if ( isset( $userdata['spam'] ) && $userdata['spam'] != $old_user_data->spam ) { 2091 if ( 1 == $userdata['spam'] ) { 2092 /** 2093 * Fires after the user is marked as a SPAM user. 2094 * 2095 * @since 3.0.0 2096 * 2097 * @param int $user_id ID of the user marked as SPAM. 2098 */ 2099 do_action( 'make_spam_user', $user_id ); 2100 } else { 2101 /** 2102 * Fires after the user is marked as a HAM user. Opposite of SPAM. 2103 * 2104 * @since 3.0.0 2105 * 2106 * @param int $user_id ID of the user marked as HAM. 2107 */ 2108 do_action( 'make_ham_user', $user_id ); 2109 } 2110 } 2111 } else { 2112 /** 2113 * Fires immediately after a new user is registered. 2114 * 2115 * @since 1.5.0 2116 * 2117 * @param int $user_id User ID. 2118 */ 2119 do_action( 'user_register', $user_id ); 2120 } 2121 2122 return $user_id; 2123 } 2124 2125 /** 2126 * Update a user in the database. 2127 * 2128 * It is possible to update a user's password by specifying the 'user_pass' 2129 * value in the $userdata parameter array. 2130 * 2131 * If current user's password is being updated, then the cookies will be 2132 * cleared. 2133 * 2134 * @since 2.0.0 2135 * 2136 * @see wp_insert_user() For what fields can be set in $userdata. 2137 * 2138 * @param array|object|WP_User $userdata An array of user data or a user object of type stdClass or WP_User. 2139 * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated. 2140 */ 2141 function wp_update_user( $userdata ) { 2142 if ( $userdata instanceof stdClass ) { 2143 $userdata = get_object_vars( $userdata ); 2144 } elseif ( $userdata instanceof WP_User ) { 2145 $userdata = $userdata->to_array(); 2146 } 2147 2148 $ID = isset( $userdata['ID'] ) ? (int) $userdata['ID'] : 0; 2149 if ( ! $ID ) { 2150 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) ); 2151 } 2152 2153 // First, get all of the original fields. 2154 $user_obj = get_userdata( $ID ); 2155 if ( ! $user_obj ) { 2156 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) ); 2157 } 2158 2159 $user = $user_obj->to_array(); 2160 2161 // Add additional custom fields. 2162 foreach ( _get_additional_user_keys( $user_obj ) as $key ) { 2163 $user[ $key ] = get_user_meta( $ID, $key, true ); 2164 } 2165 2166 // Escape data pulled from DB. 2167 $user = add_magic_quotes( $user ); 2168 2169 if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) { 2170 // If password is changing, hash it now. 2171 $plaintext_pass = $userdata['user_pass']; 2172 $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] ); 2173 2174 /** 2175 * Filters whether to send the password change email. 2176 * 2177 * @since 4.3.0 2178 * 2179 * @see wp_insert_user() For `$user` and `$userdata` fields. 2180 * 2181 * @param bool $send Whether to send the email. 2182 * @param array $user The original user array. 2183 * @param array $userdata The updated user array. 2184 */ 2185 $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata ); 2186 } 2187 2188 if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) { 2189 /** 2190 * Filters whether to send the email change email. 2191 * 2192 * @since 4.3.0 2193 * 2194 * @see wp_insert_user() For `$user` and `$userdata` fields. 2195 * 2196 * @param bool $send Whether to send the email. 2197 * @param array $user The original user array. 2198 * @param array $userdata The updated user array. 2199 */ 2200 $send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata ); 2201 } 2202 2203 clean_user_cache( $user_obj ); 2204 2205 // Merge old and new fields with new fields overwriting old ones. 2206 $userdata = array_merge( $user, $userdata ); 2207 $user_id = wp_insert_user( $userdata ); 2208 2209 if ( ! is_wp_error( $user_id ) ) { 2210 2211 $blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 2212 2213 $switched_locale = false; 2214 if ( ! empty( $send_password_change_email ) || ! empty( $send_email_change_email ) ) { 2215 $switched_locale = switch_to_locale( get_user_locale( $user_id ) ); 2216 } 2217 2218 if ( ! empty( $send_password_change_email ) ) { 2219 /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */ 2220 $pass_change_text = __( 2221 'Hi ###USERNAME###, 2222 2223 This notice confirms that your password was changed on ###SITENAME###. 2224 2225 If you did not change your password, please contact the Site Administrator at 2226 ###ADMIN_EMAIL### 2227 2228 This email has been sent to ###EMAIL### 2229 2230 Regards, 2231 All at ###SITENAME### 2232 ###SITEURL###' 2233 ); 2234 2235 $pass_change_email = array( 2236 'to' => $user['user_email'], 2237 /* translators: Password change notification email subject. %s: Site title. */ 2238 'subject' => __( '[%s] Password Changed' ), 2239 'message' => $pass_change_text, 2240 'headers' => '', 2241 ); 2242 2243 /** 2244 * Filters the contents of the email sent when the user's password is changed. 2245 * 2246 * @since 4.3.0 2247 * 2248 * @param array $pass_change_email { 2249 * Used to build wp_mail(). 2250 * 2251 * @type string $to The intended recipients. Add emails in a comma separated string. 2252 * @type string $subject The subject of the email. 2253 * @type string $message The content of the email. 2254 * The following strings have a special meaning and will get replaced dynamically: 2255 * - ###USERNAME### The current user's username. 2256 * - ###ADMIN_EMAIL### The admin email in case this was unexpected. 2257 * - ###EMAIL### The user's email address. 2258 * - ###SITENAME### The name of the site. 2259 * - ###SITEURL### The URL to the site. 2260 * @type string $headers Headers. Add headers in a newline (\r\n) separated string. 2261 * } 2262 * @param array $user The original user array. 2263 * @param array $userdata The updated user array. 2264 */ 2265 $pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata ); 2266 2267 $pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] ); 2268 $pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] ); 2269 $pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] ); 2270 $pass_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $pass_change_email['message'] ); 2271 $pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] ); 2272 2273 wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] ); 2274 } 2275 2276 if ( ! empty( $send_email_change_email ) ) { 2277 /* translators: Do not translate USERNAME, ADMIN_EMAIL, NEW_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */ 2278 $email_change_text = __( 2279 'Hi ###USERNAME###, 2280 2281 This notice confirms that your email address on ###SITENAME### was changed to ###NEW_EMAIL###. 2282 2283 If you did not change your email, please contact the Site Administrator at 2284 ###ADMIN_EMAIL### 2285 2286 This email has been sent to ###EMAIL### 2287 2288 Regards, 2289 All at ###SITENAME### 2290 ###SITEURL###' 2291 ); 2292 2293 $email_change_email = array( 2294 'to' => $user['user_email'], 2295 /* translators: Email change notification email subject. %s: Site title. */ 2296 'subject' => __( '[%s] Email Changed' ), 2297 'message' => $email_change_text, 2298 'headers' => '', 2299 ); 2300 2301 /** 2302 * Filters the contents of the email sent when the user's email is changed. 2303 * 2304 * @since 4.3.0 2305 * 2306 * @param array $email_change_email { 2307 * Used to build wp_mail(). 2308 * 2309 * @type string $to The intended recipients. 2310 * @type string $subject The subject of the email. 2311 * @type string $message The content of the email. 2312 * The following strings have a special meaning and will get replaced dynamically: 2313 * - ###USERNAME### The current user's username. 2314 * - ###ADMIN_EMAIL### The admin email in case this was unexpected. 2315 * - ###NEW_EMAIL### The new email address. 2316 * - ###EMAIL### The old email address. 2317 * - ###SITENAME### The name of the site. 2318 * - ###SITEURL### The URL to the site. 2319 * @type string $headers Headers. 2320 * } 2321 * @param array $user The original user array. 2322 * @param array $userdata The updated user array. 2323 */ 2324 $email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata ); 2325 2326 $email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] ); 2327 $email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] ); 2328 $email_change_email['message'] = str_replace( '###NEW_EMAIL###', $userdata['user_email'], $email_change_email['message'] ); 2329 $email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] ); 2330 $email_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $email_change_email['message'] ); 2331 $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] ); 2332 2333 wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] ); 2334 } 2335 2336 if ( $switched_locale ) { 2337 restore_previous_locale(); 2338 } 2339 } 2340 2341 // Update the cookies if the password changed. 2342 $current_user = wp_get_current_user(); 2343 if ( $current_user->ID == $ID ) { 2344 if ( isset( $plaintext_pass ) ) { 2345 wp_clear_auth_cookie(); 2346 2347 // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration. 2348 // If it's greater than this, then we know the user checked 'Remember Me' when they logged in. 2349 $logged_in_cookie = wp_parse_auth_cookie( '', 'logged_in' ); 2350 /** This filter is documented in wp-includes/pluggable.php */ 2351 $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $ID, false ); 2352 $remember = false; 2353 if ( false !== $logged_in_cookie && ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life ) { 2354 $remember = true; 2355 } 2356 2357 wp_set_auth_cookie( $ID, $remember ); 2358 } 2359 } 2360 2361 return $user_id; 2362 } 2363 2364 /** 2365 * A simpler way of inserting a user into the database. 2366 * 2367 * Creates a new user with just the username, password, and email. For more 2368 * complex user creation use wp_insert_user() to specify more information. 2369 * 2370 * @since 2.0.0 2371 * 2372 * @see wp_insert_user() More complete way to create a new user. 2373 * 2374 * @param string $username The user's username. 2375 * @param string $password The user's password. 2376 * @param string $email Optional. The user's email. Default empty. 2377 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not 2378 * be created. 2379 */ 2380 function wp_create_user( $username, $password, $email = '' ) { 2381 $user_login = wp_slash( $username ); 2382 $user_email = wp_slash( $email ); 2383 $user_pass = $password; 2384 2385 $userdata = compact( 'user_login', 'user_email', 'user_pass' ); 2386 return wp_insert_user( $userdata ); 2387 } 2388 2389 /** 2390 * Returns a list of meta keys to be (maybe) populated in wp_update_user(). 2391 * 2392 * The list of keys returned via this function are dependent on the presence 2393 * of those keys in the user meta data to be set. 2394 * 2395 * @since 3.3.0 2396 * @access private 2397 * 2398 * @param WP_User $user WP_User instance. 2399 * @return string[] List of user keys to be populated in wp_update_user(). 2400 */ 2401 function _get_additional_user_keys( $user ) { 2402 $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' ); 2403 return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) ); 2404 } 2405 2406 /** 2407 * Set up the user contact methods. 2408 * 2409 * Default contact methods were removed in 3.6. A filter dictates contact methods. 2410 * 2411 * @since 3.7.0 2412 * 2413 * @param WP_User $user Optional. WP_User object. 2414 * @return string[] Array of contact method labels keyed by contact method. 2415 */ 2416 function wp_get_user_contact_methods( $user = null ) { 2417 $methods = array(); 2418 if ( get_site_option( 'initial_db_version' ) < 23588 ) { 2419 $methods = array( 2420 'aim' => __( 'AIM' ), 2421 'yim' => __( 'Yahoo IM' ), 2422 'jabber' => __( 'Jabber / Google Talk' ), 2423 ); 2424 } 2425 2426 /** 2427 * Filters the user contact methods. 2428 * 2429 * @since 2.9.0 2430 * 2431 * @param string[] $methods Array of contact method labels keyed by contact method. 2432 * @param WP_User $user WP_User object. 2433 */ 2434 return apply_filters( 'user_contactmethods', $methods, $user ); 2435 } 2436 2437 /** 2438 * The old private function for setting up user contact methods. 2439 * 2440 * Use wp_get_user_contact_methods() instead. 2441 * 2442 * @since 2.9.0 2443 * @access private 2444 * 2445 * @param WP_User $user Optional. WP_User object. Default null. 2446 * @return string[] Array of contact method labels keyed by contact method. 2447 */ 2448 function _wp_get_user_contactmethods( $user = null ) { 2449 return wp_get_user_contact_methods( $user ); 2450 } 2451 2452 /** 2453 * Gets the text suggesting how to create strong passwords. 2454 * 2455 * @since 4.1.0 2456 * 2457 * @return string The password hint text. 2458 */ 2459 function wp_get_password_hint() { 2460 $hint = __( 'Hint: The password should be at least twelve characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ & ).' ); 2461 2462 /** 2463 * Filters the text describing the site's password complexity policy. 2464 * 2465 * @since 4.1.0 2466 * 2467 * @param string $hint The password hint text. 2468 */ 2469 return apply_filters( 'password_hint', $hint ); 2470 } 2471 2472 /** 2473 * Creates, stores, then returns a password reset key for user. 2474 * 2475 * @since 4.4.0 2476 * 2477 * @global PasswordHash $wp_hasher Portable PHP password hashing framework. 2478 * 2479 * @param WP_User $user User to retrieve password reset key for. 2480 * @return string|WP_Error Password reset key on success. WP_Error on error. 2481 */ 2482 function get_password_reset_key( $user ) { 2483 global $wp_hasher; 2484 2485 if ( ! ( $user instanceof WP_User ) ) { 2486 return new WP_Error( 'invalidcombo', __( '<strong>Error</strong>: There is no account with that username or email address.' ) ); 2487 } 2488 2489 /** 2490 * Fires before a new password is retrieved. 2491 * 2492 * Use the {@see 'retrieve_password'} hook instead. 2493 * 2494 * @since 1.5.0 2495 * @deprecated 1.5.1 Misspelled. Use {@see 'retrieve_password'} hook instead. 2496 * 2497 * @param string $user_login The user login name. 2498 */ 2499 do_action_deprecated( 'retreive_password', array( $user->user_login ), '1.5.1', 'retrieve_password' ); 2500 2501 /** 2502 * Fires before a new password is retrieved. 2503 * 2504 * @since 1.5.1 2505 * 2506 * @param string $user_login The user login name. 2507 */ 2508 do_action( 'retrieve_password', $user->user_login ); 2509 2510 $allow = true; 2511 if ( is_multisite() && is_user_spammy( $user ) ) { 2512 $allow = false; 2513 } 2514 2515 /** 2516 * Filters whether to allow a password to be reset. 2517 * 2518 * @since 2.7.0 2519 * 2520 * @param bool $allow Whether to allow the password to be reset. Default true. 2521 * @param int $ID The ID of the user attempting to reset a password. 2522 */ 2523 $allow = apply_filters( 'allow_password_reset', $allow, $user->ID ); 2524 2525 if ( ! $allow ) { 2526 return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) ); 2527 } elseif ( is_wp_error( $allow ) ) { 2528 return $allow; 2529 } 2530 2531 // Generate something random for a password reset key. 2532 $key = wp_generate_password( 20, false ); 2533 2534 /** 2535 * Fires when a password reset key is generated. 2536 * 2537 * @since 2.5.0 2538 * 2539 * @param string $user_login The username for the user. 2540 * @param string $key The generated password reset key. 2541 */ 2542 do_action( 'retrieve_password_key', $user->user_login, $key ); 2543 2544 // Now insert the key, hashed, into the DB. 2545 if ( empty( $wp_hasher ) ) { 2546 require_once ABSPATH . WPINC . '/class-phpass.php'; 2547 $wp_hasher = new PasswordHash( 8, true ); 2548 } 2549 2550 $hashed = time() . ':' . $wp_hasher->HashPassword( $key ); 2551 2552 $key_saved = wp_update_user( 2553 array( 2554 'ID' => $user->ID, 2555 'user_activation_key' => $hashed, 2556 ) 2557 ); 2558 2559 if ( is_wp_error( $key_saved ) ) { 2560 return $key_saved; 2561 } 2562 2563 return $key; 2564 } 2565 2566 /** 2567 * Retrieves a user row based on password reset key and login 2568 * 2569 * A key is considered 'expired' if it exactly matches the value of the 2570 * user_activation_key field, rather than being matched after going through the 2571 * hashing process. This field is now hashed; old values are no longer accepted 2572 * but have a different WP_Error code so good user feedback can be provided. 2573 * 2574 * @since 3.1.0 2575 * 2576 * @global wpdb $wpdb WordPress database object for queries. 2577 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance. 2578 * 2579 * @param string $key Hash to validate sending user's password. 2580 * @param string $login The user login. 2581 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys. 2582 */ 2583 function check_password_reset_key( $key, $login ) { 2584 global $wpdb, $wp_hasher; 2585 2586 $key = preg_replace( '/[^a-z0-9]/i', '', $key ); 2587 2588 if ( empty( $key ) || ! is_string( $key ) ) { 2589 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) ); 2590 } 2591 2592 if ( empty( $login ) || ! is_string( $login ) ) { 2593 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) ); 2594 } 2595 2596 $user = get_user_by( 'login', $login ); 2597 2598 if ( ! $user ) { 2599 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) ); 2600 } 2601 2602 if ( empty( $wp_hasher ) ) { 2603 require_once ABSPATH . WPINC . '/class-phpass.php'; 2604 $wp_hasher = new PasswordHash( 8, true ); 2605 } 2606 2607 /** 2608 * Filters the expiration time of password reset keys. 2609 * 2610 * @since 4.3.0 2611 * 2612 * @param int $expiration The expiration time in seconds. 2613 */ 2614 $expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS ); 2615 2616 if ( false !== strpos( $user->user_activation_key, ':' ) ) { 2617 list( $pass_request_time, $pass_key ) = explode( ':', $user->user_activation_key, 2 ); 2618 $expiration_time = $pass_request_time + $expiration_duration; 2619 } else { 2620 $pass_key = $user->user_activation_key; 2621 $expiration_time = false; 2622 } 2623 2624 if ( ! $pass_key ) { 2625 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) ); 2626 } 2627 2628 $hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key ); 2629 2630 if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) { 2631 return $user; 2632 } elseif ( $hash_is_correct && $expiration_time ) { 2633 // Key has an expiration time that's passed. 2634 return new WP_Error( 'expired_key', __( 'Invalid key.' ) ); 2635 } 2636 2637 if ( hash_equals( $user->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) { 2638 $return = new WP_Error( 'expired_key', __( 'Invalid key.' ) ); 2639 $user_id = $user->ID; 2640 2641 /** 2642 * Filters the return value of check_password_reset_key() when an 2643 * old-style key is used. 2644 * 2645 * @since 3.7.0 Previously plain-text keys were stored in the database. 2646 * @since 4.3.0 Previously key hashes were stored without an expiration time. 2647 * 2648 * @param WP_Error $return A WP_Error object denoting an expired key. 2649 * Return a WP_User object to validate the key. 2650 * @param int $user_id The matched user ID. 2651 */ 2652 return apply_filters( 'password_reset_key_expired', $return, $user_id ); 2653 } 2654 2655 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) ); 2656 } 2657 2658 /** 2659 * Handles resetting the user's password. 2660 * 2661 * @since 2.5.0 2662 * 2663 * @param WP_User $user The user 2664 * @param string $new_pass New password for the user in plaintext 2665 */ 2666 function reset_password( $user, $new_pass ) { 2667 /** 2668 * Fires before the user's password is reset. 2669 * 2670 * @since 1.5.0 2671 * 2672 * @param WP_User $user The user. 2673 * @param string $new_pass New user password. 2674 */ 2675 do_action( 'password_reset', $user, $new_pass ); 2676 2677 wp_set_password( $new_pass, $user->ID ); 2678 update_user_option( $user->ID, 'default_password_nag', false, true ); 2679 2680 /** 2681 * Fires after the user's password is reset. 2682 * 2683 * @since 4.4.0 2684 * 2685 * @param WP_User $user The user. 2686 * @param string $new_pass New user password. 2687 */ 2688 do_action( 'after_password_reset', $user, $new_pass ); 2689 } 2690 2691 /** 2692 * Handles registering a new user. 2693 * 2694 * @since 2.5.0 2695 * 2696 * @param string $user_login User's username for logging in 2697 * @param string $user_email User's email address to send password and add 2698 * @return int|WP_Error Either user's ID or error on failure. 2699 */ 2700 function register_new_user( $user_login, $user_email ) { 2701 $errors = new WP_Error(); 2702 2703 $sanitized_user_login = sanitize_user( $user_login ); 2704 /** 2705 * Filters the email address of a user being registered. 2706 * 2707 * @since 2.1.0 2708 * 2709 * @param string $user_email The email address of the new user. 2710 */ 2711 $user_email = apply_filters( 'user_registration_email', $user_email ); 2712 2713 // Check the username. 2714 if ( '' === $sanitized_user_login ) { 2715 $errors->add( 'empty_username', __( '<strong>Error</strong>: Please enter a username.' ) ); 2716 } elseif ( ! validate_username( $user_login ) ) { 2717 $errors->add( 'invalid_username', __( '<strong>Error</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) ); 2718 $sanitized_user_login = ''; 2719 } elseif ( username_exists( $sanitized_user_login ) ) { 2720 $errors->add( 'username_exists', __( '<strong>Error</strong>: This username is already registered. Please choose another one.' ) ); 2721 2722 } else { 2723 /** This filter is documented in wp-includes/user.php */ 2724 $illegal_user_logins = (array) apply_filters( 'illegal_user_logins', array() ); 2725 if ( in_array( strtolower( $sanitized_user_login ), array_map( 'strtolower', $illegal_user_logins ), true ) ) { 2726 $errors->add( 'invalid_username', __( '<strong>Error</strong>: Sorry, that username is not allowed.' ) ); 2727 } 2728 } 2729 2730 // Check the email address. 2731 if ( '' === $user_email ) { 2732 $errors->add( 'empty_email', __( '<strong>Error</strong>: Please type your email address.' ) ); 2733 } elseif ( ! is_email( $user_email ) ) { 2734 $errors->add( 'invalid_email', __( '<strong>Error</strong>: The email address isn’t correct.' ) ); 2735 $user_email = ''; 2736 } elseif ( email_exists( $user_email ) ) { 2737 $errors->add( 'email_exists', __( '<strong>Error</strong>: This email is already registered. Please choose another one.' ) ); 2738 } 2739 2740 /** 2741 * Fires when submitting registration form data, before the user is created. 2742 * 2743 * @since 2.1.0 2744 * 2745 * @param string $sanitized_user_login The submitted username after being sanitized. 2746 * @param string $user_email The submitted email. 2747 * @param WP_Error $errors Contains any errors with submitted username and email, 2748 * e.g., an empty field, an invalid username or email, 2749 * or an existing username or email. 2750 */ 2751 do_action( 'register_post', $sanitized_user_login, $user_email, $errors ); 2752 2753 /** 2754 * Filters the errors encountered when a new user is being registered. 2755 * 2756 * The filtered WP_Error object may, for example, contain errors for an invalid 2757 * or existing username or email address. A WP_Error object should always be returned, 2758 * but may or may not contain errors. 2759 * 2760 * If any errors are present in $errors, this will abort the user's registration. 2761 * 2762 * @since 2.1.0 2763 * 2764 * @param WP_Error $errors A WP_Error object containing any errors encountered 2765 * during registration. 2766 * @param string $sanitized_user_login User's username after it has been sanitized. 2767 * @param string $user_email User's email. 2768 */ 2769 $errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email ); 2770 2771 if ( $errors->has_errors() ) { 2772 return $errors; 2773 } 2774 2775 $user_pass = wp_generate_password( 12, false ); 2776 $user_id = wp_create_user( $sanitized_user_login, $user_pass, $user_email ); 2777 if ( ! $user_id || is_wp_error( $user_id ) ) { 2778 $errors->add( 2779 'registerfail', 2780 sprintf( 2781 /* translators: %s: Admin email address. */ 2782 __( '<strong>Error</strong>: Couldn’t register you… please contact the <a href="mailto:%s">site admin</a>!' ), 2783 get_option( 'admin_email' ) 2784 ) 2785 ); 2786 return $errors; 2787 } 2788 2789 update_user_option( $user_id, 'default_password_nag', true, true ); // Set up the password change nag. 2790 2791 /** 2792 * Fires after a new user registration has been recorded. 2793 * 2794 * @since 4.4.0 2795 * 2796 * @param int $user_id ID of the newly registered user. 2797 */ 2798 do_action( 'register_new_user', $user_id ); 2799 2800 return $user_id; 2801 } 2802 2803 /** 2804 * Initiates email notifications related to the creation of new users. 2805 * 2806 * Notifications are sent both to the site admin and to the newly created user. 2807 * 2808 * @since 4.4.0 2809 * @since 4.6.0 Converted the `$notify` parameter to accept 'user' for sending 2810 * notifications only to the user created. 2811 * 2812 * @param int $user_id ID of the newly created user. 2813 * @param string $notify Optional. Type of notification that should happen. Accepts 'admin' 2814 * or an empty string (admin only), 'user', or 'both' (admin and user). 2815 * Default 'both'. 2816 */ 2817 function wp_send_new_user_notifications( $user_id, $notify = 'both' ) { 2818 wp_new_user_notification( $user_id, null, $notify ); 2819 } 2820 2821 /** 2822 * Retrieve the current session token from the logged_in cookie. 2823 * 2824 * @since 4.0.0 2825 * 2826 * @return string Token. 2827 */ 2828 function wp_get_session_token() { 2829 $cookie = wp_parse_auth_cookie( '', 'logged_in' ); 2830 return ! empty( $cookie['token'] ) ? $cookie['token'] : ''; 2831 } 2832 2833 /** 2834 * Retrieve a list of sessions for the current user. 2835 * 2836 * @since 4.0.0 2837 * 2838 * @return array Array of sessions. 2839 */ 2840 function wp_get_all_sessions() { 2841 $manager = WP_Session_Tokens::get_instance( get_current_user_id() ); 2842 return $manager->get_all(); 2843 } 2844 2845 /** 2846 * Remove the current session token from the database. 2847 * 2848 * @since 4.0.0 2849 */ 2850 function wp_destroy_current_session() { 2851 $token = wp_get_session_token(); 2852 if ( $token ) { 2853 $manager = WP_Session_Tokens::get_instance( get_current_user_id() ); 2854 $manager->destroy( $token ); 2855 } 2856 } 2857 2858 /** 2859 * Remove all but the current session token for the current user for the database. 2860 * 2861 * @since 4.0.0 2862 */ 2863 function wp_destroy_other_sessions() { 2864 $token = wp_get_session_token(); 2865 if ( $token ) { 2866 $manager = WP_Session_Tokens::get_instance( get_current_user_id() ); 2867 $manager->destroy_others( $token ); 2868 } 2869 } 2870 2871 /** 2872 * Remove all session tokens for the current user from the database. 2873 * 2874 * @since 4.0.0 2875 */ 2876 function wp_destroy_all_sessions() { 2877 $manager = WP_Session_Tokens::get_instance( get_current_user_id() ); 2878 $manager->destroy_all(); 2879 } 2880 2881 /** 2882 * Get the user IDs of all users with no role on this site. 2883 * 2884 * @since 4.4.0 2885 * @since 4.9.0 The `$site_id` parameter was added to support multisite. 2886 * 2887 * @param int|null $site_id Optional. The site ID to get users with no role for. Defaults to the current site. 2888 * @return string[] Array of user IDs as strings. 2889 */ 2890 function wp_get_users_with_no_role( $site_id = null ) { 2891 global $wpdb; 2892 2893 if ( ! $site_id ) { 2894 $site_id = get_current_blog_id(); 2895 } 2896 2897 $prefix = $wpdb->get_blog_prefix( $site_id ); 2898 2899 if ( is_multisite() && get_current_blog_id() != $site_id ) { 2900 switch_to_blog( $site_id ); 2901 $role_names = wp_roles()->get_names(); 2902 restore_current_blog(); 2903 } else { 2904 $role_names = wp_roles()->get_names(); 2905 } 2906 2907 $regex = implode( '|', array_keys( $role_names ) ); 2908 $regex = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex ); 2909 $users = $wpdb->get_col( 2910 $wpdb->prepare( 2911 " 2912 SELECT user_id 2913 FROM $wpdb->usermeta 2914 WHERE meta_key = '{$prefix}capabilities' 2915 AND meta_value NOT REGEXP %s 2916 ", 2917 $regex 2918 ) 2919 ); 2920 2921 return $users; 2922 } 2923 2924 /** 2925 * Retrieves the current user object. 2926 * 2927 * Will set the current user, if the current user is not set. The current user 2928 * will be set to the logged-in person. If no user is logged-in, then it will 2929 * set the current user to 0, which is invalid and won't have any permissions. 2930 * 2931 * This function is used by the pluggable functions wp_get_current_user() and 2932 * get_currentuserinfo(), the latter of which is deprecated but used for backward 2933 * compatibility. 2934 * 2935 * @since 4.5.0 2936 * @access private 2937 * 2938 * @see wp_get_current_user() 2939 * @global WP_User $current_user Checks if the current user is set. 2940 * 2941 * @return WP_User Current WP_User instance. 2942 */ 2943 function _wp_get_current_user() { 2944 global $current_user; 2945 2946 if ( ! empty( $current_user ) ) { 2947 if ( $current_user instanceof WP_User ) { 2948 return $current_user; 2949 } 2950 2951 // Upgrade stdClass to WP_User. 2952 if ( is_object( $current_user ) && isset( $current_user->ID ) ) { 2953 $cur_id = $current_user->ID; 2954 $current_user = null; 2955 wp_set_current_user( $cur_id ); 2956 return $current_user; 2957 } 2958 2959 // $current_user has a junk value. Force to WP_User with ID 0. 2960 $current_user = null; 2961 wp_set_current_user( 0 ); 2962 return $current_user; 2963 } 2964 2965 if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) { 2966 wp_set_current_user( 0 ); 2967 return $current_user; 2968 } 2969 2970 /** 2971 * Filters the current user. 2972 * 2973 * The default filters use this to determine the current user from the 2974 * request's cookies, if available. 2975 * 2976 * Returning a value of false will effectively short-circuit setting 2977 * the current user. 2978 * 2979 * @since 3.9.0 2980 * 2981 * @param int|false $user_id User ID if one has been determined, false otherwise. 2982 */ 2983 $user_id = apply_filters( 'determine_current_user', false ); 2984 if ( ! $user_id ) { 2985 wp_set_current_user( 0 ); 2986 return $current_user; 2987 } 2988 2989 wp_set_current_user( $user_id ); 2990 2991 return $current_user; 2992 } 2993 2994 /** 2995 * Send a confirmation request email when a change of user email address is attempted. 2996 * 2997 * @since 3.0.0 2998 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific. 2999 * 3000 * @global WP_Error $errors WP_Error object. 3001 */ 3002 function send_confirmation_on_profile_email() { 3003 global $errors; 3004 3005 $current_user = wp_get_current_user(); 3006 if ( ! is_object( $errors ) ) { 3007 $errors = new WP_Error(); 3008 } 3009 3010 if ( $current_user->ID != $_POST['user_id'] ) { 3011 return false; 3012 } 3013 3014 if ( $current_user->user_email != $_POST['email'] ) { 3015 if ( ! is_email( $_POST['email'] ) ) { 3016 $errors->add( 3017 'user_email', 3018 __( '<strong>Error</strong>: The email address isn’t correct.' ), 3019 array( 3020 'form-field' => 'email', 3021 ) 3022 ); 3023 3024 return; 3025 } 3026 3027 if ( email_exists( $_POST['email'] ) ) { 3028 $errors->add( 3029 'user_email', 3030 __( '<strong>Error</strong>: The email address is already used.' ), 3031 array( 3032 'form-field' => 'email', 3033 ) 3034 ); 3035 delete_user_meta( $current_user->ID, '_new_email' ); 3036 3037 return; 3038 } 3039 3040 $hash = md5( $_POST['email'] . time() . wp_rand() ); 3041 $new_user_email = array( 3042 'hash' => $hash, 3043 'newemail' => $_POST['email'], 3044 ); 3045 update_user_meta( $current_user->ID, '_new_email', $new_user_email ); 3046 3047 $sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 3048 3049 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ 3050 $email_text = __( 3051 'Howdy ###USERNAME###, 3052 3053 You recently requested to have the email address on your account changed. 3054 3055 If this is correct, please click on the following link to change it: 3056 ###ADMIN_URL### 3057 3058 You can safely ignore and delete this email if you do not want to 3059 take this action. 3060 3061 This email has been sent to ###EMAIL### 3062 3063 Regards, 3064 All at ###SITENAME### 3065 ###SITEURL###' 3066 ); 3067 3068 /** 3069 * Filters the text of the email sent when a change of user email address is attempted. 3070 * 3071 * The following strings have a special meaning and will get replaced dynamically: 3072 * - ###USERNAME### The current user's username. 3073 * - ###ADMIN_URL### The link to click on to confirm the email change. 3074 * - ###EMAIL### The new email. 3075 * - ###SITENAME### The name of the site. 3076 * - ###SITEURL### The URL to the site. 3077 * 3078 * @since MU (3.0.0) 3079 * @since 4.9.0 This filter is no longer Multisite specific. 3080 * 3081 * @param string $email_text Text in the email. 3082 * @param array $new_user_email { 3083 * Data relating to the new user email address. 3084 * 3085 * @type string $hash The secure hash used in the confirmation link URL. 3086 * @type string $newemail The proposed new email address. 3087 * } 3088 */ 3089 $content = apply_filters( 'new_user_email_content', $email_text, $new_user_email ); 3090 3091 $content = str_replace( '###USERNAME###', $current_user->user_login, $content ); 3092 $content = str_replace( '###ADMIN_URL###', esc_url( admin_url( 'profile.php?newuseremail=' . $hash ) ), $content ); 3093 $content = str_replace( '###EMAIL###', $_POST['email'], $content ); 3094 $content = str_replace( '###SITENAME###', $sitename, $content ); 3095 $content = str_replace( '###SITEURL###', home_url(), $content ); 3096 3097 /* translators: New email address notification email subject. %s: Site title. */ 3098 wp_mail( $_POST['email'], sprintf( __( '[%s] Email Change Request' ), $sitename ), $content ); 3099 3100 $_POST['email'] = $current_user->user_email; 3101 } 3102 } 3103 3104 /** 3105 * Adds an admin notice alerting the user to check for confirmation request email 3106 * after email address change. 3107 * 3108 * @since 3.0.0 3109 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific. 3110 * 3111 * @global string $pagenow 3112 */ 3113 function new_user_email_admin_notice() { 3114 global $pagenow; 3115 3116 if ( 'profile.php' === $pagenow && isset( $_GET['updated'] ) ) { 3117 $email = get_user_meta( get_current_user_id(), '_new_email', true ); 3118 if ( $email ) { 3119 /* translators: %s: New email address. */ 3120 echo '<div class="notice notice-info"><p>' . sprintf( __( 'Your email address has not been updated yet. Please check your inbox at %s for a confirmation email.' ), '<code>' . esc_html( $email['newemail'] ) . '</code>' ) . '</p></div>'; 3121 } 3122 } 3123 } 3124 3125 /** 3126 * Get all personal data request types. 3127 * 3128 * @since 4.9.6 3129 * @access private 3130 * 3131 * @return array List of core privacy action types. 3132 */ 3133 function _wp_privacy_action_request_types() { 3134 return array( 3135 'export_personal_data', 3136 'remove_personal_data', 3137 ); 3138 } 3139 3140 /** 3141 * Registers the personal data exporter for users. 3142 * 3143 * @since 4.9.6 3144 * 3145 * @param array $exporters An array of personal data exporters. 3146 * @return array An array of personal data exporters. 3147 */ 3148 function wp_register_user_personal_data_exporter( $exporters ) { 3149 $exporters['wordpress-user'] = array( 3150 'exporter_friendly_name' => __( 'WordPress User' ), 3151 'callback' => 'wp_user_personal_data_exporter', 3152 ); 3153 3154 return $exporters; 3155 } 3156 3157 /** 3158 * Finds and exports personal data associated with an email address from the user and user_meta table. 3159 * 3160 * @since 4.9.6 3161 * @since 5.4.0 Added 'Community Events Location' group to the export data. 3162 * @since 5.4.0 Added 'Session Tokens' group to the export data. 3163 * 3164 * @param string $email_address The user's email address. 3165 * @return array An array of personal data. 3166 */ 3167 function wp_user_personal_data_exporter( $email_address ) { 3168 $email_address = trim( $email_address ); 3169 3170 $data_to_export = array(); 3171 3172 $user = get_user_by( 'email', $email_address ); 3173 3174 if ( ! $user ) { 3175 return array( 3176 'data' => array(), 3177 'done' => true, 3178 ); 3179 } 3180 3181 $user_meta = get_user_meta( $user->ID ); 3182 3183 $user_props_to_export = array( 3184 'ID' => __( 'User ID' ), 3185 'user_login' => __( 'User Login Name' ), 3186 'user_nicename' => __( 'User Nice Name' ), 3187 'user_email' => __( 'User Email' ), 3188 'user_url' => __( 'User URL' ), 3189 'user_registered' => __( 'User Registration Date' ), 3190 'display_name' => __( 'User Display Name' ), 3191 'nickname' => __( 'User Nickname' ), 3192 'first_name' => __( 'User First Name' ), 3193 'last_name' => __( 'User Last Name' ), 3194 'description' => __( 'User Description' ), 3195 ); 3196 3197 $user_data_to_export = array(); 3198 3199 foreach ( $user_props_to_export as $key => $name ) { 3200 $value = ''; 3201 3202 switch ( $key ) { 3203 case 'ID': 3204 case 'user_login': 3205 case 'user_nicename': 3206 case 'user_email': 3207 case 'user_url': 3208 case 'user_registered': 3209 case 'display_name': 3210 $value = $user->data->$key; 3211 break; 3212 case 'nickname': 3213 case 'first_name': 3214 case 'last_name': 3215 case 'description': 3216 $value = $user_meta[ $key ][0]; 3217 break; 3218 } 3219 3220 if ( ! empty( $value ) ) { 3221 $user_data_to_export[] = array( 3222 'name' => $name, 3223 'value' => $value, 3224 ); 3225 } 3226 } 3227 3228 // Get the list of reserved names. 3229 $reserved_names = array_values( $user_props_to_export ); 3230 3231 /** 3232 * Filter to extend the user's profile data for the privacy exporter. 3233 * 3234 * @since 5.4.0 3235 * 3236 * @param array $additional_user_profile_data { 3237 * An array of name-value pairs of additional user data items. Default empty array. 3238 * 3239 * @type string $name The user-facing name of an item name-value pair,e.g. 'IP Address'. 3240 * @type string $value The user-facing value of an item data pair, e.g. '50.60.70.0'. 3241 * } 3242 * @param WP_User $user The user whose data is being exported. 3243 * @param string[] $reserved_names An array of reserved names. Any item in `$additional_user_data` 3244 * that uses one of these for its `name` will not be included in the export. 3245 */ 3246 $_extra_data = apply_filters( 'wp_privacy_additional_user_profile_data', array(), $user, $reserved_names ); 3247 3248 if ( is_array( $_extra_data ) && ! empty( $_extra_data ) ) { 3249 // Remove items that use reserved names. 3250 $extra_data = array_filter( 3251 $_extra_data, 3252 function( $item ) use ( $reserved_names ) { 3253 return ! in_array( $item['name'], $reserved_names, true ); 3254 } 3255 ); 3256 3257 if ( count( $extra_data ) !== count( $_extra_data ) ) { 3258 _doing_it_wrong( 3259 __FUNCTION__, 3260 sprintf( 3261 /* translators: %s: wp_privacy_additional_user_profile_data */ 3262 __( 'Filter %s returned items with reserved names.' ), 3263 '<code>wp_privacy_additional_user_profile_data</code>' 3264 ), 3265 '5.4.0' 3266 ); 3267 } 3268 3269 if ( ! empty( $extra_data ) ) { 3270 $user_data_to_export = array_merge( $user_data_to_export, $extra_data ); 3271 } 3272 } 3273 3274 $data_to_export[] = array( 3275 'group_id' => 'user', 3276 'group_label' => __( 'User' ), 3277 'group_description' => __( 'User’s profile data.' ), 3278 'item_id' => "user-{$user->ID}", 3279 'data' => $user_data_to_export, 3280 ); 3281 3282 if ( isset( $user_meta['community-events-location'] ) ) { 3283 $location = maybe_unserialize( $user_meta['community-events-location'][0] ); 3284 3285 $location_props_to_export = array( 3286 'description' => __( 'City' ), 3287 'country' => __( 'Country' ), 3288 'latitude' => __( 'Latitude' ), 3289 'longitude' => __( 'Longitude' ), 3290 'ip' => __( 'IP' ), 3291 ); 3292 3293 $location_data_to_export = array(); 3294 3295 foreach ( $location_props_to_export as $key => $name ) { 3296 if ( ! empty( $location[ $key ] ) ) { 3297 $location_data_to_export[] = array( 3298 'name' => $name, 3299 'value' => $location[ $key ], 3300 ); 3301 } 3302 } 3303 3304 $data_to_export[] = array( 3305 'group_id' => 'community-events-location', 3306 'group_label' => __( 'Community Events Location' ), 3307 'group_description' => __( 'User’s location data used for the Community Events in the WordPress Events and News dashboard widget.' ), 3308 'item_id' => "community-events-location-{$user->ID}", 3309 'data' => $location_data_to_export, 3310 ); 3311 } 3312 3313 if ( isset( $user_meta['session_tokens'] ) ) { 3314 $session_tokens = maybe_unserialize( $user_meta['session_tokens'][0] ); 3315 3316 $session_tokens_props_to_export = array( 3317 'expiration' => __( 'Expiration' ), 3318 'ip' => __( 'IP' ), 3319 'ua' => __( 'User Agent' ), 3320 'login' => __( 'Last Login' ), 3321 ); 3322 3323 foreach ( $session_tokens as $token_key => $session_token ) { 3324 $session_tokens_data_to_export = array(); 3325 3326 foreach ( $session_tokens_props_to_export as $key => $name ) { 3327 if ( ! empty( $session_token[ $key ] ) ) { 3328 $value = $session_token[ $key ]; 3329 if ( in_array( $key, array( 'expiration', 'login' ), true ) ) { 3330 $value = date_i18n( 'F d, Y H:i A', $value ); 3331 } 3332 $session_tokens_data_to_export[] = array( 3333 'name' => $name, 3334 'value' => $value, 3335 ); 3336 } 3337 } 3338 3339 $data_to_export[] = array( 3340 'group_id' => 'session-tokens', 3341 'group_label' => __( 'Session Tokens' ), 3342 'group_description' => __( 'User’s Session Tokens data.' ), 3343 'item_id' => "session-tokens-{$user->ID}-{$token_key}", 3344 'data' => $session_tokens_data_to_export, 3345 ); 3346 } 3347 } 3348 3349 return array( 3350 'data' => $data_to_export, 3351 'done' => true, 3352 ); 3353 } 3354 3355 /** 3356 * Update log when privacy request is confirmed. 3357 * 3358 * @since 4.9.6 3359 * @access private 3360 * 3361 * @param int $request_id ID of the request. 3362 */ 3363 function _wp_privacy_account_request_confirmed( $request_id ) { 3364 $request = wp_get_user_request( $request_id ); 3365 3366 if ( ! $request ) { 3367 return; 3368 } 3369 3370 if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) { 3371 return; 3372 } 3373 3374 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() ); 3375 wp_update_post( 3376 array( 3377 'ID' => $request_id, 3378 'post_status' => 'request-confirmed', 3379 ) 3380 ); 3381 } 3382 3383 /** 3384 * Notify the site administrator via email when a request is confirmed. 3385 * 3386 * Without this, the admin would have to manually check the site to see if any 3387 * action was needed on their part yet. 3388 * 3389 * @since 4.9.6 3390 * 3391 * @param int $request_id The ID of the request. 3392 */ 3393 function _wp_privacy_send_request_confirmation_notification( $request_id ) { 3394 $request = wp_get_user_request( $request_id ); 3395 3396 if ( ! is_a( $request, 'WP_User_Request' ) || 'request-confirmed' !== $request->status ) { 3397 return; 3398 } 3399 3400 $already_notified = (bool) get_post_meta( $request_id, '_wp_admin_notified', true ); 3401 3402 if ( $already_notified ) { 3403 return; 3404 } 3405 3406 if ( 'export_personal_data' === $request->action_name ) { 3407 $manage_url = admin_url( 'export-personal-data.php' ); 3408 } elseif ( 'remove_personal_data' === $request->action_name ) { 3409 $manage_url = admin_url( 'erase-personal-data.php' ); 3410 } 3411 $action_description = wp_user_request_action_description( $request->action_name ); 3412 3413 /** 3414 * Filters the recipient of the data request confirmation notification. 3415 * 3416 * In a Multisite environment, this will default to the email address of the 3417 * network admin because, by default, single site admins do not have the 3418 * capabilities required to process requests. Some networks may wish to 3419 * delegate those capabilities to a single-site admin, or a dedicated person 3420 * responsible for managing privacy requests. 3421 * 3422 * @since 4.9.6 3423 * 3424 * @param string $admin_email The email address of the notification recipient. 3425 * @param WP_User_Request $request The request that is initiating the notification. 3426 */ 3427 $admin_email = apply_filters( 'user_request_confirmed_email_to', get_site_option( 'admin_email' ), $request ); 3428 3429 $email_data = array( 3430 'request' => $request, 3431 'user_email' => $request->email, 3432 'description' => $action_description, 3433 'manage_url' => $manage_url, 3434 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), 3435 'siteurl' => home_url(), 3436 'admin_email' => $admin_email, 3437 ); 3438 3439 /* translators: Do not translate SITENAME, USER_EMAIL, DESCRIPTION, MANAGE_URL, SITEURL; those are placeholders. */ 3440 $email_text = __( 3441 'Howdy, 3442 3443 A user data privacy request has been confirmed on ###SITENAME###: 3444 3445 User: ###USER_EMAIL### 3446 Request: ###DESCRIPTION### 3447 3448 You can view and manage these data privacy requests here: 3449 3450 ###MANAGE_URL### 3451 3452 Regards, 3453 All at ###SITENAME### 3454 ###SITEURL###' 3455 ); 3456 3457 /** 3458 * Filters the body of the user request confirmation email. 3459 * 3460 * The email is sent to an administrator when an user request is confirmed. 3461 * The following strings have a special meaning and will get replaced dynamically: 3462 * 3463 * ###SITENAME### The name of the site. 3464 * ###USER_EMAIL### The user email for the request. 3465 * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for. 3466 * ###MANAGE_URL### The URL to manage requests. 3467 * ###SITEURL### The URL to the site. 3468 * 3469 * @since 4.9.6 3470 * 3471 * @param string $email_text Text in the email. 3472 * @param array $email_data { 3473 * Data relating to the account action email. 3474 * 3475 * @type WP_User_Request $request User request object. 3476 * @type string $user_email The email address confirming a request 3477 * @type string $description Description of the action being performed so the user knows what the email is for. 3478 * @type string $manage_url The link to click manage privacy requests of this type. 3479 * @type string $sitename The site name sending the mail. 3480 * @type string $siteurl The site URL sending the mail. 3481 * @type string $admin_email The administrator email receiving the mail. 3482 * } 3483 */ 3484 $content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data ); 3485 3486 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content ); 3487 $content = str_replace( '###USER_EMAIL###', $email_data['user_email'], $content ); 3488 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); 3489 $content = str_replace( '###MANAGE_URL###', esc_url_raw( $email_data['manage_url'] ), $content ); 3490 $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content ); 3491 3492 $subject = sprintf( 3493 /* translators: Privacy data request confirmed notification email subject. 1: Site title, 2: Name of the confirmed action. */ 3494 __( '[%1$s] Action Confirmed: %2$s' ), 3495 $email_data['sitename'], 3496 $action_description 3497 ); 3498 3499 /** 3500 * Filters the subject of the user request confirmation email. 3501 * 3502 * @since 4.9.8 3503 * 3504 * @param string $subject The email subject. 3505 * @param string $sitename The name of the site. 3506 * @param array $email_data { 3507 * Data relating to the account action email. 3508 * 3509 * @type WP_User_Request $request User request object. 3510 * @type string $user_email The email address confirming a request 3511 * @type string $description Description of the action being performed so the user knows what the email is for. 3512 * @type string $manage_url The link to click manage privacy requests of this type. 3513 * @type string $sitename The site name sending the mail. 3514 * @type string $siteurl The site URL sending the mail. 3515 * @type string $admin_email The administrator email receiving the mail. 3516 * } 3517 */ 3518 $subject = apply_filters( 'user_request_confirmed_email_subject', $subject, $email_data['sitename'], $email_data ); 3519 3520 $headers = ''; 3521 3522 /** 3523 * Filters the headers of the user request confirmation email. 3524 * 3525 * @since 5.4.0 3526 * 3527 * @param string|array $headers The email headers. 3528 * @param string $subject The email subject. 3529 * @param string $content The email content. 3530 * @param int $request_id The request ID. 3531 * @param array $email_data { 3532 * Data relating to the account action email. 3533 * 3534 * @type WP_User_Request $request User request object. 3535 * @type string $user_email The email address confirming a request 3536 * @type string $description Description of the action being performed so the user knows what the email is for. 3537 * @type string $manage_url The link to click manage privacy requests of this type. 3538 * @type string $sitename The site name sending the mail. 3539 * @type string $siteurl The site URL sending the mail. 3540 * @type string $admin_email The administrator email receiving the mail. 3541 * } 3542 */ 3543 $headers = apply_filters( 'user_request_confirmed_email_headers', $headers, $subject, $content, $request_id, $email_data ); 3544 3545 $email_sent = wp_mail( $email_data['admin_email'], $subject, $content, $headers ); 3546 3547 if ( $email_sent ) { 3548 update_post_meta( $request_id, '_wp_admin_notified', true ); 3549 } 3550 } 3551 3552 /** 3553 * Notify the user when their erasure request is fulfilled. 3554 * 3555 * Without this, the user would never know if their data was actually erased. 3556 * 3557 * @since 4.9.6 3558 * 3559 * @param int $request_id The privacy request post ID associated with this request. 3560 */ 3561 function _wp_privacy_send_erasure_fulfillment_notification( $request_id ) { 3562 $request = wp_get_user_request( $request_id ); 3563 3564 if ( ! is_a( $request, 'WP_User_Request' ) || 'request-completed' !== $request->status ) { 3565 return; 3566 } 3567 3568 $already_notified = (bool) get_post_meta( $request_id, '_wp_user_notified', true ); 3569 3570 if ( $already_notified ) { 3571 return; 3572 } 3573 3574 // Localize message content for user; fallback to site default for visitors. 3575 if ( ! empty( $request->user_id ) ) { 3576 $locale = get_user_locale( $request->user_id ); 3577 } else { 3578 $locale = get_locale(); 3579 } 3580 3581 $switched_locale = switch_to_locale( $locale ); 3582 3583 /** 3584 * Filters the recipient of the data erasure fulfillment notification. 3585 * 3586 * @since 4.9.6 3587 * 3588 * @param string $user_email The email address of the notification recipient. 3589 * @param WP_User_Request $request The request that is initiating the notification. 3590 */ 3591 $user_email = apply_filters( 'user_erasure_fulfillment_email_to', $request->email, $request ); 3592 3593 $email_data = array( 3594 'request' => $request, 3595 'message_recipient' => $user_email, 3596 'privacy_policy_url' => get_privacy_policy_url(), 3597 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), 3598 'siteurl' => home_url(), 3599 ); 3600 3601 $subject = sprintf( 3602 /* translators: Erasure request fulfilled notification email subject. %s: Site title. */ 3603 __( '[%s] Erasure Request Fulfilled' ), 3604 $email_data['sitename'] 3605 ); 3606 3607 /** 3608 * Filters the subject of the email sent when an erasure request is completed. 3609 * 3610 * @since 4.9.8 3611 * 3612 * @param string $subject The email subject. 3613 * @param string $sitename The name of the site. 3614 * @param array $email_data { 3615 * Data relating to the account action email. 3616 * 3617 * @type WP_User_Request $request User request object. 3618 * @type string $message_recipient The address that the email will be sent to. Defaults 3619 * to the value of `$request->email`, but can be changed 3620 * by the `user_erasure_fulfillment_email_to` filter. 3621 * @type string $privacy_policy_url Privacy policy URL. 3622 * @type string $sitename The site name sending the mail. 3623 * @type string $siteurl The site URL sending the mail. 3624 * } 3625 */ 3626 $subject = apply_filters( 'user_erasure_complete_email_subject', $subject, $email_data['sitename'], $email_data ); 3627 3628 if ( empty( $email_data['privacy_policy_url'] ) ) { 3629 /* translators: Do not translate SITENAME, SITEURL; those are placeholders. */ 3630 $email_text = __( 3631 'Howdy, 3632 3633 Your request to erase your personal data on ###SITENAME### has been completed. 3634 3635 If you have any follow-up questions or concerns, please contact the site administrator. 3636 3637 Regards, 3638 All at ###SITENAME### 3639 ###SITEURL###' 3640 ); 3641 } else { 3642 /* translators: Do not translate SITENAME, SITEURL, PRIVACY_POLICY_URL; those are placeholders. */ 3643 $email_text = __( 3644 'Howdy, 3645 3646 Your request to erase your personal data on ###SITENAME### has been completed. 3647 3648 If you have any follow-up questions or concerns, please contact the site administrator. 3649 3650 For more information, you can also read our privacy policy: ###PRIVACY_POLICY_URL### 3651 3652 Regards, 3653 All at ###SITENAME### 3654 ###SITEURL###' 3655 ); 3656 } 3657 3658 /** 3659 * Filters the body of the data erasure fulfillment notification. 3660 * 3661 * The email is sent to a user when a their data erasure request is fulfilled 3662 * by an administrator. 3663 * 3664 * The following strings have a special meaning and will get replaced dynamically: 3665 * 3666 * ###SITENAME### The name of the site. 3667 * ###PRIVACY_POLICY_URL### Privacy policy page URL. 3668 * ###SITEURL### The URL to the site. 3669 * 3670 * @since 4.9.6 3671 * 3672 * @param string $email_text Text in the email. 3673 * @param array $email_data { 3674 * Data relating to the account action email. 3675 * 3676 * @type WP_User_Request $request User request object. 3677 * @type string $message_recipient The address that the email will be sent to. Defaults 3678 * to the value of `$request->email`, but can be changed 3679 * by the `user_erasure_fulfillment_email_to` filter. 3680 * @type string $privacy_policy_url Privacy policy URL. 3681 * @type string $sitename The site name sending the mail. 3682 * @type string $siteurl The site URL sending the mail. 3683 * } 3684 */ 3685 $content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data ); 3686 3687 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content ); 3688 $content = str_replace( '###PRIVACY_POLICY_URL###', $email_data['privacy_policy_url'], $content ); 3689 $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content ); 3690 3691 $headers = ''; 3692 3693 /** 3694 * Filters the headers of the data erasure fulfillment notification. 3695 * 3696 * @since 5.4.0 3697 * 3698 * @param string|array $headers The email headers. 3699 * @param string $subject The email subject. 3700 * @param string $content The email content. 3701 * @param int $request_id The request ID. 3702 * @param array $email_data { 3703 * Data relating to the account action email. 3704 * 3705 * @type WP_User_Request $request User request object. 3706 * @type string $message_recipient The address that the email will be sent to. Defaults 3707 * to the value of `$request->email`, but can be changed 3708 * by the `user_erasure_fulfillment_email_to` filter. 3709 * @type string $privacy_policy_url Privacy policy URL. 3710 * @type string $sitename The site name sending the mail. 3711 * @type string $siteurl The site URL sending the mail. 3712 * } 3713 */ 3714 $headers = apply_filters( 'user_erasure_complete_email_headers', $headers, $subject, $content, $request_id, $email_data ); 3715 3716 $email_sent = wp_mail( $user_email, $subject, $content, $headers ); 3717 3718 if ( $switched_locale ) { 3719 restore_previous_locale(); 3720 } 3721 3722 if ( $email_sent ) { 3723 update_post_meta( $request_id, '_wp_user_notified', true ); 3724 } 3725 } 3726 3727 /** 3728 * Return request confirmation message HTML. 3729 * 3730 * @since 4.9.6 3731 * @access private 3732 * 3733 * @param int $request_id The request ID being confirmed. 3734 * @return string The confirmation message. 3735 */ 3736 function _wp_privacy_account_request_confirmed_message( $request_id ) { 3737 $request = wp_get_user_request( $request_id ); 3738 3739 $message = '<p class="success">' . __( 'Action has been confirmed.' ) . '</p>'; 3740 $message .= '<p>' . __( 'The site administrator has been notified and will fulfill your request as soon as possible.' ) . '</p>'; 3741 3742 if ( $request && in_array( $request->action_name, _wp_privacy_action_request_types(), true ) ) { 3743 if ( 'export_personal_data' === $request->action_name ) { 3744 $message = '<p class="success">' . __( 'Thanks for confirming your export request.' ) . '</p>'; 3745 $message .= '<p>' . __( 'The site administrator has been notified. You will receive a link to download your export via email when they fulfill your request.' ) . '</p>'; 3746 } elseif ( 'remove_personal_data' === $request->action_name ) { 3747 $message = '<p class="success">' . __( 'Thanks for confirming your erasure request.' ) . '</p>'; 3748 $message .= '<p>' . __( 'The site administrator has been notified. You will receive an email confirmation when they erase your data.' ) . '</p>'; 3749 } 3750 } 3751 3752 /** 3753 * Filters the message displayed to a user when they confirm a data request. 3754 * 3755 * @since 4.9.6 3756 * 3757 * @param string $message The message to the user. 3758 * @param int $request_id The ID of the request being confirmed. 3759 */ 3760 $message = apply_filters( 'user_request_action_confirmed_message', $message, $request_id ); 3761 3762 return $message; 3763 } 3764 3765 /** 3766 * Create and log a user request to perform a specific action. 3767 * 3768 * Requests are stored inside a post type named `user_request` since they can apply to both 3769 * users on the site, or guests without a user account. 3770 * 3771 * @since 4.9.6 3772 * 3773 * @param string $email_address User email address. This can be the address of a registered or non-registered user. 3774 * @param string $action_name Name of the action that is being confirmed. Required. 3775 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 3776 * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure. 3777 */ 3778 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) { 3779 $email_address = sanitize_email( $email_address ); 3780 $action_name = sanitize_key( $action_name ); 3781 3782 if ( ! is_email( $email_address ) ) { 3783 return new WP_Error( 'invalid_email', __( 'Invalid email address.' ) ); 3784 } 3785 3786 if ( ! in_array( $action_name, _wp_privacy_action_request_types(), true ) ) { 3787 return new WP_Error( 'invalid_action', __( 'Invalid action name.' ) ); 3788 } 3789 3790 $user = get_user_by( 'email', $email_address ); 3791 $user_id = $user && ! is_wp_error( $user ) ? $user->ID : 0; 3792 3793 // Check for duplicates. 3794 $requests_query = new WP_Query( 3795 array( 3796 'post_type' => 'user_request', 3797 'post_name__in' => array( $action_name ), // Action name stored in post_name column. 3798 'title' => $email_address, // Email address stored in post_title column. 3799 'post_status' => array( 3800 'request-pending', 3801 'request-confirmed', 3802 ), 3803 'fields' => 'ids', 3804 ) 3805 ); 3806 3807 if ( $requests_query->found_posts ) { 3808 return new WP_Error( 'duplicate_request', __( 'An incomplete personal data request for this email address already exists.' ) ); 3809 } 3810 3811 $request_id = wp_insert_post( 3812 array( 3813 'post_author' => $user_id, 3814 'post_name' => $action_name, 3815 'post_title' => $email_address, 3816 'post_content' => wp_json_encode( $request_data ), 3817 'post_status' => 'request-pending', 3818 'post_type' => 'user_request', 3819 'post_date' => current_time( 'mysql', false ), 3820 'post_date_gmt' => current_time( 'mysql', true ), 3821 ), 3822 true 3823 ); 3824 3825 return $request_id; 3826 } 3827 3828 /** 3829 * Get action description from the name and return a string. 3830 * 3831 * @since 4.9.6 3832 * 3833 * @param string $action_name Action name of the request. 3834 * @return string Human readable action name. 3835 */ 3836 function wp_user_request_action_description( $action_name ) { 3837 switch ( $action_name ) { 3838 case 'export_personal_data': 3839 $description = __( 'Export Personal Data' ); 3840 break; 3841 case 'remove_personal_data': 3842 $description = __( 'Erase Personal Data' ); 3843 break; 3844 default: 3845 /* translators: %s: Action name. */ 3846 $description = sprintf( __( 'Confirm the "%s" action' ), $action_name ); 3847 break; 3848 } 3849 3850 /** 3851 * Filters the user action description. 3852 * 3853 * @since 4.9.6 3854 * 3855 * @param string $description The default description. 3856 * @param string $action_name The name of the request. 3857 */ 3858 return apply_filters( 'user_request_action_description', $description, $action_name ); 3859 } 3860 3861 /** 3862 * Send a confirmation request email to confirm an action. 3863 * 3864 * If the request is not already pending, it will be updated. 3865 * 3866 * @since 4.9.6 3867 * 3868 * @param string $request_id ID of the request created via wp_create_user_request(). 3869 * @return true|WP_Error True on success, `WP_Error` on failure. 3870 */ 3871 function wp_send_user_request( $request_id ) { 3872 $request_id = absint( $request_id ); 3873 $request = wp_get_user_request( $request_id ); 3874 3875 if ( ! $request ) { 3876 return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) ); 3877 } 3878 3879 // Localize message content for user; fallback to site default for visitors. 3880 if ( ! empty( $request->user_id ) ) { 3881 $locale = get_user_locale( $request->user_id ); 3882 } else { 3883 $locale = get_locale(); 3884 } 3885 3886 $switched_locale = switch_to_locale( $locale ); 3887 3888 $email_data = array( 3889 'request' => $request, 3890 'email' => $request->email, 3891 'description' => wp_user_request_action_description( $request->action_name ), 3892 'confirm_url' => add_query_arg( 3893 array( 3894 'action' => 'confirmaction', 3895 'request_id' => $request_id, 3896 'confirm_key' => wp_generate_user_request_key( $request_id ), 3897 ), 3898 wp_login_url() 3899 ), 3900 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), 3901 'siteurl' => home_url(), 3902 ); 3903 3904 /* translators: Do not translate DESCRIPTION, CONFIRM_URL, SITENAME, SITEURL: those are placeholders. */ 3905 $email_text = __( 3906 'Howdy, 3907 3908 A request has been made to perform the following action on your account: 3909 3910 ###DESCRIPTION### 3911 3912 To confirm this, please click on the following link: 3913 ###CONFIRM_URL### 3914 3915 You can safely ignore and delete this email if you do not want to 3916 take this action. 3917 3918 Regards, 3919 All at ###SITENAME### 3920 ###SITEURL###' 3921 ); 3922 3923 /** 3924 * Filters the text of the email sent when an account action is attempted. 3925 * 3926 * The following strings have a special meaning and will get replaced dynamically: 3927 * 3928 * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for. 3929 * ###CONFIRM_URL### The link to click on to confirm the account action. 3930 * ###SITENAME### The name of the site. 3931 * ###SITEURL### The URL to the site. 3932 * 3933 * @since 4.9.6 3934 * 3935 * @param string $email_text Text in the email. 3936 * @param array $email_data { 3937 * Data relating to the account action email. 3938 * 3939 * @type WP_User_Request $request User request object. 3940 * @type string $email The email address this is being sent to. 3941 * @type string $description Description of the action being performed so the user knows what the email is for. 3942 * @type string $confirm_url The link to click on to confirm the account action. 3943 * @type string $sitename The site name sending the mail. 3944 * @type string $siteurl The site URL sending the mail. 3945 * } 3946 */ 3947 $content = apply_filters( 'user_request_action_email_content', $email_text, $email_data ); 3948 3949 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); 3950 $content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content ); 3951 $content = str_replace( '###EMAIL###', $email_data['email'], $content ); 3952 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content ); 3953 $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content ); 3954 3955 /* translators: Confirm privacy data request notification email subject. 1: Site title, 2: Name of the action. */ 3956 $subject = sprintf( __( '[%1$s] Confirm Action: %2$s' ), $email_data['sitename'], $email_data['description'] ); 3957 3958 /** 3959 * Filters the subject of the email sent when an account action is attempted. 3960 * 3961 * @since 4.9.6 3962 * 3963 * @param string $subject The email subject. 3964 * @param string $sitename The name of the site. 3965 * @param array $email_data { 3966 * Data relating to the account action email. 3967 * 3968 * @type WP_User_Request $request User request object. 3969 * @type string $email The email address this is being sent to. 3970 * @type string $description Description of the action being performed so the user knows what the email is for. 3971 * @type string $confirm_url The link to click on to confirm the account action. 3972 * @type string $sitename The site name sending the mail. 3973 * @type string $siteurl The site URL sending the mail. 3974 * } 3975 */ 3976 $subject = apply_filters( 'user_request_action_email_subject', $subject, $email_data['sitename'], $email_data ); 3977 3978 $headers = ''; 3979 3980 /** 3981 * Filters the headers of the email sent when an account action is attempted. 3982 * 3983 * @since 5.4.0 3984 * 3985 * @param string|array $headers The email headers. 3986 * @param string $subject The email subject. 3987 * @param string $content The email content. 3988 * @param int $request_id The request ID. 3989 * @param array $email_data { 3990 * Data relating to the account action email. 3991 * 3992 * @type WP_User_Request $request User request object. 3993 * @type string $email The email address this is being sent to. 3994 * @type string $description Description of the action being performed so the user knows what the email is for. 3995 * @type string $confirm_url The link to click on to confirm the account action. 3996 * @type string $sitename The site name sending the mail. 3997 * @type string $siteurl The site URL sending the mail. 3998 * } 3999 */ 4000 $headers = apply_filters( 'user_request_action_email_headers', $headers, $subject, $content, $request_id, $email_data ); 4001 4002 $email_sent = wp_mail( $email_data['email'], $subject, $content, $headers ); 4003 4004 if ( $switched_locale ) { 4005 restore_previous_locale(); 4006 } 4007 4008 if ( ! $email_sent ) { 4009 return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export confirmation email.' ) ); 4010 } 4011 4012 return true; 4013 } 4014 4015 /** 4016 * Returns a confirmation key for a user action and stores the hashed version for future comparison. 4017 * 4018 * @since 4.9.6 4019 * 4020 * @param int $request_id Request ID. 4021 * @return string Confirmation key. 4022 */ 4023 function wp_generate_user_request_key( $request_id ) { 4024 global $wp_hasher; 4025 4026 // Generate something random for a confirmation key. 4027 $key = wp_generate_password( 20, false ); 4028 4029 // Return the key, hashed. 4030 if ( empty( $wp_hasher ) ) { 4031 require_once ABSPATH . WPINC . '/class-phpass.php'; 4032 $wp_hasher = new PasswordHash( 8, true ); 4033 } 4034 4035 wp_update_post( 4036 array( 4037 'ID' => $request_id, 4038 'post_status' => 'request-pending', 4039 'post_password' => $wp_hasher->HashPassword( $key ), 4040 ) 4041 ); 4042 4043 return $key; 4044 } 4045 4046 /** 4047 * Validate a user request by comparing the key with the request's key. 4048 * 4049 * @since 4.9.6 4050 * 4051 * @param string $request_id ID of the request being confirmed. 4052 * @param string $key Provided key to validate. 4053 * @return true|WP_Error True on success, WP_Error on failure. 4054 */ 4055 function wp_validate_user_request_key( $request_id, $key ) { 4056 global $wp_hasher; 4057 4058 $request_id = absint( $request_id ); 4059 $request = wp_get_user_request( $request_id ); 4060 $saved_key = $request->confirm_key; 4061 $key_request_time = $request->modified_timestamp; 4062 4063 if ( ! $request || ! $saved_key || ! $key_request_time ) { 4064 return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) ); 4065 } 4066 4067 if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) { 4068 return new WP_Error( 'expired_request', __( 'This personal data request has expired.' ) ); 4069 } 4070 4071 if ( empty( $key ) ) { 4072 return new WP_Error( 'missing_key', __( 'The confirmation key is missing from this personal data request.' ) ); 4073 } 4074 4075 if ( empty( $wp_hasher ) ) { 4076 require_once ABSPATH . WPINC . '/class-phpass.php'; 4077 $wp_hasher = new PasswordHash( 8, true ); 4078 } 4079 4080 /** 4081 * Filters the expiration time of confirm keys. 4082 * 4083 * @since 4.9.6 4084 * 4085 * @param int $expiration The expiration time in seconds. 4086 */ 4087 $expiration_duration = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS ); 4088 $expiration_time = $key_request_time + $expiration_duration; 4089 4090 if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) { 4091 return new WP_Error( 'invalid_key', __( 'The confirmation key is invalid for this personal data request.' ) ); 4092 } 4093 4094 if ( ! $expiration_time || time() > $expiration_time ) { 4095 return new WP_Error( 'expired_key', __( 'The confirmation key has expired for this personal data request.' ) ); 4096 } 4097 4098 return true; 4099 } 4100 4101 /** 4102 * Return the user request object for the specified request ID. 4103 * 4104 * @since 4.9.6 4105 * 4106 * @param int $request_id The ID of the user request. 4107 * @return WP_User_Request|false 4108 */ 4109 function wp_get_user_request( $request_id ) { 4110 $request_id = absint( $request_id ); 4111 $post = get_post( $request_id ); 4112 4113 if ( ! $post || 'user_request' !== $post->post_type ) { 4114 return false; 4115 } 4116 4117 return new WP_User_Request( $post ); 4118 } 4119 4120 /** 4121 * Checks if Application Passwords is globally available. 4122 * 4123 * By default, Application Passwords is available to all sites using SSL or to local environments. 4124 * Use {@see 'wp_is_application_passwords_available'} to adjust its availability. 4125 * 4126 * @since 5.6.0 4127 * 4128 * @return bool 4129 */ 4130 function wp_is_application_passwords_available() { 4131 $available = is_ssl() || 'local' === wp_get_environment_type(); 4132 4133 /** 4134 * Filters whether Application Passwords is available. 4135 * 4136 * @since 5.6.0 4137 * 4138 * @param bool $available True if available, false otherwise. 4139 */ 4140 return apply_filters( 'wp_is_application_passwords_available', $available ); 4141 } 4142 4143 /** 4144 * Checks if Application Passwords is available for a specific user. 4145 * 4146 * By default all users can use Application Passwords. Use {@see 'wp_is_application_passwords_available_for_user'} 4147 * to restrict availability to certain users. 4148 * 4149 * @since 5.6.0 4150 * 4151 * @param int|WP_User $user The user to check. 4152 * @return bool 4153 */ 4154 function wp_is_application_passwords_available_for_user( $user ) { 4155 if ( ! wp_is_application_passwords_available() ) { 4156 return false; 4157 } 4158 4159 if ( ! is_object( $user ) ) { 4160 $user = get_userdata( $user ); 4161 } 4162 4163 if ( ! $user || ! $user->exists() ) { 4164 return false; 4165 } 4166 4167 /** 4168 * Filters whether Application Passwords is available for a specific user. 4169 * 4170 * @since 5.6.0 4171 * 4172 * @param bool $available True if available, false otherwise. 4173 * @param WP_User $user The user to check. 4174 */ 4175 return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user ); 4176 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Jan 23 01:00:05 2021 | Cross-referenced by PHPXref 0.7.1 |