wp/wp-includes/user.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * WordPress User API
     3  * Core User API
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Users
     6  * @subpackage Users
     7  */
     7  */
     8 
     8 
     9 /**
     9 /**
    10  * Authenticate user with remember capability.
    10  * Authenticates and logs a user in with 'remember' capability.
    11  *
    11  *
    12  * The credentials is an array that has 'user_login', 'user_password', and
    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
    13  * 'remember' indices. If the credentials is not given, then the log in form
    14  * will be assumed and used if set.
    14  * will be assumed and used if set.
    15  *
    15  *
    16  * The various authentication cookies will be set by this function and will be
    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
    17  * set for a longer period depending on if the 'remember' credential is set to
    18  * true.
    18  * true.
    19  *
    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  *
    20  * @since 2.5.0
    25  * @since 2.5.0
       
    26  *
       
    27  * @global string $auth_secure_cookie
    21  *
    28  *
    22  * @param array       $credentials   Optional. User info in order to sign on.
    29  * @param array       $credentials   Optional. User info in order to sign on.
    23  * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
    30  * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
    24  * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
    31  * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
    25  */
    32  */
    26 function wp_signon( $credentials = array(), $secure_cookie = '' ) {
    33 function wp_signon( $credentials = array(), $secure_cookie = '' ) {
    27 	if ( empty($credentials) ) {
    34 	if ( empty($credentials) ) {
       
    35 		$credentials = array(); // Back-compat for plugins passing an empty string.
       
    36 
    28 		if ( ! empty($_POST['log']) )
    37 		if ( ! empty($_POST['log']) )
    29 			$credentials['user_login'] = $_POST['log'];
    38 			$credentials['user_login'] = $_POST['log'];
    30 		if ( ! empty($_POST['pwd']) )
    39 		if ( ! empty($_POST['pwd']) )
    31 			$credentials['user_password'] = $_POST['pwd'];
    40 			$credentials['user_password'] = $_POST['pwd'];
    32 		if ( ! empty($_POST['rememberme']) )
    41 		if ( ! empty($_POST['rememberme']) )
    46 	 *
    55 	 *
    47 	 * @since 1.5.1
    56 	 * @since 1.5.1
    48 	 *
    57 	 *
    49 	 * @todo Decide whether to deprecate the wp_authenticate action.
    58 	 * @todo Decide whether to deprecate the wp_authenticate action.
    50 	 *
    59 	 *
    51 	 * @param string $user_login    Username, passed by reference.
    60 	 * @param string $user_login    Username (passed by reference).
    52 	 * @param string $user_password User password, passed by reference.
    61 	 * @param string $user_password User password (passed by reference).
    53 	 */
    62 	 */
    54 	do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
    63 	do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
    55 
    64 
    56 	if ( '' === $secure_cookie )
    65 	if ( '' === $secure_cookie )
    57 		$secure_cookie = is_ssl();
    66 		$secure_cookie = is_ssl();
    58 
    67 
    59 	/**
    68 	/**
    60 	 * Filter whether to use a secure sign-on cookie.
    69 	 * Filters whether to use a secure sign-on cookie.
    61 	 *
    70 	 *
    62 	 * @since 3.1.0
    71 	 * @since 3.1.0
    63 	 *
    72 	 *
    64 	 * @param bool  $secure_cookie Whether to use a secure sign-on cookie.
    73 	 * @param bool  $secure_cookie Whether to use a secure sign-on cookie.
    65 	 * @param array $credentials {
    74 	 * @param array $credentials {
   100 	do_action( 'wp_login', $user->user_login, $user );
   109 	do_action( 'wp_login', $user->user_login, $user );
   101 	return $user;
   110 	return $user;
   102 }
   111 }
   103 
   112 
   104 /**
   113 /**
   105  * Authenticate the user using the username and password.
   114  * Authenticate a user, confirming the username and password are valid.
   106  *
   115  *
   107  * @since 2.8.0
   116  * @since 2.8.0
   108  *
   117  *
   109  * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
   118  * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
   110  * @param string                $username Username for authentication.
   119  * @param string                $username Username for authentication.
   131 		return $error;
   140 		return $error;
   132 	}
   141 	}
   133 
   142 
   134 	$user = get_user_by('login', $username);
   143 	$user = get_user_by('login', $username);
   135 
   144 
   136 	if ( !$user )
   145 	if ( !$user ) {
   137 		return new WP_Error( 'invalid_username', sprintf( __( '<strong>ERROR</strong>: Invalid username. <a href="%s">Lost your password?</a>' ), wp_lostpassword_url() ) );
   146 		return new WP_Error( 'invalid_username',
   138 
   147 			__( '<strong>ERROR</strong>: Invalid username.' ) .
   139 	/**
   148 			' <a href="' . wp_lostpassword_url() . '">' .
   140 	 * Filter whether the given user can be authenticated with the provided $password.
   149 			__( 'Lost your password?' ) .
       
   150 			'</a>'
       
   151 		);
       
   152 	}
       
   153 
       
   154 	/**
       
   155 	 * Filters whether the given user can be authenticated with the provided $password.
   141 	 *
   156 	 *
   142 	 * @since 2.5.0
   157 	 * @since 2.5.0
   143 	 *
   158 	 *
   144 	 * @param WP_User|WP_Error $user     WP_User or WP_Error object if a previous
   159 	 * @param WP_User|WP_Error $user     WP_User or WP_Error object if a previous
   145 	 *                                   callback failed authentication.
   160 	 *                                   callback failed authentication.
   147 	 */
   162 	 */
   148 	$user = apply_filters( 'wp_authenticate_user', $user, $password );
   163 	$user = apply_filters( 'wp_authenticate_user', $user, $password );
   149 	if ( is_wp_error($user) )
   164 	if ( is_wp_error($user) )
   150 		return $user;
   165 		return $user;
   151 
   166 
   152 	if ( !wp_check_password($password, $user->user_pass, $user->ID) )
   167 	if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
   153 		return new WP_Error( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The password you entered for the username <strong>%1$s</strong> is incorrect. <a href="%2$s">Lost your password?</a>' ),
   168 		return new WP_Error( 'incorrect_password',
   154 		$username, wp_lostpassword_url() ) );
   169 			sprintf(
       
   170 				/* translators: %s: user name */
       
   171 				__( '<strong>ERROR</strong>: The password you entered for the username %s is incorrect.' ),
       
   172 				'<strong>' . $username . '</strong>'
       
   173 			) .
       
   174 			' <a href="' . wp_lostpassword_url() . '">' .
       
   175 			__( 'Lost your password?' ) .
       
   176 			'</a>'
       
   177 		);
       
   178 	}
   155 
   179 
   156 	return $user;
   180 	return $user;
   157 }
   181 }
   158 
   182 
   159 /**
   183 /**
       
   184  * Authenticates a user using the email and password.
       
   185  *
       
   186  * @since 4.5.0
       
   187  *
       
   188  * @param WP_User|WP_Error|null $user     WP_User or WP_Error object if a previous
       
   189  *                                        callback failed authentication.
       
   190  * @param string                $email    Email address for authentication.
       
   191  * @param string                $password Password for authentication.
       
   192  * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
       
   193  */
       
   194 function wp_authenticate_email_password( $user, $email, $password ) {
       
   195 	if ( $user instanceof WP_User ) {
       
   196 		return $user;
       
   197 	}
       
   198 
       
   199 	if ( empty( $email ) || empty( $password ) ) {
       
   200 		if ( is_wp_error( $user ) ) {
       
   201 			return $user;
       
   202 		}
       
   203 
       
   204 		$error = new WP_Error();
       
   205 
       
   206 		if ( empty( $email ) ) {
       
   207 			$error->add( 'empty_username', __( '<strong>ERROR</strong>: The email field is empty.' ) ); // Uses 'empty_username' for back-compat with wp_signon()
       
   208 		}
       
   209 
       
   210 		if ( empty( $password ) ) {
       
   211 			$error->add( 'empty_password', __( '<strong>ERROR</strong>: The password field is empty.' ) );
       
   212 		}
       
   213 
       
   214 		return $error;
       
   215 	}
       
   216 
       
   217 	if ( ! is_email( $email ) ) {
       
   218 		return $user;
       
   219 	}
       
   220 
       
   221 	$user = get_user_by( 'email', $email );
       
   222 
       
   223 	if ( ! $user ) {
       
   224 		return new WP_Error( 'invalid_email',
       
   225 			__( '<strong>ERROR</strong>: Invalid email address.' ) .
       
   226 			' <a href="' . wp_lostpassword_url() . '">' .
       
   227 			__( 'Lost your password?' ) .
       
   228 			'</a>'
       
   229 		);
       
   230 	}
       
   231 
       
   232 	/** This filter is documented in wp-includes/user.php */
       
   233 	$user = apply_filters( 'wp_authenticate_user', $user, $password );
       
   234 
       
   235 	if ( is_wp_error( $user ) ) {
       
   236 		return $user;
       
   237 	}
       
   238 
       
   239 	if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
       
   240 		return new WP_Error( 'incorrect_password',
       
   241 			sprintf(
       
   242 				/* translators: %s: email address */
       
   243 				__( '<strong>ERROR</strong>: The password you entered for the email address %s is incorrect.' ),
       
   244 				'<strong>' . $email . '</strong>'
       
   245 			) .
       
   246 			' <a href="' . wp_lostpassword_url() . '">' .
       
   247 			__( 'Lost your password?' ) .
       
   248 			'</a>'
       
   249 		);
       
   250 	}
       
   251 
       
   252 	return $user;
       
   253 }
       
   254 
       
   255 /**
   160  * Authenticate the user using the WordPress auth cookie.
   256  * Authenticate the user using the WordPress auth cookie.
   161  *
   257  *
   162  * @since 2.8.0
   258  * @since 2.8.0
       
   259  *
       
   260  * @global string $auth_secure_cookie
   163  *
   261  *
   164  * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
   262  * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
   165  * @param string                $username Username. If not empty, cancels the cookie authentication.
   263  * @param string                $username Username. If not empty, cancels the cookie authentication.
   166  * @param string                $password Password. If not empty, cancels the cookie authentication.
   264  * @param string                $password Password. If not empty, cancels the cookie authentication.
   167  * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
   265  * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
   202  * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
   300  * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
   203  */
   301  */
   204 function wp_authenticate_spam_check( $user ) {
   302 function wp_authenticate_spam_check( $user ) {
   205 	if ( $user instanceof WP_User && is_multisite() ) {
   303 	if ( $user instanceof WP_User && is_multisite() ) {
   206 		/**
   304 		/**
   207 		 * Filter whether the user has been marked as a spammer.
   305 		 * Filters whether the user has been marked as a spammer.
   208 		 *
   306 		 *
   209 		 * @since 3.7.0
   307 		 * @since 3.7.0
   210 		 *
   308 		 *
   211 		 * @param bool    $spammed Whether the user is considered a spammer.
   309 		 * @param bool    $spammed Whether the user is considered a spammer.
   212 		 * @param WP_User $user    User to check against.
   310 		 * @param WP_User $user    User to check against.
   213 		 */
   311 		 */
   214 		$spammed = apply_filters( 'check_is_user_spammed', is_user_spammy(), $user );
   312 		$spammed = apply_filters( 'check_is_user_spammed', is_user_spammy( $user ), $user );
   215 
   313 
   216 		if ( $spammed )
   314 		if ( $spammed )
   217 			return new WP_Error( 'spammer_account', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.' ) );
   315 			return new WP_Error( 'spammer_account', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.' ) );
   218 	}
   316 	}
   219 	return $user;
   317 	return $user;
   220 }
   318 }
   221 
   319 
   222 /**
   320 /**
   223  * Validate the logged-in cookie.
   321  * Validates the logged-in cookie.
   224  *
   322  *
   225  * Checks the logged-in cookie if the previous auth cookie could not be
   323  * Checks the logged-in cookie if the previous auth cookie could not be
   226  * validated and parsed.
   324  * validated and parsed.
   227  *
   325  *
   228  * This is a callback for the determine_current_user filter, rather than API.
   326  * This is a callback for the {@see 'determine_current_user'} filter, rather than API.
   229  *
   327  *
   230  * @since 3.9.0
   328  * @since 3.9.0
   231  *
   329  *
   232  * @param int|bool $user_id The user ID (or false) as received from the
   330  * @param int|bool $user_id The user ID (or false) as received from the
   233  *                       determine_current_user filter.
   331  *                       determine_current_user filter.
   234  * @return int|bool User ID if validated, false otherwise. If a user ID from
   332  * @return int|false User ID if validated, false otherwise. If a user ID from
   235  *                  an earlier filter callback is received, that value is returned.
   333  *                   an earlier filter callback is received, that value is returned.
   236  */
   334  */
   237 function wp_validate_logged_in_cookie( $user_id ) {
   335 function wp_validate_logged_in_cookie( $user_id ) {
   238 	if ( $user_id ) {
   336 	if ( $user_id ) {
   239 		return $user_id;
   337 		return $user_id;
   240 	}
   338 	}
   249 /**
   347 /**
   250  * Number of posts user has written.
   348  * Number of posts user has written.
   251  *
   349  *
   252  * @since 3.0.0
   350  * @since 3.0.0
   253  * @since 4.1.0 Added `$post_type` argument.
   351  * @since 4.1.0 Added `$post_type` argument.
   254  *
   352  * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array
   255  * @global wpdb $wpdb WordPress database object for queries.
   353  *              of post types to `$post_type`.
   256  *
   354  *
   257  * @param int    $userid    User ID.
   355  * @global wpdb $wpdb WordPress database abstraction object.
   258  * @param string $post_type Optional. Post type to count the number of posts for. Default 'post'.
   356  *
   259  * @return int Number of posts the user has written in this post type.
   357  * @param int          $userid      User ID.
   260  */
   358  * @param array|string $post_type   Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
   261 function count_user_posts( $userid, $post_type = 'post' ) {
   359  * @param bool         $public_only Optional. Whether to only return counts for public posts. Default false.
       
   360  * @return string Number of posts the user has written in this post type.
       
   361  */
       
   362 function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
   262 	global $wpdb;
   363 	global $wpdb;
   263 
   364 
   264 	$where = get_posts_by_author_sql( $post_type, true, $userid );
   365 	$where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
   265 
   366 
   266 	$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
   367 	$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
   267 
   368 
   268 	/**
   369 	/**
   269 	 * Filter the number of posts a user has written.
   370 	 * Filters the number of posts a user has written.
   270 	 *
   371 	 *
   271 	 * @since 2.7.0
   372 	 * @since 2.7.0
   272 	 * @since 4.1.0 Added `$post_type` argument.
   373 	 * @since 4.1.0 Added `$post_type` argument.
   273 	 *
   374 	 * @since 4.3.1 Added `$public_only` argument.
   274 	 * @param int    $count     The user's post count.
   375 	 *
   275 	 * @param int    $userid    User ID.
   376 	 * @param int          $count       The user's post count.
   276 	 * @param string $post_type Post type to count the number of posts for.
   377 	 * @param int          $userid      User ID.
   277 	 */
   378 	 * @param string|array $post_type   Single post type or array of post types to count the number of posts for.
   278 	return apply_filters( 'get_usernumposts', $count, $userid, $post_type );
   379 	 * @param bool         $public_only Whether to limit counted posts to public posts.
       
   380 	 */
       
   381 	return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only );
   279 }
   382 }
   280 
   383 
   281 /**
   384 /**
   282  * Number of posts written by a list of users.
   385  * Number of posts written by a list of users.
   283  *
   386  *
   284  * @since 3.0.0
   387  * @since 3.0.0
   285  *
   388  *
   286  * @param array $users Array of user IDs.
   389  * @global wpdb $wpdb WordPress database abstraction object.
   287  * @param string $post_type Optional. Post type to check. Defaults to post.
   390  *
   288  * @param bool $public_only Optional. Only return counts for public posts.  Defaults to false.
   391  * @param array        $users       Array of user IDs.
       
   392  * @param string|array $post_type   Optional. Single post type or array of post types to check. Defaults to 'post'.
       
   393  * @param bool         $public_only Optional. Only return counts for public posts.  Defaults to false.
   289  * @return array Amount of posts each user has written.
   394  * @return array Amount of posts each user has written.
   290  */
   395  */
   291 function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
   396 function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
   292 	global $wpdb;
   397 	global $wpdb;
   293 
   398 
   316 //
   421 //
   317 
   422 
   318 /**
   423 /**
   319  * Get the current user's ID
   424  * Get the current user's ID
   320  *
   425  *
   321  * @since MU
   426  * @since MU (3.0.0)
   322  *
   427  *
   323  * @return int The current user's ID
   428  * @return int The current user's ID, or 0 if no user is logged in.
   324  */
   429  */
   325 function get_current_user_id() {
   430 function get_current_user_id() {
   326 	if ( ! function_exists( 'wp_get_current_user' ) )
   431 	if ( ! function_exists( 'wp_get_current_user' ) )
   327 		return 0;
   432 		return 0;
   328 	$user = wp_get_current_user();
   433 	$user = wp_get_current_user();
   339  *
   444  *
   340  * The option will first check for the per site name and then the per Network name.
   445  * The option will first check for the per site name and then the per Network name.
   341  *
   446  *
   342  * @since 2.0.0
   447  * @since 2.0.0
   343  *
   448  *
   344  * @global wpdb $wpdb WordPress database object for queries.
   449  * @global wpdb $wpdb WordPress database abstraction object.
   345  *
   450  *
   346  * @param string $option     User option name.
   451  * @param string $option     User option name.
   347  * @param int    $user       Optional. User ID.
   452  * @param int    $user       Optional. User ID.
   348  * @param string $deprecated Use get_option() to check for an option in the options table.
   453  * @param string $deprecated Use get_option() to check for an option in the options table.
   349  * @return mixed User option value on success, false on failure.
   454  * @return mixed User option value on success, false on failure.
   350  */
   455  */
   351 function get_user_option( $option, $user = 0, $deprecated = '' ) {
   456 function get_user_option( $option, $user = 0, $deprecated = '' ) {
   352 	global $wpdb;
   457 	global $wpdb;
   353 
   458 
   354 	if ( !empty( $deprecated ) )
   459 	if ( !empty( $deprecated ) )
   355 		_deprecated_argument( __FUNCTION__, '3.0' );
   460 		_deprecated_argument( __FUNCTION__, '3.0.0' );
   356 
   461 
   357 	if ( empty( $user ) )
   462 	if ( empty( $user ) )
   358 		$user = get_current_user_id();
   463 		$user = get_current_user_id();
   359 
   464 
   360 	if ( ! $user = get_userdata( $user ) )
   465 	if ( ! $user = get_userdata( $user ) )
   367 		$result = $user->get( $option );
   472 		$result = $user->get( $option );
   368 	else
   473 	else
   369 		$result = false;
   474 		$result = false;
   370 
   475 
   371 	/**
   476 	/**
   372 	 * Filter a specific user option value.
   477 	 * Filters a specific user option value.
   373 	 *
   478 	 *
   374 	 * The dynamic portion of the hook name, `$option`, refers to the user option name.
   479 	 * The dynamic portion of the hook name, `$option`, refers to the user option name.
   375 	 *
   480 	 *
   376 	 * @since 2.5.0
   481 	 * @since 2.5.0
   377 	 *
   482 	 *
   391  *
   496  *
   392  * Deletes the user option if $newvalue is empty.
   497  * Deletes the user option if $newvalue is empty.
   393  *
   498  *
   394  * @since 2.0.0
   499  * @since 2.0.0
   395  *
   500  *
   396  * @global wpdb $wpdb WordPress database object for queries.
   501  * @global wpdb $wpdb WordPress database abstraction object.
   397  *
   502  *
   398  * @param int    $user_id     User ID.
   503  * @param int    $user_id     User ID.
   399  * @param string $option_name User option name.
   504  * @param string $option_name User option name.
   400  * @param mixed  $newvalue    User option value.
   505  * @param mixed  $newvalue    User option value.
   401  * @param bool   $global      Optional. Whether option name is global or blog specific.
   506  * @param bool   $global      Optional. Whether option name is global or blog specific.
   419  * global blog options. If the 'global' parameter is false, which it is by default
   524  * global blog options. If the 'global' parameter is false, which it is by default
   420  * it will prepend the WordPress table prefix to the option name.
   525  * it will prepend the WordPress table prefix to the option name.
   421  *
   526  *
   422  * @since 3.0.0
   527  * @since 3.0.0
   423  *
   528  *
   424  * @global wpdb $wpdb WordPress database object for queries.
   529  * @global wpdb $wpdb WordPress database abstraction object.
   425  *
   530  *
   426  * @param int    $user_id     User ID
   531  * @param int    $user_id     User ID
   427  * @param string $option_name User option name.
   532  * @param string $option_name User option name.
   428  * @param bool   $global      Optional. Whether option name is global or blog specific.
   533  * @param bool   $global      Optional. Whether option name is global or blog specific.
   429  *                            Default false (blog specific).
   534  *                            Default false (blog specific).
   436 		$option_name = $wpdb->get_blog_prefix() . $option_name;
   541 		$option_name = $wpdb->get_blog_prefix() . $option_name;
   437 	return delete_user_meta( $user_id, $option_name );
   542 	return delete_user_meta( $user_id, $option_name );
   438 }
   543 }
   439 
   544 
   440 /**
   545 /**
   441  * WordPress User Query class.
   546  * Retrieve list of users matching criteria.
   442  *
   547  *
   443  * @since 3.1.0
   548  * @since 3.1.0
   444  *
   549  *
   445  * @see WP_User_Query::prepare_query() for information on accepted arguments.
       
   446  */
       
   447 class WP_User_Query {
       
   448 
       
   449 	/**
       
   450 	 * Query vars, after parsing
       
   451 	 *
       
   452 	 * @since 3.5.0
       
   453 	 * @access public
       
   454 	 * @var array
       
   455 	 */
       
   456 	public $query_vars = array();
       
   457 
       
   458 	/**
       
   459 	 * List of found user ids
       
   460 	 *
       
   461 	 * @since 3.1.0
       
   462 	 * @access private
       
   463 	 * @var array
       
   464 	 */
       
   465 	private $results;
       
   466 
       
   467 	/**
       
   468 	 * Total number of found users for the current query
       
   469 	 *
       
   470 	 * @since 3.1.0
       
   471 	 * @access private
       
   472 	 * @var int
       
   473 	 */
       
   474 	private $total_users = 0;
       
   475 
       
   476 	/**
       
   477 	 * Metadata query container.
       
   478 	 *
       
   479 	 * @since 4.2.0
       
   480 	 * @access public
       
   481 	 * @var object WP_Meta_Query
       
   482 	 */
       
   483 	public $meta_query = false;
       
   484 
       
   485 	private $compat_fields = array( 'results', 'total_users' );
       
   486 
       
   487 	// SQL clauses
       
   488 	public $query_fields;
       
   489 	public $query_from;
       
   490 	public $query_where;
       
   491 	public $query_orderby;
       
   492 	public $query_limit;
       
   493 
       
   494 	/**
       
   495 	 * PHP5 constructor.
       
   496 	 *
       
   497 	 * @since 3.1.0
       
   498 	 *
       
   499 	 * @param null|string|array $args Optional. The query variables.
       
   500 	 */
       
   501 	public function __construct( $query = null ) {
       
   502 		if ( ! empty( $query ) ) {
       
   503 			$this->prepare_query( $query );
       
   504 			$this->query();
       
   505 		}
       
   506 	}
       
   507 
       
   508 	/**
       
   509 	 * Prepare the query variables.
       
   510 	 *
       
   511 	 * @since 3.1.0
       
   512 	 * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax
       
   513 	 *              for `$orderby` parameter.
       
   514 	 * @access public
       
   515 	 *
       
   516 	 * @param string|array $query {
       
   517 	 *     Optional. Array or string of Query parameters.
       
   518 	 *
       
   519 	 *     @type int          $blog_id         The site ID. Default is the global blog id.
       
   520 	 *     @type string       $role            Role name. Default empty.
       
   521 	 *     @type string       $meta_key        User meta key. Default empty.
       
   522 	 *     @type string       $meta_value      User meta value. Default empty.
       
   523 	 *     @type string       $meta_compare    Comparison operator to test the `$meta_value`. Accepts '=', '!=',
       
   524 	 *                                         '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN',
       
   525 	 *                                         'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP', 'NOT REGEXP',
       
   526 	 *                                         or 'RLIKE'. Default '='.
       
   527 	 *     @type array        $include         An array of user IDs to include. Default empty array.
       
   528 	 *     @type array        $exclude         An array of user IDs to exclude. Default empty array.
       
   529 	 *     @type string       $search          Search keyword. Searches for possible string matches on columns.
       
   530 	 *                                         When `$search_columns` is left empty, it tries to determine which
       
   531 	 *                                         column to search in based on search string. Default empty.
       
   532 	 *     @type array        $search_columns  Array of column names to be searched. Accepts 'ID', 'login',
       
   533 	 *                                         'nicename', 'email', 'url'. Default empty array.
       
   534 	 *     @type string|array $orderby         Field(s) to sort the retrieved users by. May be a single value,
       
   535 	 *                                         an array of values, or a multi-dimensional array with fields as keys
       
   536 	 *                                         and orders ('ASC' or 'DESC') as values. Accepted values are'ID',
       
   537 	 *                                         'display_name' (or 'name'), 'user_login' (or 'login'),
       
   538 	 *                                         'user_nicename' (or 'nicename'), 'user_email' (or 'email'),
       
   539 	 *                                         'user_url' (or 'url'), 'user_registered' (or 'registered'),
       
   540 	 *                                         'post_count', 'meta_value', 'meta_value_num', the value of
       
   541 	 *                                         `$meta_key`, or an array key of `$meta_query`. To use 'meta_value'
       
   542 	 *                                         or 'meta_value_num', `$meta_key` must be also be defined.
       
   543 	 *                                         Default 'user_login'.
       
   544 	 *     @type string       $order           Designates ascending or descending order of users. Order values
       
   545 	 *                                         passed as part of an `$orderby` array take precedence over this
       
   546 	 *                                         parameter. Accepts 'ASC', 'DESC'. Default 'ASC'.
       
   547 	 *     @type int          $offset          Number of users to offset in retrieved results. Can be used in
       
   548 	 *                                         conjunction with pagination. Default 0.
       
   549 	 *     @type int          $number          Number of users to limit the query for. Can be used in conjunction
       
   550 	 *                                         with pagination. Value -1 (all) is not supported.
       
   551 	 *                                         Default empty (all users).
       
   552 	 *     @type bool         $count_total     Whether to count the total number of users found. If pagination is not
       
   553 	 *                                         needed, setting this to false can improve performance. Default true.
       
   554 	 *     @type string|array $fields          Which fields to return. Single or all fields (string), or array
       
   555 	 *                                         of fields. Accepts 'ID', 'display_name', 'login', 'nicename', 'email',
       
   556 	 *                                         'url', 'registered'. Use 'all' for all fields and 'all_with_meta' to
       
   557 	 *                                         include meta fields. Default 'all'.
       
   558 	 *     @type string       $who             Type of users to query. Accepts 'authors'. Default empty (all users).
       
   559 	 * }
       
   560 	 */
       
   561 	public function prepare_query( $query = array() ) {
       
   562 		global $wpdb;
       
   563 
       
   564 		if ( empty( $this->query_vars ) || ! empty( $query ) ) {
       
   565 			$this->query_limit = null;
       
   566 			$this->query_vars = wp_parse_args( $query, array(
       
   567 				'blog_id' => $GLOBALS['blog_id'],
       
   568 				'role' => '',
       
   569 				'meta_key' => '',
       
   570 				'meta_value' => '',
       
   571 				'meta_compare' => '',
       
   572 				'include' => array(),
       
   573 				'exclude' => array(),
       
   574 				'search' => '',
       
   575 				'search_columns' => array(),
       
   576 				'orderby' => 'login',
       
   577 				'order' => 'ASC',
       
   578 				'offset' => '',
       
   579 				'number' => '',
       
   580 				'count_total' => true,
       
   581 				'fields' => 'all',
       
   582 				'who' => ''
       
   583 			) );
       
   584 		}
       
   585 
       
   586 		/**
       
   587 		 * Fires before the WP_User_Query has been parsed.
       
   588 		 *
       
   589 		 * The passed WP_User_Query object contains the query variables, not
       
   590 		 * yet passed into SQL.
       
   591 		 *
       
   592 		 * @since 4.0.0
       
   593 		 *
       
   594 		 * @param WP_User_Query $this The current WP_User_Query instance,
       
   595 		 *                            passed by reference.
       
   596 		 */
       
   597 		do_action( 'pre_get_users', $this );
       
   598 
       
   599 		$qv =& $this->query_vars;
       
   600 
       
   601 		if ( is_array( $qv['fields'] ) ) {
       
   602 			$qv['fields'] = array_unique( $qv['fields'] );
       
   603 
       
   604 			$this->query_fields = array();
       
   605 			foreach ( $qv['fields'] as $field ) {
       
   606 				$field = 'ID' === $field ? 'ID' : sanitize_key( $field );
       
   607 				$this->query_fields[] = "$wpdb->users.$field";
       
   608 			}
       
   609 			$this->query_fields = implode( ',', $this->query_fields );
       
   610 		} elseif ( 'all' == $qv['fields'] ) {
       
   611 			$this->query_fields = "$wpdb->users.*";
       
   612 		} else {
       
   613 			$this->query_fields = "$wpdb->users.ID";
       
   614 		}
       
   615 
       
   616 		if ( isset( $qv['count_total'] ) && $qv['count_total'] )
       
   617 			$this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;
       
   618 
       
   619 		$this->query_from = "FROM $wpdb->users";
       
   620 		$this->query_where = "WHERE 1=1";
       
   621 
       
   622 		// Parse and sanitize 'include', for use by 'orderby' as well as 'include' below.
       
   623 		if ( ! empty( $qv['include'] ) ) {
       
   624 			$include = wp_parse_id_list( $qv['include'] );
       
   625 		} else {
       
   626 			$include = false;
       
   627 		}
       
   628 
       
   629 		$blog_id = 0;
       
   630 		if ( isset( $qv['blog_id'] ) ) {
       
   631 			$blog_id = absint( $qv['blog_id'] );
       
   632 		}
       
   633 
       
   634 		if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) {
       
   635 			$qv['meta_key'] = $wpdb->get_blog_prefix( $blog_id ) . 'user_level';
       
   636 			$qv['meta_value'] = 0;
       
   637 			$qv['meta_compare'] = '!=';
       
   638 			$qv['blog_id'] = $blog_id = 0; // Prevent extra meta query
       
   639 		}
       
   640 
       
   641 		// Meta query.
       
   642 		$this->meta_query = new WP_Meta_Query();
       
   643 		$this->meta_query->parse_query_vars( $qv );
       
   644 
       
   645 		$role = '';
       
   646 		if ( isset( $qv['role'] ) ) {
       
   647 			$role = trim( $qv['role'] );
       
   648 		}
       
   649 
       
   650 		if ( $blog_id && ( $role || is_multisite() ) ) {
       
   651 			$cap_meta_query = array();
       
   652 			$cap_meta_query['key'] = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
       
   653 
       
   654 			if ( $role ) {
       
   655 				$cap_meta_query['value'] = '"' . $role . '"';
       
   656 				$cap_meta_query['compare'] = 'like';
       
   657 			}
       
   658 
       
   659 			if ( empty( $this->meta_query->queries ) ) {
       
   660 				$this->meta_query->queries = array( $cap_meta_query );
       
   661 			} elseif ( ! in_array( $cap_meta_query, $this->meta_query->queries, true ) ) {
       
   662 				// Append the cap query to the original queries and reparse the query.
       
   663 				$this->meta_query->queries = array(
       
   664 					'relation' => 'AND',
       
   665 					array( $this->meta_query->queries, $cap_meta_query ),
       
   666 				);
       
   667 			}
       
   668 
       
   669 			$this->meta_query->parse_query_vars( $this->meta_query->queries );
       
   670 		}
       
   671 
       
   672 		if ( ! empty( $this->meta_query->queries ) ) {
       
   673 			$clauses = $this->meta_query->get_sql( 'user', $wpdb->users, 'ID', $this );
       
   674 			$this->query_from .= $clauses['join'];
       
   675 			$this->query_where .= $clauses['where'];
       
   676 
       
   677 			if ( 'OR' == $this->meta_query->relation ) {
       
   678 				$this->query_fields = 'DISTINCT ' . $this->query_fields;
       
   679 			}
       
   680 		}
       
   681 
       
   682 		// sorting
       
   683 		$qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
       
   684 		$order = $this->parse_order( $qv['order'] );
       
   685 
       
   686 		if ( empty( $qv['orderby'] ) ) {
       
   687 			// Default order is by 'user_login'.
       
   688 			$ordersby = array( 'user_login' => $order );
       
   689 		} else if ( is_array( $qv['orderby'] ) ) {
       
   690 			$ordersby = $qv['orderby'];
       
   691 		} else {
       
   692 			// 'orderby' values may be a comma- or space-separated list.
       
   693 			$ordersby = preg_split( '/[,\s]+/', $qv['orderby'] );
       
   694 		}
       
   695 
       
   696 		$orderby_array = array();
       
   697 		foreach ( $ordersby as $_key => $_value ) {
       
   698 			if ( ! $_value ) {
       
   699 				continue;
       
   700 			}
       
   701 
       
   702 			if ( is_int( $_key ) ) {
       
   703 				// Integer key means this is a flat array of 'orderby' fields.
       
   704 				$_orderby = $_value;
       
   705 				$_order = $order;
       
   706 			} else {
       
   707 				// Non-integer key means this the key is the field and the value is ASC/DESC.
       
   708 				$_orderby = $_key;
       
   709 				$_order = $_value;
       
   710 			}
       
   711 
       
   712 			$parsed = $this->parse_orderby( $_orderby );
       
   713 
       
   714 			if ( ! $parsed ) {
       
   715 				continue;
       
   716 			}
       
   717 
       
   718 			$orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
       
   719 		}
       
   720 
       
   721 		// If no valid clauses were found, order by user_login.
       
   722 		if ( empty( $orderby_array ) ) {
       
   723 			$orderby_array[] = "user_login $order";
       
   724 		}
       
   725 
       
   726 		$this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
       
   727 
       
   728 		// limit
       
   729 		if ( isset( $qv['number'] ) && $qv['number'] ) {
       
   730 			if ( $qv['offset'] )
       
   731 				$this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']);
       
   732 			else
       
   733 				$this->query_limit = $wpdb->prepare("LIMIT %d", $qv['number']);
       
   734 		}
       
   735 
       
   736 		$search = '';
       
   737 		if ( isset( $qv['search'] ) )
       
   738 			$search = trim( $qv['search'] );
       
   739 
       
   740 		if ( $search ) {
       
   741 			$leading_wild = ( ltrim($search, '*') != $search );
       
   742 			$trailing_wild = ( rtrim($search, '*') != $search );
       
   743 			if ( $leading_wild && $trailing_wild )
       
   744 				$wild = 'both';
       
   745 			elseif ( $leading_wild )
       
   746 				$wild = 'leading';
       
   747 			elseif ( $trailing_wild )
       
   748 				$wild = 'trailing';
       
   749 			else
       
   750 				$wild = false;
       
   751 			if ( $wild )
       
   752 				$search = trim($search, '*');
       
   753 
       
   754 			$search_columns = array();
       
   755 			if ( $qv['search_columns'] )
       
   756 				$search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename' ) );
       
   757 			if ( ! $search_columns ) {
       
   758 				if ( false !== strpos( $search, '@') )
       
   759 					$search_columns = array('user_email');
       
   760 				elseif ( is_numeric($search) )
       
   761 					$search_columns = array('user_login', 'ID');
       
   762 				elseif ( preg_match('|^https?://|', $search) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) )
       
   763 					$search_columns = array('user_url');
       
   764 				else
       
   765 					$search_columns = array('user_login', 'user_nicename');
       
   766 			}
       
   767 
       
   768 			/**
       
   769 			 * Filter the columns to search in a WP_User_Query search.
       
   770 			 *
       
   771 			 * The default columns depend on the search term, and include 'user_email',
       
   772 			 * 'user_login', 'ID', 'user_url', and 'user_nicename'.
       
   773 			 *
       
   774 			 * @since 3.6.0
       
   775 			 *
       
   776 			 * @param array         $search_columns Array of column names to be searched.
       
   777 			 * @param string        $search         Text being searched.
       
   778 			 * @param WP_User_Query $this           The current WP_User_Query instance.
       
   779 			 */
       
   780 			$search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this );
       
   781 
       
   782 			$this->query_where .= $this->get_search_sql( $search, $search_columns, $wild );
       
   783 		}
       
   784 
       
   785 		if ( ! empty( $include ) ) {
       
   786 			// Sanitized earlier.
       
   787 			$ids = implode( ',', $include );
       
   788 			$this->query_where .= " AND $wpdb->users.ID IN ($ids)";
       
   789 		} elseif ( ! empty( $qv['exclude'] ) ) {
       
   790 			$ids = implode( ',', wp_parse_id_list( $qv['exclude'] ) );
       
   791 			$this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)";
       
   792 		}
       
   793 
       
   794 		// Date queries are allowed for the user_registered field.
       
   795 		if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) {
       
   796 			$date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' );
       
   797 			$this->query_where .= $date_query->get_sql();
       
   798 		}
       
   799 
       
   800 		/**
       
   801 		 * Fires after the WP_User_Query has been parsed, and before
       
   802 		 * the query is executed.
       
   803 		 *
       
   804 		 * The passed WP_User_Query object contains SQL parts formed
       
   805 		 * from parsing the given query.
       
   806 		 *
       
   807 		 * @since 3.1.0
       
   808 		 *
       
   809 		 * @param WP_User_Query $this The current WP_User_Query instance,
       
   810 		 *                            passed by reference.
       
   811 		 */
       
   812 		do_action_ref_array( 'pre_user_query', array( &$this ) );
       
   813 	}
       
   814 
       
   815 	/**
       
   816 	 * Execute the query, with the current variables.
       
   817 	 *
       
   818 	 * @since 3.1.0
       
   819 	 *
       
   820 	 * @global wpdb $wpdb WordPress database object for queries.
       
   821 	 */
       
   822 	public function query() {
       
   823 		global $wpdb;
       
   824 
       
   825 		$qv =& $this->query_vars;
       
   826 
       
   827 		$query = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";
       
   828 
       
   829 		if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) {
       
   830 			$this->results = $wpdb->get_results( $query );
       
   831 		} else {
       
   832 			$this->results = $wpdb->get_col( $query );
       
   833 		}
       
   834 
       
   835 		/**
       
   836 		 * Filter SELECT FOUND_ROWS() query for the current WP_User_Query instance.
       
   837 		 *
       
   838 		 * @since 3.2.0
       
   839 		 *
       
   840 		 * @global wpdb $wpdb WordPress database abstraction object.
       
   841 		 *
       
   842 		 * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query.
       
   843 		 */
       
   844 		if ( isset( $qv['count_total'] ) && $qv['count_total'] )
       
   845 			$this->total_users = $wpdb->get_var( apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()' ) );
       
   846 
       
   847 		if ( !$this->results )
       
   848 			return;
       
   849 
       
   850 		if ( 'all_with_meta' == $qv['fields'] ) {
       
   851 			cache_users( $this->results );
       
   852 
       
   853 			$r = array();
       
   854 			foreach ( $this->results as $userid )
       
   855 				$r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] );
       
   856 
       
   857 			$this->results = $r;
       
   858 		} elseif ( 'all' == $qv['fields'] ) {
       
   859 			foreach ( $this->results as $key => $user ) {
       
   860 				$this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] );
       
   861 			}
       
   862 		}
       
   863 	}
       
   864 
       
   865 	/**
       
   866 	 * Retrieve query variable.
       
   867 	 *
       
   868 	 * @since 3.5.0
       
   869 	 * @access public
       
   870 	 *
       
   871 	 * @param string $query_var Query variable key.
       
   872 	 * @return mixed
       
   873 	 */
       
   874 	public function get( $query_var ) {
       
   875 		if ( isset( $this->query_vars[$query_var] ) )
       
   876 			return $this->query_vars[$query_var];
       
   877 
       
   878 		return null;
       
   879 	}
       
   880 
       
   881 	/**
       
   882 	 * Set query variable.
       
   883 	 *
       
   884 	 * @since 3.5.0
       
   885 	 * @access public
       
   886 	 *
       
   887 	 * @param string $query_var Query variable key.
       
   888 	 * @param mixed $value Query variable value.
       
   889 	 */
       
   890 	public function set( $query_var, $value ) {
       
   891 		$this->query_vars[$query_var] = $value;
       
   892 	}
       
   893 
       
   894 	/**
       
   895 	 * Used internally to generate an SQL string for searching across multiple columns
       
   896 	 *
       
   897 	 * @access protected
       
   898 	 * @since 3.1.0
       
   899 	 *
       
   900 	 * @param string $string
       
   901 	 * @param array $cols
       
   902 	 * @param bool $wild Whether to allow wildcard searches. Default is false for Network Admin, true for
       
   903 	 *  single site. Single site allows leading and trailing wildcards, Network Admin only trailing.
       
   904 	 * @return string
       
   905 	 */
       
   906 	protected function get_search_sql( $string, $cols, $wild = false ) {
       
   907 		global $wpdb;
       
   908 
       
   909 		$searches = array();
       
   910 		$leading_wild = ( 'leading' == $wild || 'both' == $wild ) ? '%' : '';
       
   911 		$trailing_wild = ( 'trailing' == $wild || 'both' == $wild ) ? '%' : '';
       
   912 		$like = $leading_wild . $wpdb->esc_like( $string ) . $trailing_wild;
       
   913 
       
   914 		foreach ( $cols as $col ) {
       
   915 			if ( 'ID' == $col ) {
       
   916 				$searches[] = $wpdb->prepare( "$col = %s", $string );
       
   917 			} else {
       
   918 				$searches[] = $wpdb->prepare( "$col LIKE %s", $like );
       
   919 			}
       
   920 		}
       
   921 
       
   922 		return ' AND (' . implode(' OR ', $searches) . ')';
       
   923 	}
       
   924 
       
   925 	/**
       
   926 	 * Return the list of users.
       
   927 	 *
       
   928 	 * @since 3.1.0
       
   929 	 * @access public
       
   930 	 *
       
   931 	 * @return array Array of results.
       
   932 	 */
       
   933 	public function get_results() {
       
   934 		return $this->results;
       
   935 	}
       
   936 
       
   937 	/**
       
   938 	 * Return the total number of users for the current query.
       
   939 	 *
       
   940 	 * @since 3.1.0
       
   941 	 * @access public
       
   942 	 *
       
   943 	 * @return int Number of total users.
       
   944 	 */
       
   945 	public function get_total() {
       
   946 		return $this->total_users;
       
   947 	}
       
   948 
       
   949 	/**
       
   950 	 * Parse and sanitize 'orderby' keys passed to the user query.
       
   951 	 *
       
   952 	 * @since 4.2.0
       
   953 	 * @access protected
       
   954 	 *
       
   955 	 * @global wpdb $wpdb WordPress database abstraction object.
       
   956 	 *
       
   957 	 * @param string $orderby Alias for the field to order by.
       
   958 	 * @return string|bool Value to used in the ORDER clause, if `$orderby` is valid. False otherwise.
       
   959 	 */
       
   960 	protected function parse_orderby( $orderby ) {
       
   961 		global $wpdb;
       
   962 
       
   963 		$meta_query_clauses = $this->meta_query->get_clauses();
       
   964 
       
   965 		$_orderby = '';
       
   966 		if ( in_array( $orderby, array( 'login', 'nicename', 'email', 'url', 'registered' ) ) ) {
       
   967 			$_orderby = 'user_' . $orderby;
       
   968 		} elseif ( in_array( $orderby, array( 'user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered' ) ) ) {
       
   969 			$_orderby = $orderby;
       
   970 		} elseif ( 'name' == $orderby || 'display_name' == $orderby ) {
       
   971 			$_orderby = 'display_name';
       
   972 		} elseif ( 'post_count' == $orderby ) {
       
   973 			// todo: avoid the JOIN
       
   974 			$where = get_posts_by_author_sql( 'post' );
       
   975 			$this->query_from .= " LEFT OUTER JOIN (
       
   976 				SELECT post_author, COUNT(*) as post_count
       
   977 				FROM $wpdb->posts
       
   978 				$where
       
   979 				GROUP BY post_author
       
   980 			) p ON ({$wpdb->users}.ID = p.post_author)
       
   981 			";
       
   982 			$_orderby = 'post_count';
       
   983 		} elseif ( 'ID' == $orderby || 'id' == $orderby ) {
       
   984 			$_orderby = 'ID';
       
   985 		} elseif ( 'meta_value' == $orderby || $this->get( 'meta_key' ) == $orderby ) {
       
   986 			$_orderby = "$wpdb->usermeta.meta_value";
       
   987 		} elseif ( 'meta_value_num' == $orderby ) {
       
   988 			$_orderby = "$wpdb->usermeta.meta_value+0";
       
   989 		} elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
       
   990 			$include = wp_parse_id_list( $this->query_vars['include'] );
       
   991 			$include_sql = implode( ',', $include );
       
   992 			$_orderby = "FIELD( $wpdb->users.ID, $include_sql )";
       
   993 		} elseif ( isset( $meta_query_clauses[ $orderby ] ) ) {
       
   994 			$meta_clause = $meta_query_clauses[ $orderby ];
       
   995 			$_orderby = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );
       
   996 		}
       
   997 
       
   998 		return $_orderby;
       
   999 	}
       
  1000 
       
  1001 	/**
       
  1002 	 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
       
  1003 	 *
       
  1004 	 * @since 4.2.0
       
  1005 	 * @access protected
       
  1006 	 *
       
  1007 	 * @param string $order The 'order' query variable.
       
  1008 	 * @return string The sanitized 'order' query variable.
       
  1009 	 */
       
  1010 	protected function parse_order( $order ) {
       
  1011 		if ( ! is_string( $order ) || empty( $order ) ) {
       
  1012 			return 'DESC';
       
  1013 		}
       
  1014 
       
  1015 		if ( 'ASC' === strtoupper( $order ) ) {
       
  1016 			return 'ASC';
       
  1017 		} else {
       
  1018 			return 'DESC';
       
  1019 		}
       
  1020 	}
       
  1021 
       
  1022 	/**
       
  1023 	 * Make private properties readable for backwards compatibility.
       
  1024 	 *
       
  1025 	 * @since 4.0.0
       
  1026 	 * @access public
       
  1027 	 *
       
  1028 	 * @param string $name Property to get.
       
  1029 	 * @return mixed Property.
       
  1030 	 */
       
  1031 	public function __get( $name ) {
       
  1032 		if ( in_array( $name, $this->compat_fields ) ) {
       
  1033 			return $this->$name;
       
  1034 		}
       
  1035 	}
       
  1036 
       
  1037 	/**
       
  1038 	 * Make private properties settable for backwards compatibility.
       
  1039 	 *
       
  1040 	 * @since 4.0.0
       
  1041 	 * @access public
       
  1042 	 *
       
  1043 	 * @param string $name  Property to check if set.
       
  1044 	 * @param mixed  $value Property value.
       
  1045 	 * @return mixed Newly-set property.
       
  1046 	 */
       
  1047 	public function __set( $name, $value ) {
       
  1048 		if ( in_array( $name, $this->compat_fields ) ) {
       
  1049 			return $this->$name = $value;
       
  1050 		}
       
  1051 	}
       
  1052 
       
  1053 	/**
       
  1054 	 * Make private properties checkable for backwards compatibility.
       
  1055 	 *
       
  1056 	 * @since 4.0.0
       
  1057 	 * @access public
       
  1058 	 *
       
  1059 	 * @param string $name Property to check if set.
       
  1060 	 * @return bool Whether the property is set.
       
  1061 	 */
       
  1062 	public function __isset( $name ) {
       
  1063 		if ( in_array( $name, $this->compat_fields ) ) {
       
  1064 			return isset( $this->$name );
       
  1065 		}
       
  1066 	}
       
  1067 
       
  1068 	/**
       
  1069 	 * Make private properties un-settable for backwards compatibility.
       
  1070 	 *
       
  1071 	 * @since 4.0.0
       
  1072 	 * @access public
       
  1073 	 *
       
  1074 	 * @param string $name Property to unset.
       
  1075 	 */
       
  1076 	public function __unset( $name ) {
       
  1077 		if ( in_array( $name, $this->compat_fields ) ) {
       
  1078 			unset( $this->$name );
       
  1079 		}
       
  1080 	}
       
  1081 
       
  1082 	/**
       
  1083 	 * Make private/protected methods readable for backwards compatibility.
       
  1084 	 *
       
  1085 	 * @since 4.0.0
       
  1086 	 * @access public
       
  1087 	 *
       
  1088 	 * @param callable $name      Method to call.
       
  1089 	 * @param array    $arguments Arguments to pass when calling.
       
  1090 	 * @return mixed|bool Return value of the callback, false otherwise.
       
  1091 	 */
       
  1092 	public function __call( $name, $arguments ) {
       
  1093 		if ( 'get_search_sql' === $name ) {
       
  1094 			return call_user_func_array( array( $this, $name ), $arguments );
       
  1095 		}
       
  1096 		return false;
       
  1097 	}
       
  1098 }
       
  1099 
       
  1100 /**
       
  1101  * Retrieve list of users matching criteria.
       
  1102  *
       
  1103  * @since 3.1.0
       
  1104  *
       
  1105  * @see WP_User_Query
   550  * @see WP_User_Query
  1106  *
   551  *
  1107  * @param array $args Optional. Arguments to retrieve users. See {@see WP_User_Query::prepare_query()}
   552  * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query().
  1108  *                    for more information on accepted arguments.
   553  *                    for more information on accepted arguments.
  1109  * @return array List of users.
   554  * @return array List of users.
  1110  */
   555  */
  1111 function get_users( $args = array() ) {
   556 function get_users( $args = array() ) {
  1112 
   557 
  1117 
   562 
  1118 	return (array) $user_search->get_results();
   563 	return (array) $user_search->get_results();
  1119 }
   564 }
  1120 
   565 
  1121 /**
   566 /**
  1122  * Get the blogs a user belongs to.
   567  * Get the sites a user belongs to.
  1123  *
   568  *
  1124  * @since 3.0.0
   569  * @since 3.0.0
  1125  *
   570  * @since 4.7.0 Converted to use get_sites().
  1126  * @global wpdb $wpdb WordPress database object for queries.
   571  *
       
   572  * @global wpdb $wpdb WordPress database abstraction object.
  1127  *
   573  *
  1128  * @param int  $user_id User ID
   574  * @param int  $user_id User ID
  1129  * @param bool $all     Whether to retrieve all blogs, or only blogs that are not
   575  * @param bool $all     Whether to retrieve all sites, or only sites that are not
  1130  *                      marked as deleted, archived, or spam.
   576  *                      marked as deleted, archived, or spam.
  1131  * @return array A list of the user's blogs. An empty array if the user doesn't exist
   577  * @return array A list of the user's sites. An empty array if the user doesn't exist
  1132  *               or belongs to no blogs.
   578  *               or belongs to no sites.
  1133  */
   579  */
  1134 function get_blogs_of_user( $user_id, $all = false ) {
   580 function get_blogs_of_user( $user_id, $all = false ) {
  1135 	global $wpdb;
   581 	global $wpdb;
  1136 
   582 
  1137 	$user_id = (int) $user_id;
   583 	$user_id = (int) $user_id;
  1138 
   584 
  1139 	// Logged out users can't have blogs
   585 	// Logged out users can't have sites
  1140 	if ( empty( $user_id ) )
   586 	if ( empty( $user_id ) )
  1141 		return array();
   587 		return array();
       
   588 
       
   589 	/**
       
   590 	 * Filters the list of a user's sites before it is populated.
       
   591 	 *
       
   592 	 * Passing a non-null value to the filter will effectively short circuit
       
   593 	 * get_blogs_of_user(), returning that value instead.
       
   594 	 *
       
   595 	 * @since 4.6.0
       
   596 	 *
       
   597 	 * @param null|array $sites   An array of site objects of which the user is a member.
       
   598 	 * @param int        $user_id User ID.
       
   599 	 * @param bool       $all     Whether the returned array should contain all sites, including
       
   600 	 *                            those marked 'deleted', 'archived', or 'spam'. Default false.
       
   601 	 */
       
   602 	$sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
       
   603 
       
   604 	if ( null !== $sites ) {
       
   605 		return $sites;
       
   606 	}
  1142 
   607 
  1143 	$keys = get_user_meta( $user_id );
   608 	$keys = get_user_meta( $user_id );
  1144 	if ( empty( $keys ) )
   609 	if ( empty( $keys ) )
  1145 		return array();
   610 		return array();
  1146 
   611 
  1147 	if ( ! is_multisite() ) {
   612 	if ( ! is_multisite() ) {
  1148 		$blog_id = get_current_blog_id();
   613 		$site_id = get_current_blog_id();
  1149 		$blogs = array( $blog_id => new stdClass );
   614 		$sites = array( $site_id => new stdClass );
  1150 		$blogs[ $blog_id ]->userblog_id = $blog_id;
   615 		$sites[ $site_id ]->userblog_id = $site_id;
  1151 		$blogs[ $blog_id ]->blogname = get_option('blogname');
   616 		$sites[ $site_id ]->blogname = get_option('blogname');
  1152 		$blogs[ $blog_id ]->domain = '';
   617 		$sites[ $site_id ]->domain = '';
  1153 		$blogs[ $blog_id ]->path = '';
   618 		$sites[ $site_id ]->path = '';
  1154 		$blogs[ $blog_id ]->site_id = 1;
   619 		$sites[ $site_id ]->site_id = 1;
  1155 		$blogs[ $blog_id ]->siteurl = get_option('siteurl');
   620 		$sites[ $site_id ]->siteurl = get_option('siteurl');
  1156 		$blogs[ $blog_id ]->archived = 0;
   621 		$sites[ $site_id ]->archived = 0;
  1157 		$blogs[ $blog_id ]->spam = 0;
   622 		$sites[ $site_id ]->spam = 0;
  1158 		$blogs[ $blog_id ]->deleted = 0;
   623 		$sites[ $site_id ]->deleted = 0;
  1159 		return $blogs;
   624 		return $sites;
  1160 	}
   625 	}
  1161 
   626 
  1162 	$blogs = array();
   627 	$site_ids = array();
  1163 
   628 
  1164 	if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
   629 	if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
  1165 		$blog = get_blog_details( 1 );
   630 		$site_ids[] = 1;
  1166 		if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
       
  1167 			$blogs[ 1 ] = (object) array(
       
  1168 				'userblog_id' => 1,
       
  1169 				'blogname'    => $blog->blogname,
       
  1170 				'domain'      => $blog->domain,
       
  1171 				'path'        => $blog->path,
       
  1172 				'site_id'     => $blog->site_id,
       
  1173 				'siteurl'     => $blog->siteurl,
       
  1174 				'archived'    => 0,
       
  1175 				'spam'        => 0,
       
  1176 				'deleted'     => 0
       
  1177 			);
       
  1178 		}
       
  1179 		unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
   631 		unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
  1180 	}
   632 	}
  1181 
   633 
  1182 	$keys = array_keys( $keys );
   634 	$keys = array_keys( $keys );
  1183 
   635 
  1184 	foreach ( $keys as $key ) {
   636 	foreach ( $keys as $key ) {
  1185 		if ( 'capabilities' !== substr( $key, -12 ) )
   637 		if ( 'capabilities' !== substr( $key, -12 ) )
  1186 			continue;
   638 			continue;
  1187 		if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) )
   639 		if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) )
  1188 			continue;
   640 			continue;
  1189 		$blog_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
   641 		$site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
  1190 		if ( ! is_numeric( $blog_id ) )
   642 		if ( ! is_numeric( $site_id ) )
  1191 			continue;
   643 			continue;
  1192 
   644 
  1193 		$blog_id = (int) $blog_id;
   645 		$site_ids[] = (int) $site_id;
  1194 		$blog = get_blog_details( $blog_id );
   646 	}
  1195 		if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
   647 
  1196 			$blogs[ $blog_id ] = (object) array(
   648 	$sites = array();
  1197 				'userblog_id' => $blog_id,
   649 
  1198 				'blogname'    => $blog->blogname,
   650 	if ( ! empty( $site_ids ) ) {
  1199 				'domain'      => $blog->domain,
   651 		$args = array(
  1200 				'path'        => $blog->path,
   652 			'number'   => '',
  1201 				'site_id'     => $blog->site_id,
   653 			'site__in' => $site_ids,
  1202 				'siteurl'     => $blog->siteurl,
   654 		);
  1203 				'archived'    => 0,
   655 		if ( ! $all ) {
  1204 				'spam'        => 0,
   656 			$args['archived'] = 0;
  1205 				'deleted'     => 0
   657 			$args['spam']     = 0;
       
   658 			$args['deleted']  = 0;
       
   659 		}
       
   660 
       
   661 		$_sites = get_sites( $args );
       
   662 
       
   663 		foreach ( $_sites as $site ) {
       
   664 			$sites[ $site->id ] = (object) array(
       
   665 				'userblog_id' => $site->id,
       
   666 				'blogname'    => $site->blogname,
       
   667 				'domain'      => $site->domain,
       
   668 				'path'        => $site->path,
       
   669 				'site_id'     => $site->network_id,
       
   670 				'siteurl'     => $site->siteurl,
       
   671 				'archived'    => $site->archived,
       
   672 				'mature'      => $site->mature,
       
   673 				'spam'        => $site->spam,
       
   674 				'deleted'     => $site->deleted,
  1206 			);
   675 			);
  1207 		}
   676 		}
  1208 	}
   677 	}
  1209 
   678 
  1210 	/**
   679 	/**
  1211 	 * Filter the list of blogs a user belongs to.
   680 	 * Filters the list of sites a user belongs to.
  1212 	 *
   681 	 *
  1213 	 * @since MU
   682 	 * @since MU (3.0.0)
  1214 	 *
   683 	 *
  1215 	 * @param array $blogs   An array of blog objects belonging to the user.
   684 	 * @param array $sites   An array of site objects belonging to the user.
  1216 	 * @param int   $user_id User ID.
   685 	 * @param int   $user_id User ID.
  1217 	 * @param bool  $all     Whether the returned blogs array should contain all blogs, including
   686 	 * @param bool  $all     Whether the returned sites array should contain all sites, including
  1218 	 *                       those marked 'deleted', 'archived', or 'spam'. Default false.
   687 	 *                       those marked 'deleted', 'archived', or 'spam'. Default false.
  1219 	 */
   688 	 */
  1220 	return apply_filters( 'get_blogs_of_user', $blogs, $user_id, $all );
   689 	return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all );
  1221 }
   690 }
  1222 
   691 
  1223 /**
   692 /**
  1224  * Find out whether a user is a member of a given blog.
   693  * Find out whether a user is a member of a given blog.
  1225  *
   694  *
  1226  * @since MU 1.1
   695  * @since MU (3.0.0)
       
   696  *
       
   697  * @global wpdb $wpdb WordPress database abstraction object.
  1227  *
   698  *
  1228  * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
   699  * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
  1229  * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
   700  * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
  1230  * @return bool
   701  * @return bool
  1231  */
   702  */
  1232 function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
   703 function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
       
   704 	global $wpdb;
       
   705 
  1233 	$user_id = (int) $user_id;
   706 	$user_id = (int) $user_id;
  1234 	$blog_id = (int) $blog_id;
   707 	$blog_id = (int) $blog_id;
  1235 
   708 
  1236 	if ( empty( $user_id ) )
   709 	if ( empty( $user_id ) ) {
  1237 		$user_id = get_current_user_id();
   710 		$user_id = get_current_user_id();
  1238 
   711 	}
  1239 	if ( empty( $blog_id ) )
   712 
       
   713 	// Technically not needed, but does save calls to get_site and get_user_meta
       
   714 	// in the event that the function is called when a user isn't logged in
       
   715 	if ( empty( $user_id ) ) {
       
   716 		return false;
       
   717 	} else {
       
   718 		$user = get_userdata( $user_id );
       
   719 		if ( ! $user instanceof WP_User ) {
       
   720 			return false;
       
   721 		}
       
   722 	}
       
   723 
       
   724 	if ( ! is_multisite() ) {
       
   725 		return true;
       
   726 	}
       
   727 
       
   728 	if ( empty( $blog_id ) ) {
  1240 		$blog_id = get_current_blog_id();
   729 		$blog_id = get_current_blog_id();
  1241 
   730 	}
  1242 	$blogs = get_blogs_of_user( $user_id );
   731 
  1243 	return array_key_exists( $blog_id, $blogs );
   732 	$blog = get_site( $blog_id );
  1244 }
   733 
  1245 
   734 	if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
  1246 /**
   735 		return false;
  1247  * Add meta data field to a user.
   736 	}
  1248  *
   737 
  1249  * Post meta data is called "Custom Fields" on the Administration Screens.
   738 	$keys = get_user_meta( $user_id );
       
   739 	if ( empty( $keys ) ) {
       
   740 		return false;
       
   741 	}
       
   742 
       
   743 	// no underscore before capabilities in $base_capabilities_key
       
   744 	$base_capabilities_key = $wpdb->base_prefix . 'capabilities';
       
   745 	$site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
       
   746 
       
   747 	if ( isset( $keys[ $base_capabilities_key ] ) && $blog_id == 1 ) {
       
   748 		return true;
       
   749 	}
       
   750 
       
   751 	if ( isset( $keys[ $site_capabilities_key ] ) ) {
       
   752 		return true;
       
   753 	}
       
   754 
       
   755 	return false;
       
   756 }
       
   757 
       
   758 /**
       
   759  * Adds meta data to a user.
  1250  *
   760  *
  1251  * @since 3.0.0
   761  * @since 3.0.0
  1252  * @link https://codex.wordpress.org/Function_Reference/add_user_meta
   762  *
  1253  *
   763  * @param int    $user_id    User ID.
  1254  * @param int $user_id User ID.
   764  * @param string $meta_key   Metadata name.
  1255  * @param string $meta_key Metadata name.
   765  * @param mixed  $meta_value Metadata value.
  1256  * @param mixed $meta_value Metadata value.
   766  * @param bool   $unique     Optional. Whether the same key should not be added. Default false.
  1257  * @param bool $unique Optional, default is false. Whether the same key should not be added.
   767  * @return int|false Meta ID on success, false on failure.
  1258  * @return int|bool Meta ID on success, false on failure.
       
  1259  */
   768  */
  1260 function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) {
   769 function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) {
  1261 	return add_metadata('user', $user_id, $meta_key, $meta_value, $unique);
   770 	return add_metadata('user', $user_id, $meta_key, $meta_value, $unique);
  1262 }
   771 }
  1263 
   772 
  1269  * allows removing all metadata matching key, if needed.
   778  * allows removing all metadata matching key, if needed.
  1270  *
   779  *
  1271  * @since 3.0.0
   780  * @since 3.0.0
  1272  * @link https://codex.wordpress.org/Function_Reference/delete_user_meta
   781  * @link https://codex.wordpress.org/Function_Reference/delete_user_meta
  1273  *
   782  *
  1274  * @param int $user_id user ID
   783  * @param int    $user_id    User ID
  1275  * @param string $meta_key Metadata name.
   784  * @param string $meta_key   Metadata name.
  1276  * @param mixed $meta_value Optional. Metadata value.
   785  * @param mixed  $meta_value Optional. Metadata value.
  1277  * @return bool True on success, false on failure.
   786  * @return bool True on success, false on failure.
  1278  */
   787  */
  1279 function delete_user_meta($user_id, $meta_key, $meta_value = '') {
   788 function delete_user_meta($user_id, $meta_key, $meta_value = '') {
  1280 	return delete_metadata('user', $user_id, $meta_key, $meta_value);
   789 	return delete_metadata('user', $user_id, $meta_key, $meta_value);
  1281 }
   790 }
  1284  * Retrieve user meta field for a user.
   793  * Retrieve user meta field for a user.
  1285  *
   794  *
  1286  * @since 3.0.0
   795  * @since 3.0.0
  1287  * @link https://codex.wordpress.org/Function_Reference/get_user_meta
   796  * @link https://codex.wordpress.org/Function_Reference/get_user_meta
  1288  *
   797  *
  1289  * @param int $user_id User ID.
   798  * @param int    $user_id User ID.
  1290  * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
   799  * @param string $key     Optional. The meta key to retrieve. By default, returns data for all keys.
  1291  * @param bool $single Whether to return a single value.
   800  * @param bool   $single  Whether to return a single value.
  1292  * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
   801  * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
  1293  *  is true.
       
  1294  */
   802  */
  1295 function get_user_meta($user_id, $key = '', $single = false) {
   803 function get_user_meta($user_id, $key = '', $single = false) {
  1296 	return get_metadata('user', $user_id, $key, $single);
   804 	return get_metadata('user', $user_id, $key, $single);
  1297 }
   805 }
  1298 
   806 
  1305  * If the meta field for the user does not exist, it will be added.
   813  * If the meta field for the user does not exist, it will be added.
  1306  *
   814  *
  1307  * @since 3.0.0
   815  * @since 3.0.0
  1308  * @link https://codex.wordpress.org/Function_Reference/update_user_meta
   816  * @link https://codex.wordpress.org/Function_Reference/update_user_meta
  1309  *
   817  *
  1310  * @param int $user_id User ID.
   818  * @param int    $user_id    User ID.
  1311  * @param string $meta_key Metadata key.
   819  * @param string $meta_key   Metadata key.
  1312  * @param mixed $meta_value Metadata value.
   820  * @param mixed  $meta_value Metadata value.
  1313  * @param mixed $prev_value Optional. Previous value to check before removing.
   821  * @param mixed  $prev_value Optional. Previous value to check before removing.
  1314  * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
   822  * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
  1315  */
   823  */
  1316 function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
   824 function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
  1317 	return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
   825 	return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
  1318 }
   826 }
  1324  * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
   832  * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
  1325  * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
   833  * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
  1326  * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
   834  * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
  1327  *
   835  *
  1328  * @since 3.0.0
   836  * @since 3.0.0
  1329  * @param string $strategy 'time' or 'memory'
   837  * @since 4.4.0 The number of users with no role is now included in the `none` element.
       
   838  * @since 4.9.0 The `$site_id` parameter was added to support multisite.
       
   839  *
       
   840  * @global wpdb $wpdb WordPress database abstraction object.
       
   841  *
       
   842  * @param string   $strategy Optional. The computational strategy to use when counting the users.
       
   843  *                           Accepts either 'time' or 'memory'. Default 'time'.
       
   844  * @param int|null $site_id  Optional. The site ID to count users for. Defaults to the current site.
  1330  * @return array Includes a grand total and an array of counts indexed by role strings.
   845  * @return array Includes a grand total and an array of counts indexed by role strings.
  1331  */
   846  */
  1332 function count_users($strategy = 'time') {
   847 function count_users( $strategy = 'time', $site_id = null ) {
  1333 	global $wpdb, $wp_roles;
   848 	global $wpdb;
  1334 
   849 
  1335 	// Initialize
   850 	// Initialize
  1336 	$id = get_current_blog_id();
   851 	if ( ! $site_id ) {
  1337 	$blog_prefix = $wpdb->get_blog_prefix($id);
   852 		$site_id = get_current_blog_id();
       
   853 	}
       
   854 	$blog_prefix = $wpdb->get_blog_prefix( $site_id );
  1338 	$result = array();
   855 	$result = array();
  1339 
   856 
  1340 	if ( 'time' == $strategy ) {
   857 	if ( 'time' == $strategy ) {
  1341 		global $wp_roles;
   858 		if ( is_multisite() && $site_id != get_current_blog_id() ) {
  1342 
   859 			switch_to_blog( $site_id );
  1343 		if ( ! isset( $wp_roles ) )
   860 			$avail_roles = wp_roles()->get_names();
  1344 			$wp_roles = new WP_Roles();
   861 			restore_current_blog();
  1345 
   862 		} else {
  1346 		$avail_roles = $wp_roles->get_names();
   863 			$avail_roles = wp_roles()->get_names();
       
   864 		}
  1347 
   865 
  1348 		// Build a CPU-intensive query that will return concise information.
   866 		// Build a CPU-intensive query that will return concise information.
  1349 		$select_count = array();
   867 		$select_count = array();
  1350 		foreach ( $avail_roles as $this_role => $name ) {
   868 		foreach ( $avail_roles as $this_role => $name ) {
  1351 			$select_count[] = $wpdb->prepare( "COUNT(NULLIF(`meta_value` LIKE %s, false))", '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%');
   869 			$select_count[] = $wpdb->prepare( "COUNT(NULLIF(`meta_value` LIKE %s, false))", '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%');
  1352 		}
   870 		}
       
   871 		$select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
  1353 		$select_count = implode(', ', $select_count);
   872 		$select_count = implode(', ', $select_count);
  1354 
   873 
  1355 		// Add the meta_value index to the selection list, then run the query.
   874 		// Add the meta_value index to the selection list, then run the query.
  1356 		$row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
   875 		$row = $wpdb->get_row( "
       
   876 			SELECT {$select_count}, COUNT(*)
       
   877 			FROM {$wpdb->usermeta}
       
   878 			INNER JOIN {$wpdb->users} ON user_id = ID
       
   879 			WHERE meta_key = '{$blog_prefix}capabilities'
       
   880 		", ARRAY_N );
  1357 
   881 
  1358 		// Run the previous loop again to associate results with role names.
   882 		// Run the previous loop again to associate results with role names.
  1359 		$col = 0;
   883 		$col = 0;
  1360 		$role_counts = array();
   884 		$role_counts = array();
  1361 		foreach ( $avail_roles as $this_role => $name ) {
   885 		foreach ( $avail_roles as $this_role => $name ) {
  1363 			if ($count > 0) {
   887 			if ($count > 0) {
  1364 				$role_counts[$this_role] = $count;
   888 				$role_counts[$this_role] = $count;
  1365 			}
   889 			}
  1366 		}
   890 		}
  1367 
   891 
       
   892 		$role_counts['none'] = (int) $row[$col++];
       
   893 
  1368 		// Get the meta_value index from the end of the result set.
   894 		// Get the meta_value index from the end of the result set.
  1369 		$total_users = (int) $row[$col];
   895 		$total_users = (int) $row[$col];
  1370 
   896 
  1371 		$result['total_users'] = $total_users;
   897 		$result['total_users'] = $total_users;
  1372 		$result['avail_roles'] =& $role_counts;
   898 		$result['avail_roles'] =& $role_counts;
  1373 	} else {
   899 	} else {
  1374 		$avail_roles = array();
   900 		$avail_roles = array(
  1375 
   901 			'none' => 0,
  1376 		$users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
   902 		);
       
   903 
       
   904 		$users_of_blog = $wpdb->get_col( "
       
   905 			SELECT meta_value
       
   906 			FROM {$wpdb->usermeta}
       
   907 			INNER JOIN {$wpdb->users} ON user_id = ID
       
   908 			WHERE meta_key = '{$blog_prefix}capabilities'
       
   909 		" );
  1377 
   910 
  1378 		foreach ( $users_of_blog as $caps_meta ) {
   911 		foreach ( $users_of_blog as $caps_meta ) {
  1379 			$b_roles = maybe_unserialize($caps_meta);
   912 			$b_roles = maybe_unserialize($caps_meta);
  1380 			if ( ! is_array( $b_roles ) )
   913 			if ( ! is_array( $b_roles ) )
  1381 				continue;
   914 				continue;
       
   915 			if ( empty( $b_roles ) ) {
       
   916 				$avail_roles['none']++;
       
   917 			}
  1382 			foreach ( $b_roles as $b_role => $val ) {
   918 			foreach ( $b_roles as $b_role => $val ) {
  1383 				if ( isset($avail_roles[$b_role]) ) {
   919 				if ( isset($avail_roles[$b_role]) ) {
  1384 					$avail_roles[$b_role]++;
   920 					$avail_roles[$b_role]++;
  1385 				} else {
   921 				} else {
  1386 					$avail_roles[$b_role] = 1;
   922 					$avail_roles[$b_role] = 1;
  1403  * Set up global user vars.
   939  * Set up global user vars.
  1404  *
   940  *
  1405  * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
   941  * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
  1406  *
   942  *
  1407  * @since 2.0.4
   943  * @since 2.0.4
  1408  * @global string $userdata User description.
   944  *
  1409  * @global string $user_login The user username for logging in
   945  * @global string  $user_login    The user username for logging in
  1410  * @global int $user_level The level of the user
   946  * @global WP_User $userdata      User data.
  1411  * @global int $user_ID The ID of the user
   947  * @global int     $user_level    The level of the user
  1412  * @global string $user_email The email address of the user
   948  * @global int     $user_ID       The ID of the user
  1413  * @global string $user_url The url in the user's profile
   949  * @global string  $user_email    The email address of the user
  1414  * @global string $user_identity The display name of the user
   950  * @global string  $user_url      The url in the user's profile
       
   951  * @global string  $user_identity The display name of the user
  1415  *
   952  *
  1416  * @param int $for_user_id Optional. User ID to set up global data.
   953  * @param int $for_user_id Optional. User ID to set up global data.
  1417  */
   954  */
  1418 function setup_userdata($for_user_id = '') {
   955 function setup_userdata($for_user_id = '') {
  1419 	global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
   956 	global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
  1448  * used, either 'include' or 'exclude', but not both.
   985  * used, either 'include' or 'exclude', but not both.
  1449  *
   986  *
  1450  * The available arguments are as follows:
   987  * The available arguments are as follows:
  1451  *
   988  *
  1452  * @since 2.3.0
   989  * @since 2.3.0
  1453  *
   990  * @since 4.5.0 Added the 'display_name_with_login' value for 'show'.
  1454  * @global wpdb $wpdb WordPress database object for queries.
   991  * @since 4.7.0 Added the `$role`, `$role__in`, and `$role__not_in` parameters.
  1455  *
   992  *
  1456  * @param array|string $args {
   993  * @param array|string $args {
  1457  *     Optional. Array or string of arguments to generate a drop-down of users.
   994  *     Optional. Array or string of arguments to generate a drop-down of users.
  1458  *     {@see WP_User_Query::prepare_query() for additional available arguments.
   995  *     See WP_User_Query::prepare_query() for additional available arguments.
  1459  *
   996  *
  1460  *     @type string       $show_option_all         Text to show as the drop-down default (all).
   997  *     @type string       $show_option_all         Text to show as the drop-down default (all).
  1461  *                                                 Default empty.
   998  *                                                 Default empty.
  1462  *     @type string       $show_option_none        Text to show as the drop-down default when no
   999  *     @type string       $show_option_none        Text to show as the drop-down default when no
  1463  *                                                 users were found. Default empty.
  1000  *                                                 users were found. Default empty.
  1474  *                                                 Default empty.
  1011  *                                                 Default empty.
  1475  *     @type array|string $exclude                 Array or comma-separated list of user IDs to exclude.
  1012  *     @type array|string $exclude                 Array or comma-separated list of user IDs to exclude.
  1476  *                                                 Default empty.
  1013  *                                                 Default empty.
  1477  *     @type bool|int     $multi                   Whether to skip the ID attribute on the 'select' element.
  1014  *     @type bool|int     $multi                   Whether to skip the ID attribute on the 'select' element.
  1478  *                                                 Accepts 1|true or 0|false. Default 0|false.
  1015  *                                                 Accepts 1|true or 0|false. Default 0|false.
  1479  *     @type string       $show                    User table column to display. If the selected item is empty
  1016  *     @type string       $show                    User data to display. If the selected item is empty
  1480  *                                                 then the 'user_login' will be displayed in parentheses.
  1017  *                                                 then the 'user_login' will be displayed in parentheses.
  1481  *                                                 Accepts user fields. Default 'display_name'.
  1018  *                                                 Accepts any user field, or 'display_name_with_login' to show
       
  1019  *                                                 the display name with user_login in parentheses.
       
  1020  *                                                 Default 'display_name'.
  1482  *     @type int|bool     $echo                    Whether to echo or return the drop-down. Accepts 1|true (echo)
  1021  *     @type int|bool     $echo                    Whether to echo or return the drop-down. Accepts 1|true (echo)
  1483  *                                                 or 0|false (return). Default 1|true.
  1022  *                                                 or 0|false (return). Default 1|true.
  1484  *     @type int          $selected                Which user ID should be selected. Default 0.
  1023  *     @type int          $selected                Which user ID should be selected. Default 0.
  1485  *     @type bool         $include_selected        Whether to always include the selected user ID in the drop-
  1024  *     @type bool         $include_selected        Whether to always include the selected user ID in the drop-
  1486  *                                                 down. Default false.
  1025  *                                                 down. Default false.
  1488  *     @type string       $id                      ID attribute of the select element. Default is the value of $name.
  1027  *     @type string       $id                      ID attribute of the select element. Default is the value of $name.
  1489  *     @type string       $class                   Class attribute of the select element. Default empty.
  1028  *     @type string       $class                   Class attribute of the select element. Default empty.
  1490  *     @type int          $blog_id                 ID of blog (Multisite only). Default is ID of the current blog.
  1029  *     @type int          $blog_id                 ID of blog (Multisite only). Default is ID of the current blog.
  1491  *     @type string       $who                     Which type of users to query. Accepts only an empty string or
  1030  *     @type string       $who                     Which type of users to query. Accepts only an empty string or
  1492  *                                                 'authors'. Default empty.
  1031  *                                                 'authors'. Default empty.
       
  1032  *     @type string|array $role                    An array or a comma-separated list of role names that users must
       
  1033  *                                                 match to be included in results. Note that this is an inclusive
       
  1034  *                                                 list: users must match *each* role. Default empty.
       
  1035  *     @type array        $role__in                An array of role names. Matched users must have at least one of
       
  1036  *                                                 these roles. Default empty array.
       
  1037  *     @type array        $role__not_in            An array of role names to exclude. Users matching one or more of
       
  1038  *                                                 these roles will not be included in results. Default empty array.
  1493  * }
  1039  * }
  1494  * @return string|null Null on display. String of HTML content on retrieve.
  1040  * @return string String of HTML content.
  1495  */
  1041  */
  1496 function wp_dropdown_users( $args = '' ) {
  1042 function wp_dropdown_users( $args = '' ) {
  1497 	$defaults = array(
  1043 	$defaults = array(
  1498 		'show_option_all' => '', 'show_option_none' => '', 'hide_if_only_one_author' => '',
  1044 		'show_option_all' => '', 'show_option_none' => '', 'hide_if_only_one_author' => '',
  1499 		'orderby' => 'display_name', 'order' => 'ASC',
  1045 		'orderby' => 'display_name', 'order' => 'ASC',
  1500 		'include' => '', 'exclude' => '', 'multi' => 0,
  1046 		'include' => '', 'exclude' => '', 'multi' => 0,
  1501 		'show' => 'display_name', 'echo' => 1,
  1047 		'show' => 'display_name', 'echo' => 1,
  1502 		'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
  1048 		'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
  1503 		'blog_id' => $GLOBALS['blog_id'], 'who' => '', 'include_selected' => false,
  1049 		'blog_id' => get_current_blog_id(), 'who' => '', 'include_selected' => false,
  1504 		'option_none_value' => -1
  1050 		'option_none_value' => -1,
       
  1051 		'role' => '',
       
  1052 		'role__in' => array(),
       
  1053 		'role__not_in' => array(),
  1505 	);
  1054 	);
  1506 
  1055 
  1507 	$defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
  1056 	$defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
  1508 
  1057 
  1509 	$r = wp_parse_args( $args, $defaults );
  1058 	$r = wp_parse_args( $args, $defaults );
  1510 	$show = $r['show'];
  1059 
       
  1060 	$query_args = wp_array_slice_assoc( $r, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who', 'role', 'role__in', 'role__not_in' ) );
       
  1061 
       
  1062 	$fields = array( 'ID', 'user_login' );
       
  1063 
       
  1064 	$show = ! empty( $r['show'] ) ? $r['show'] : 'display_name';
       
  1065 	if ( 'display_name_with_login' === $show ) {
       
  1066 		$fields[] = 'display_name';
       
  1067 	} else {
       
  1068 		$fields[] = $show;
       
  1069 	}
       
  1070 
       
  1071 	$query_args['fields'] = $fields;
       
  1072 
  1511 	$show_option_all = $r['show_option_all'];
  1073 	$show_option_all = $r['show_option_all'];
  1512 	$show_option_none = $r['show_option_none'];
  1074 	$show_option_none = $r['show_option_none'];
  1513 	$option_none_value = $r['option_none_value'];
  1075 	$option_none_value = $r['option_none_value'];
  1514 
  1076 
  1515 	$query_args = wp_array_slice_assoc( $r, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who' ) );
  1077 	/**
  1516 	$query_args['fields'] = array( 'ID', 'user_login', $show );
  1078 	 * Filters the query arguments for the list of users in the dropdown.
       
  1079 	 *
       
  1080 	 * @since 4.4.0
       
  1081 	 *
       
  1082 	 * @param array $query_args The query arguments for get_users().
       
  1083 	 * @param array $r          The arguments passed to wp_dropdown_users() combined with the defaults.
       
  1084 	 */
       
  1085 	$query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $r );
       
  1086 
  1517 	$users = get_users( $query_args );
  1087 	$users = get_users( $query_args );
  1518 
  1088 
  1519 	$output = '';
  1089 	$output = '';
  1520 	if ( ! empty( $users ) && ( empty( $r['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
  1090 	if ( ! empty( $users ) && ( empty( $r['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
  1521 		$name = esc_attr( $r['name'] );
  1091 		$name = esc_attr( $r['name'] );
  1533 		if ( $show_option_none ) {
  1103 		if ( $show_option_none ) {
  1534 			$_selected = selected( $option_none_value, $r['selected'], false );
  1104 			$_selected = selected( $option_none_value, $r['selected'], false );
  1535 			$output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
  1105 			$output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
  1536 		}
  1106 		}
  1537 
  1107 
  1538 		$found_selected = false;
  1108 		if ( $r['include_selected'] && ( $r['selected'] > 0 ) ) {
       
  1109 			$found_selected = false;
       
  1110 			$r['selected'] = (int) $r['selected'];
       
  1111 			foreach ( (array) $users as $user ) {
       
  1112 				$user->ID = (int) $user->ID;
       
  1113 				if ( $user->ID === $r['selected'] ) {
       
  1114 					$found_selected = true;
       
  1115 				}
       
  1116 			}
       
  1117 
       
  1118 			if ( ! $found_selected ) {
       
  1119 				$users[] = get_userdata( $r['selected'] );
       
  1120 			}
       
  1121 		}
       
  1122 
  1539 		foreach ( (array) $users as $user ) {
  1123 		foreach ( (array) $users as $user ) {
  1540 			$user->ID = (int) $user->ID;
  1124 			if ( 'display_name_with_login' === $show ) {
       
  1125 				/* translators: 1: display name, 2: user_login */
       
  1126 				$display = sprintf( _x( '%1$s (%2$s)', 'user dropdown' ), $user->display_name, $user->user_login );
       
  1127 			} elseif ( ! empty( $user->$show ) ) {
       
  1128 				$display = $user->$show;
       
  1129 			} else {
       
  1130 				$display = '(' . $user->user_login . ')';
       
  1131 			}
       
  1132 
  1541 			$_selected = selected( $user->ID, $r['selected'], false );
  1133 			$_selected = selected( $user->ID, $r['selected'], false );
  1542 			if ( $_selected ) {
       
  1543 				$found_selected = true;
       
  1544 			}
       
  1545 			$display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
       
  1546 			$output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
  1134 			$output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
  1547 		}
  1135 		}
  1548 
  1136 
  1549 		if ( $r['include_selected'] && ! $found_selected && ( $r['selected'] > 0 ) ) {
       
  1550 			$user = get_userdata( $r['selected'] );
       
  1551 			$_selected = selected( $user->ID, $r['selected'], false );
       
  1552 			$display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
       
  1553 			$output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
       
  1554 		}
       
  1555 
       
  1556 		$output .= "</select>";
  1137 		$output .= "</select>";
  1557 	}
  1138 	}
  1558 
  1139 
  1559 	/**
  1140 	/**
  1560 	 * Filter the wp_dropdown_users() HTML output.
  1141 	 * Filters the wp_dropdown_users() HTML output.
  1561 	 *
  1142 	 *
  1562 	 * @since 2.3.0
  1143 	 * @since 2.3.0
  1563 	 *
  1144 	 *
  1564 	 * @param string $output HTML output generated by wp_dropdown_users().
  1145 	 * @param string $output HTML output generated by wp_dropdown_users().
  1565 	 */
  1146 	 */
  1578  * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
  1159  * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
  1579  * when calling filters.
  1160  * when calling filters.
  1580  *
  1161  *
  1581  * @since 2.3.0
  1162  * @since 2.3.0
  1582  *
  1163  *
  1583  * @param string $field The user Object field name.
  1164  * @param string $field   The user Object field name.
  1584  * @param mixed $value The user Object value.
  1165  * @param mixed  $value   The user Object value.
  1585  * @param int $user_id user ID.
  1166  * @param int    $user_id User ID.
  1586  * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
  1167  * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
  1587  *               'attribute' and 'js'.
  1168  *                        'attribute' and 'js'.
  1588  * @return mixed Sanitized value.
  1169  * @return mixed Sanitized value.
  1589  */
  1170  */
  1590 function sanitize_user_field($field, $value, $user_id, $context) {
  1171 function sanitize_user_field($field, $value, $user_id, $context) {
  1591 	$int_fields = array('ID');
  1172 	$int_fields = array('ID');
  1592 	if ( in_array($field, $int_fields) )
  1173 	if ( in_array($field, $int_fields) )
  1606 			/** This filter is documented in wp-includes/post.php */
  1187 			/** This filter is documented in wp-includes/post.php */
  1607 			$value = apply_filters( "edit_{$field}", $value, $user_id );
  1188 			$value = apply_filters( "edit_{$field}", $value, $user_id );
  1608 		} else {
  1189 		} else {
  1609 
  1190 
  1610 			/**
  1191 			/**
  1611 			 * Filter a user field value in the 'edit' context.
  1192 			 * Filters a user field value in the 'edit' context.
  1612 			 *
  1193 			 *
  1613 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1194 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1614 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1195 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1615 			 *
  1196 			 *
  1616 			 * @since 2.9.0
  1197 			 * @since 2.9.0
  1630 			/** This filter is documented in wp-includes/post.php */
  1211 			/** This filter is documented in wp-includes/post.php */
  1631 			$value = apply_filters( "pre_{$field}", $value );
  1212 			$value = apply_filters( "pre_{$field}", $value );
  1632 		} else {
  1213 		} else {
  1633 
  1214 
  1634 			/**
  1215 			/**
  1635 			 * Filter the value of a user field in the 'db' context.
  1216 			 * Filters the value of a user field in the 'db' context.
  1636 			 *
  1217 			 *
  1637 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1218 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1638 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1219 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1639  			 *
  1220  			 *
  1640 			 * @since 2.9.0
  1221 			 * @since 2.9.0
  1646 	} else {
  1227 	} else {
  1647 		// Use display filters by default.
  1228 		// Use display filters by default.
  1648 		if ( $prefixed ) {
  1229 		if ( $prefixed ) {
  1649 
  1230 
  1650 			/** This filter is documented in wp-includes/post.php */
  1231 			/** This filter is documented in wp-includes/post.php */
  1651 			$value = apply_filters( $field, $value, $user_id, $context );
  1232 			$value = apply_filters( "{$field}", $value, $user_id, $context );
  1652 		} else {
  1233 		} else {
  1653 
  1234 
  1654 			/**
  1235 			/**
  1655 			 * Filter the value of a user field in a standard context.
  1236 			 * Filters the value of a user field in a standard context.
  1656 			 *
  1237 			 *
  1657 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1238 			 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
  1658 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1239 			 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
  1659 			 *
  1240 			 *
  1660 			 * @since 2.9.0
  1241 			 * @since 2.9.0
  1681 /**
  1262 /**
  1682  * Update all user caches
  1263  * Update all user caches
  1683  *
  1264  *
  1684  * @since 3.0.0
  1265  * @since 3.0.0
  1685  *
  1266  *
  1686  * @param object $user User object to be cached
  1267  * @param WP_User $user User object to be cached
  1687  */
  1268  * @return bool|null Returns false on failure.
  1688 function update_user_caches($user) {
  1269  */
       
  1270 function update_user_caches( $user ) {
       
  1271 	if ( $user instanceof WP_User ) {
       
  1272 		if ( ! $user->exists() ) {
       
  1273 			return false;
       
  1274 		}
       
  1275 
       
  1276 		$user = $user->data;
       
  1277 	}
       
  1278 
  1689 	wp_cache_add($user->ID, $user, 'users');
  1279 	wp_cache_add($user->ID, $user, 'users');
  1690 	wp_cache_add($user->user_login, $user->ID, 'userlogins');
  1280 	wp_cache_add($user->user_login, $user->ID, 'userlogins');
  1691 	wp_cache_add($user->user_email, $user->ID, 'useremail');
  1281 	wp_cache_add($user->user_email, $user->ID, 'useremail');
  1692 	wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
  1282 	wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
  1693 }
  1283 }
  1694 
  1284 
  1695 /**
  1285 /**
  1696  * Clean all user caches
  1286  * Clean all user caches
  1697  *
  1287  *
  1698  * @since 3.0.0
  1288  * @since 3.0.0
       
  1289  * @since 4.4.0 'clean_user_cache' action was added.
  1699  *
  1290  *
  1700  * @param WP_User|int $user User object or ID to be cleaned from the cache
  1291  * @param WP_User|int $user User object or ID to be cleaned from the cache
  1701  */
  1292  */
  1702 function clean_user_cache( $user ) {
  1293 function clean_user_cache( $user ) {
  1703 	if ( is_numeric( $user ) )
  1294 	if ( is_numeric( $user ) )
  1708 
  1299 
  1709 	wp_cache_delete( $user->ID, 'users' );
  1300 	wp_cache_delete( $user->ID, 'users' );
  1710 	wp_cache_delete( $user->user_login, 'userlogins' );
  1301 	wp_cache_delete( $user->user_login, 'userlogins' );
  1711 	wp_cache_delete( $user->user_email, 'useremail' );
  1302 	wp_cache_delete( $user->user_email, 'useremail' );
  1712 	wp_cache_delete( $user->user_nicename, 'userslugs' );
  1303 	wp_cache_delete( $user->user_nicename, 'userslugs' );
       
  1304 
       
  1305 	/**
       
  1306 	 * Fires immediately after the given user's cache is cleaned.
       
  1307 	 *
       
  1308 	 * @since 4.4.0
       
  1309 	 *
       
  1310 	 * @param int     $user_id User ID.
       
  1311 	 * @param WP_User $user    User object.
       
  1312 	 */
       
  1313 	do_action( 'clean_user_cache', $user->ID, $user );
  1713 }
  1314 }
  1714 
  1315 
  1715 /**
  1316 /**
  1716  * Checks whether the given username exists.
  1317  * Checks whether the given username exists.
  1717  *
  1318  *
  1718  * @since 2.0.0
  1319  * @since 2.0.0
  1719  *
  1320  *
  1720  * @param string $username Username.
  1321  * @param string $username Username.
  1721  * @return null|int The user's ID on success, and null on failure.
  1322  * @return int|false The user's ID on success, and false on failure.
  1722  */
  1323  */
  1723 function username_exists( $username ) {
  1324 function username_exists( $username ) {
  1724 	if ( $user = get_user_by('login', $username ) ) {
  1325 	if ( $user = get_user_by( 'login', $username ) ) {
       
  1326 		$user_id = $user->ID;
       
  1327 	} else {
       
  1328 		$user_id = false;
       
  1329 	}
       
  1330 
       
  1331 	/**
       
  1332 	 * Filters whether the given username exists or not.
       
  1333 	 *
       
  1334 	 * @since 4.9.0
       
  1335 	 *
       
  1336 	 * @param int|false $user_id  The user's ID on success, and false on failure.
       
  1337 	 * @param string    $username Username to check.
       
  1338 	 */
       
  1339 	return apply_filters( 'username_exists', $user_id, $username );
       
  1340 }
       
  1341 
       
  1342 /**
       
  1343  * Checks whether the given email exists.
       
  1344  *
       
  1345  * @since 2.1.0
       
  1346  *
       
  1347  * @param string $email Email.
       
  1348  * @return int|false The user's ID on success, and false on failure.
       
  1349  */
       
  1350 function email_exists( $email ) {
       
  1351 	if ( $user = get_user_by( 'email', $email) ) {
  1725 		return $user->ID;
  1352 		return $user->ID;
  1726 	} else {
  1353 	}
  1727 		return null;
       
  1728 	}
       
  1729 }
       
  1730 
       
  1731 /**
       
  1732  * Checks whether the given email exists.
       
  1733  *
       
  1734  * @since 2.1.0
       
  1735  *
       
  1736  * @param string $email Email.
       
  1737  * @return bool|int The user's ID on success, and false on failure.
       
  1738  */
       
  1739 function email_exists( $email ) {
       
  1740 	if ( $user = get_user_by('email', $email) )
       
  1741 		return $user->ID;
       
  1742 
       
  1743 	return false;
  1354 	return false;
  1744 }
  1355 }
  1745 
  1356 
  1746 /**
  1357 /**
  1747  * Checks whether a username is valid.
  1358  * Checks whether a username is valid.
  1748  *
  1359  *
  1749  * @since 2.0.1
  1360  * @since 2.0.1
       
  1361  * @since 4.4.0 Empty sanitized usernames are now considered invalid
  1750  *
  1362  *
  1751  * @param string $username Username.
  1363  * @param string $username Username.
  1752  * @return bool Whether username given is valid
  1364  * @return bool Whether username given is valid
  1753  */
  1365  */
  1754 function validate_username( $username ) {
  1366 function validate_username( $username ) {
  1755 	$sanitized = sanitize_user( $username, true );
  1367 	$sanitized = sanitize_user( $username, true );
  1756 	$valid = ( $sanitized == $username );
  1368 	$valid = ( $sanitized == $username && ! empty( $sanitized ) );
  1757 	/**
  1369 
  1758 	 * Filter whether the provided username is valid or not.
  1370 	/**
       
  1371 	 * Filters whether the provided username is valid or not.
  1759 	 *
  1372 	 *
  1760 	 * @since 2.0.1
  1373 	 * @since 2.0.1
  1761 	 *
  1374 	 *
  1762 	 * @param bool   $valid    Whether given username is valid.
  1375 	 * @param bool   $valid    Whether given username is valid.
  1763 	 * @param string $username Username to check.
  1376 	 * @param string $username Username to check.
  1766 }
  1379 }
  1767 
  1380 
  1768 /**
  1381 /**
  1769  * Insert a user into the database.
  1382  * Insert a user into the database.
  1770  *
  1383  *
  1771  * Most of the $userdata array fields have filters associated with the values.
  1384  * Most of the `$userdata` array fields have filters associated with the values. Exceptions are
  1772  * The exceptions are 'rich_editing', 'role', 'jabber', 'aim', 'yim',
  1385  * 'ID', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl',
  1773  * 'user_registered', and 'ID'. The filters have the prefix 'pre_user_' followed
  1386  * 'user_registered', and 'role'. The filters have the prefix 'pre_user_' followed by the field
  1774  * by the field name. An example using 'description' would have the filter
  1387  * name. An example using 'description' would have the filter called, 'pre_user_description' that
  1775  * called, 'pre_user_description' that can be hooked into.
  1388  * can be hooked into.
  1776  *
  1389  *
  1777  * @since 2.0.0
  1390  * @since 2.0.0
  1778  *
  1391  * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact
  1779  * @global wpdb $wpdb WordPress database object for queries.
  1392  *              methods for new installations. See wp_get_user_contact_methods().
  1780  *
  1393  * @since 4.7.0 The user's locale can be passed to `$userdata`.
  1781  * @param array $userdata {
  1394  *
       
  1395  * @global wpdb $wpdb WordPress database abstraction object.
       
  1396  *
       
  1397  * @param array|object|WP_User $userdata {
  1782  *     An array, object, or WP_User object of user data arguments.
  1398  *     An array, object, or WP_User object of user data arguments.
  1783  *
  1399  *
  1784  *     @type int         $ID              User ID. If supplied, the user will be updated.
  1400  *     @type int         $ID                   User ID. If supplied, the user will be updated.
  1785  *     @type string      $user_pass       The plain-text user password.
  1401  *     @type string      $user_pass            The plain-text user password.
  1786  *     @type string      $user_login      The user's login username.
  1402  *     @type string      $user_login           The user's login username.
  1787  *     @type string      $user_nicename   The URL-friendly user name.
  1403  *     @type string      $user_nicename        The URL-friendly user name.
  1788  *     @type string      $user_url        The user URL.
  1404  *     @type string      $user_url             The user URL.
  1789  *     @type string      $user_email      The user email address.
  1405  *     @type string      $user_email           The user email address.
  1790  *     @type string      $display_name    The user's display name.
  1406  *     @type string      $display_name         The user's display name.
  1791  *                                        Default is the the user's username.
  1407  *                                             Default is the user's username.
  1792  *     @type string      $nickname        The user's nickname. Default
  1408  *     @type string      $nickname             The user's nickname.
  1793  *                                        Default is the the user's username.
  1409  *                                             Default is the user's username.
  1794  *     @type string      $first_name      The user's first name. For new users, will be used
  1410  *     @type string      $first_name           The user's first name. For new users, will be used
  1795  *                                        to build $display_name if unspecified.
  1411  *                                             to build the first part of the user's display name
  1796  *     @type stirng      $last_name       The user's last name. For new users, will be used
  1412  *                                             if `$display_name` is not specified.
  1797  *                                        to build $display_name if unspecified.
  1413  *     @type string      $last_name            The user's last name. For new users, will be used
  1798  *     @type string|bool $rich_editing    Whether to enable the rich-editor for the user. False
  1414  *                                             to build the second part of the user's display name
  1799  *                                        if not empty.
  1415  *                                             if `$display_name` is not specified.
  1800  *     @type string      $user_registered Date the user registered. Format is 'Y-m-d H:i:s'.
  1416  *     @type string      $description          The user's biographical description.
  1801  *     @type string      $role            User's role.
  1417  *     @type string|bool $rich_editing         Whether to enable the rich-editor for the user.
  1802  *     @type string      $jabber          User's Jabber account username.
  1418  *                                             False if not empty.
  1803  *     @type string      $aim             User's AIM account username.
  1419  *     @type string|bool $syntax_highlighting  Whether to enable the rich code editor for the user.
  1804  *     @type string      $yim             User's Yahoo! messenger username.
  1420  *                                             False if not empty.
       
  1421  *     @type string|bool $comment_shortcuts    Whether to enable comment moderation keyboard
       
  1422  *                                             shortcuts for the user. Default false.
       
  1423  *     @type string      $admin_color          Admin color scheme for the user. Default 'fresh'.
       
  1424  *     @type bool        $use_ssl              Whether the user should always access the admin over
       
  1425  *                                             https. Default false.
       
  1426  *     @type string      $user_registered      Date the user registered. Format is 'Y-m-d H:i:s'.
       
  1427  *     @type string|bool $show_admin_bar_front Whether to display the Admin Bar for the user on the
       
  1428  *                                             site's front end. Default true.
       
  1429  *     @type string      $role                 User's role.
       
  1430  *     @type string      $locale               User's locale. Default empty.
  1805  * }
  1431  * }
  1806  * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
  1432  * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
  1807  *                      be created.
  1433  *                      be created.
  1808  */
  1434  */
  1809 function wp_insert_user( $userdata ) {
  1435 function wp_insert_user( $userdata ) {
  1812 	if ( $userdata instanceof stdClass ) {
  1438 	if ( $userdata instanceof stdClass ) {
  1813 		$userdata = get_object_vars( $userdata );
  1439 		$userdata = get_object_vars( $userdata );
  1814 	} elseif ( $userdata instanceof WP_User ) {
  1440 	} elseif ( $userdata instanceof WP_User ) {
  1815 		$userdata = $userdata->to_array();
  1441 		$userdata = $userdata->to_array();
  1816 	}
  1442 	}
       
  1443 
  1817 	// Are we updating or creating?
  1444 	// Are we updating or creating?
  1818 	if ( ! empty( $userdata['ID'] ) ) {
  1445 	if ( ! empty( $userdata['ID'] ) ) {
  1819 		$ID = (int) $userdata['ID'];
  1446 		$ID = (int) $userdata['ID'];
  1820 		$update = true;
  1447 		$update = true;
  1821 		$old_user_data = WP_User::get_data_by( 'id', $ID );
  1448 		$old_user_data = get_userdata( $ID );
       
  1449 
       
  1450 		if ( ! $old_user_data ) {
       
  1451 			return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
       
  1452 		}
       
  1453 
  1822 		// hashed in wp_update_user(), plaintext if called directly
  1454 		// hashed in wp_update_user(), plaintext if called directly
  1823 		$user_pass = $userdata['user_pass'];
  1455 		$user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass;
  1824 	} else {
  1456 	} else {
  1825 		$update = false;
  1457 		$update = false;
  1826 		// Hash the password
  1458 		// Hash the password
  1827 		$user_pass = wp_hash_password( $userdata['user_pass'] );
  1459 		$user_pass = wp_hash_password( $userdata['user_pass'] );
  1828 	}
  1460 	}
  1829 
  1461 
  1830 	$sanitized_user_login = sanitize_user( $userdata['user_login'], true );
  1462 	$sanitized_user_login = sanitize_user( $userdata['user_login'], true );
  1831 
  1463 
  1832 	/**
  1464 	/**
  1833 	 * Filter a username after it has been sanitized.
  1465 	 * Filters a username after it has been sanitized.
  1834 	 *
  1466 	 *
  1835 	 * This filter is called before the user is created or updated.
  1467 	 * This filter is called before the user is created or updated.
  1836 	 *
  1468 	 *
  1837 	 * @since 2.0.3
  1469 	 * @since 2.0.3
  1838 	 *
  1470 	 *
  1841 	$pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
  1473 	$pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
  1842 
  1474 
  1843 	//Remove any non-printable chars from the login string to see if we have ended up with an empty username
  1475 	//Remove any non-printable chars from the login string to see if we have ended up with an empty username
  1844 	$user_login = trim( $pre_user_login );
  1476 	$user_login = trim( $pre_user_login );
  1845 
  1477 
       
  1478 	// user_login must be between 0 and 60 characters.
  1846 	if ( empty( $user_login ) ) {
  1479 	if ( empty( $user_login ) ) {
  1847 		return new WP_Error('empty_user_login', __('Cannot create a user with an empty login name.') );
  1480 		return new WP_Error('empty_user_login', __('Cannot create a user with an empty login name.') );
  1848 	}
  1481 	} elseif ( mb_strlen( $user_login ) > 60 ) {
       
  1482 		return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) );
       
  1483 	}
       
  1484 
  1849 	if ( ! $update && username_exists( $user_login ) ) {
  1485 	if ( ! $update && username_exists( $user_login ) ) {
  1850 		return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
  1486 		return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
  1851 	}
  1487 	}
  1852 
  1488 
  1853 	// If a nicename is provided, remove unsafe user characters before
  1489 	/**
  1854 	// using it. Otherwise build a nicename from the user_login.
  1490 	 * Filters the list of blacklisted usernames.
       
  1491 	 *
       
  1492 	 * @since 4.4.0
       
  1493 	 *
       
  1494 	 * @param array $usernames Array of blacklisted usernames.
       
  1495 	 */
       
  1496 	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
       
  1497 
       
  1498 	if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ) ) ) {
       
  1499 		return new WP_Error( 'invalid_username', __( 'Sorry, that username is not allowed.' ) );
       
  1500 	}
       
  1501 
       
  1502 	/*
       
  1503 	 * If a nicename is provided, remove unsafe user characters before using it.
       
  1504 	 * Otherwise build a nicename from the user_login.
       
  1505 	 */
  1855 	if ( ! empty( $userdata['user_nicename'] ) ) {
  1506 	if ( ! empty( $userdata['user_nicename'] ) ) {
  1856 		$user_nicename = sanitize_user( $userdata['user_nicename'], true );
  1507 		$user_nicename = sanitize_user( $userdata['user_nicename'], true );
       
  1508 		if ( mb_strlen( $user_nicename ) > 50 ) {
       
  1509 			return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) );
       
  1510 		}
  1857 	} else {
  1511 	} else {
  1858 		$user_nicename = $user_login;
  1512 		$user_nicename = mb_substr( $user_login, 0, 50 );
  1859 	}
  1513 	}
  1860 
  1514 
  1861 	$user_nicename = sanitize_title( $user_nicename );
  1515 	$user_nicename = sanitize_title( $user_nicename );
  1862 
  1516 
  1863 	// Store values to save in user meta.
  1517 	// Store values to save in user meta.
  1864 	$meta = array();
  1518 	$meta = array();
  1865 
  1519 
  1866 	/**
  1520 	/**
  1867 	 * Filter a user's nicename before the user is created or updated.
  1521 	 * Filters a user's nicename before the user is created or updated.
  1868 	 *
  1522 	 *
  1869 	 * @since 2.0.3
  1523 	 * @since 2.0.3
  1870 	 *
  1524 	 *
  1871 	 * @param string $user_nicename The user's nicename.
  1525 	 * @param string $user_nicename The user's nicename.
  1872 	 */
  1526 	 */
  1873 	$user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
  1527 	$user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
  1874 
  1528 
  1875 	$raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
  1529 	$raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
  1876 
  1530 
  1877 	/**
  1531 	/**
  1878 	 * Filter a user's URL before the user is created or updated.
  1532 	 * Filters a user's URL before the user is created or updated.
  1879 	 *
  1533 	 *
  1880 	 * @since 2.0.3
  1534 	 * @since 2.0.3
  1881 	 *
  1535 	 *
  1882 	 * @param string $raw_user_url The user's URL.
  1536 	 * @param string $raw_user_url The user's URL.
  1883 	 */
  1537 	 */
  1884 	$user_url = apply_filters( 'pre_user_url', $raw_user_url );
  1538 	$user_url = apply_filters( 'pre_user_url', $raw_user_url );
  1885 
  1539 
  1886 	$raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
  1540 	$raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
  1887 
  1541 
  1888 	/**
  1542 	/**
  1889 	 * Filter a user's email before the user is created or updated.
  1543 	 * Filters a user's email before the user is created or updated.
  1890 	 *
  1544 	 *
  1891 	 * @since 2.0.3
  1545 	 * @since 2.0.3
  1892 	 *
  1546 	 *
  1893 	 * @param string $raw_user_email The user's email.
  1547 	 * @param string $raw_user_email The user's email.
  1894 	 */
  1548 	 */
  1897 	/*
  1551 	/*
  1898 	 * If there is no update, just check for `email_exists`. If there is an update,
  1552 	 * If there is no update, just check for `email_exists`. If there is an update,
  1899 	 * check if current email and new email are the same, or not, and check `email_exists`
  1553 	 * check if current email and new email are the same, or not, and check `email_exists`
  1900 	 * accordingly.
  1554 	 * accordingly.
  1901 	 */
  1555 	 */
  1902 	if ( ( ! $update || ( ! empty( $old_user_data ) && $user_email !== $old_user_data->user_email ) )
  1556 	if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
  1903 		&& ! defined( 'WP_IMPORTING' )
  1557 		&& ! defined( 'WP_IMPORTING' )
  1904 		&& email_exists( $user_email )
  1558 		&& email_exists( $user_email )
  1905 	) {
  1559 	) {
  1906 		return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
  1560 		return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
  1907 	}
  1561 	}
  1908 	$nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
  1562 	$nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
  1909 
  1563 
  1910 	/**
  1564 	/**
  1911 	 * Filter a user's nickname before the user is created or updated.
  1565 	 * Filters a user's nickname before the user is created or updated.
  1912 	 *
  1566 	 *
  1913 	 * @since 2.0.3
  1567 	 * @since 2.0.3
  1914 	 *
  1568 	 *
  1915 	 * @param string $nickname The user's nickname.
  1569 	 * @param string $nickname The user's nickname.
  1916 	 */
  1570 	 */
  1917 	$meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
  1571 	$meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
  1918 
  1572 
  1919 	$first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
  1573 	$first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
  1920 
  1574 
  1921 	/**
  1575 	/**
  1922 	 * Filter a user's first name before the user is created or updated.
  1576 	 * Filters a user's first name before the user is created or updated.
  1923 	 *
  1577 	 *
  1924 	 * @since 2.0.3
  1578 	 * @since 2.0.3
  1925 	 *
  1579 	 *
  1926 	 * @param string $first_name The user's first name.
  1580 	 * @param string $first_name The user's first name.
  1927 	 */
  1581 	 */
  1928 	$meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
  1582 	$meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
  1929 
  1583 
  1930 	$last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
  1584 	$last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
  1931 
  1585 
  1932 	/**
  1586 	/**
  1933 	 * Filter a user's last name before the user is created or updated.
  1587 	 * Filters a user's last name before the user is created or updated.
  1934 	 *
  1588 	 *
  1935 	 * @since 2.0.3
  1589 	 * @since 2.0.3
  1936 	 *
  1590 	 *
  1937 	 * @param string $last_name The user's last name.
  1591 	 * @param string $last_name The user's last name.
  1938 	 */
  1592 	 */
  1954 	} else {
  1608 	} else {
  1955 		$display_name = $userdata['display_name'];
  1609 		$display_name = $userdata['display_name'];
  1956 	}
  1610 	}
  1957 
  1611 
  1958 	/**
  1612 	/**
  1959 	 * Filter a user's display name before the user is created or updated.
  1613 	 * Filters a user's display name before the user is created or updated.
  1960 	 *
  1614 	 *
  1961 	 * @since 2.0.3
  1615 	 * @since 2.0.3
  1962 	 *
  1616 	 *
  1963 	 * @param string $display_name The user's display name.
  1617 	 * @param string $display_name The user's display name.
  1964 	 */
  1618 	 */
  1965 	$display_name = apply_filters( 'pre_user_display_name', $display_name );
  1619 	$display_name = apply_filters( 'pre_user_display_name', $display_name );
  1966 
  1620 
  1967 	$description = empty( $userdata['description'] ) ? '' : $userdata['description'];
  1621 	$description = empty( $userdata['description'] ) ? '' : $userdata['description'];
  1968 
  1622 
  1969 	/**
  1623 	/**
  1970 	 * Filter a user's description before the user is created or updated.
  1624 	 * Filters a user's description before the user is created or updated.
  1971 	 *
  1625 	 *
  1972 	 * @since 2.0.3
  1626 	 * @since 2.0.3
  1973 	 *
  1627 	 *
  1974 	 * @param string $description The user's description.
  1628 	 * @param string $description The user's description.
  1975 	 */
  1629 	 */
  1976 	$meta['description'] = apply_filters( 'pre_user_description', $description );
  1630 	$meta['description'] = apply_filters( 'pre_user_description', $description );
  1977 
  1631 
  1978 	$meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
  1632 	$meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
  1979 
  1633 
  1980 	$meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) ? 'false' : $userdata['comment_shortcuts'];
  1634 	$meta['syntax_highlighting'] = empty( $userdata['syntax_highlighting'] ) ? 'true' : $userdata['syntax_highlighting'];
       
  1635 
       
  1636 	$meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true';
  1981 
  1637 
  1982 	$admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
  1638 	$admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
  1983 	$meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
  1639 	$meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
  1984 
  1640 
  1985 	$meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : $userdata['use_ssl'];
  1641 	$meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : $userdata['use_ssl'];
  1986 
  1642 
  1987 	$user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
  1643 	$user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
  1988 
  1644 
  1989 	$meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
  1645 	$meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
       
  1646 
       
  1647 	$meta['locale'] = isset( $userdata['locale'] ) ? $userdata['locale'] : '';
  1990 
  1648 
  1991 	$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));
  1649 	$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));
  1992 
  1650 
  1993 	if ( $user_nicename_check ) {
  1651 	if ( $user_nicename_check ) {
  1994 		$suffix = 2;
  1652 		$suffix = 2;
  1995 		while ($user_nicename_check) {
  1653 		while ($user_nicename_check) {
  1996 			$alt_user_nicename = $user_nicename . "-$suffix";
  1654 			// user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
       
  1655 			$base_length = 49 - mb_strlen( $suffix );
       
  1656 			$alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
  1997 			$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));
  1657 			$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));
  1998 			$suffix++;
  1658 			$suffix++;
  1999 		}
  1659 		}
  2000 		$user_nicename = $alt_user_nicename;
  1660 		$user_nicename = $alt_user_nicename;
  2001 	}
  1661 	}
  2002 
  1662 
  2003 	$compacted = compact( 'user_pass', 'user_email', 'user_url', 'user_nicename', 'display_name', 'user_registered' );
  1663 	$compacted = compact( 'user_pass', 'user_email', 'user_url', 'user_nicename', 'display_name', 'user_registered' );
  2004 	$data = wp_unslash( $compacted );
  1664 	$data = wp_unslash( $compacted );
       
  1665 
       
  1666 	if ( ! $update ) {
       
  1667 		$data = $data + compact( 'user_login' );
       
  1668 	}
       
  1669 
       
  1670 	/**
       
  1671 	 * Filters user data before the record is created or updated.
       
  1672 	 *
       
  1673 	 * It only includes data in the wp_users table wp_user, not any user metadata.
       
  1674 	 *
       
  1675 	 * @since 4.9.0
       
  1676 	 *
       
  1677 	 * @param array    $data {
       
  1678 	 *     Values and keys for the user.
       
  1679 	 *
       
  1680 	 *     @type string $user_login      The user's login. Only included if $update == false
       
  1681 	 *     @type string $user_pass       The user's password.
       
  1682 	 *     @type string $user_email      The user's email.
       
  1683 	 *     @type string $user_url        The user's url.
       
  1684 	 *     @type string $user_nicename   The user's nice name. Defaults to a URL-safe version of user's login
       
  1685 	 *     @type string $display_name    The user's display name.
       
  1686 	 *     @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to
       
  1687 	 *                                   the current UTC timestamp.
       
  1688 	 * }
       
  1689 	 * @param bool     $update Whether the user is being updated rather than created.
       
  1690 	 * @param int|null $id     ID of the user to be updated, or NULL if the user is being created.
       
  1691 	 */
       
  1692 	$data = apply_filters( 'wp_pre_insert_user_data', $data, $update, $update ? (int) $ID : null );
  2005 
  1693 
  2006 	if ( $update ) {
  1694 	if ( $update ) {
  2007 		if ( $user_email !== $old_user_data->user_email ) {
  1695 		if ( $user_email !== $old_user_data->user_email ) {
  2008 			$data['user_activation_key'] = '';
  1696 			$data['user_activation_key'] = '';
  2009 		}
  1697 		}
  2010 		$wpdb->update( $wpdb->users, $data, compact( 'ID' ) );
  1698 		$wpdb->update( $wpdb->users, $data, compact( 'ID' ) );
  2011 		$user_id = (int) $ID;
  1699 		$user_id = (int) $ID;
  2012 	} else {
  1700 	} else {
  2013 		$wpdb->insert( $wpdb->users, $data + compact( 'user_login' ) );
  1701 		$wpdb->insert( $wpdb->users, $data );
  2014 		$user_id = (int) $wpdb->insert_id;
  1702 		$user_id = (int) $wpdb->insert_id;
  2015 	}
  1703 	}
  2016 
  1704 
  2017 	$user = new WP_User( $user_id );
  1705 	$user = new WP_User( $user_id );
       
  1706 
       
  1707 	/**
       
  1708  	 * Filters a user's meta values and keys immediately after the user is created or updated
       
  1709  	 * and before any user meta is inserted or updated.
       
  1710  	 *
       
  1711  	 * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
       
  1712  	 *
       
  1713  	 * @since 4.4.0
       
  1714  	 *
       
  1715  	 * @param array $meta {
       
  1716  	 *     Default meta values and keys for the user.
       
  1717  	 *
       
  1718  	 *     @type string   $nickname             The user's nickname. Default is the user's username.
       
  1719 	 *     @type string   $first_name           The user's first name.
       
  1720 	 *     @type string   $last_name            The user's last name.
       
  1721 	 *     @type string   $description          The user's description.
       
  1722 	 *     @type bool     $rich_editing         Whether to enable the rich-editor for the user. False if not empty.
       
  1723 	 *     @type bool     $syntax_highlighting  Whether to enable the rich code editor for the user. False if not empty.
       
  1724 	 *     @type bool     $comment_shortcuts    Whether to enable keyboard shortcuts for the user. Default false.
       
  1725 	 *     @type string   $admin_color          The color scheme for a user's admin screen. Default 'fresh'.
       
  1726 	 *     @type int|bool $use_ssl              Whether to force SSL on the user's admin area. 0|false if SSL is
       
  1727 	 *                                          not forced.
       
  1728 	 *     @type bool     $show_admin_bar_front Whether to show the admin bar on the front end for the user.
       
  1729 	 *                                          Default true.
       
  1730  	 * }
       
  1731 	 * @param WP_User $user   User object.
       
  1732 	 * @param bool    $update Whether the user is being updated rather than created.
       
  1733  	 */
       
  1734 	$meta = apply_filters( 'insert_user_meta', $meta, $user, $update );
  2018 
  1735 
  2019 	// Update user meta.
  1736 	// Update user meta.
  2020 	foreach ( $meta as $key => $value ) {
  1737 	foreach ( $meta as $key => $value ) {
  2021 		update_user_meta( $user_id, $key, $value );
  1738 		update_user_meta( $user_id, $key, $value );
  2022 	}
  1739 	}
  2039 		/**
  1756 		/**
  2040 		 * Fires immediately after an existing user is updated.
  1757 		 * Fires immediately after an existing user is updated.
  2041 		 *
  1758 		 *
  2042 		 * @since 2.0.0
  1759 		 * @since 2.0.0
  2043 		 *
  1760 		 *
  2044 		 * @param int    $user_id       User ID.
  1761 		 * @param int     $user_id       User ID.
  2045 		 * @param object $old_user_data Object containing user's data prior to update.
  1762 		 * @param WP_User $old_user_data Object containing user's data prior to update.
  2046 		 */
  1763 		 */
  2047 		do_action( 'profile_update', $user_id, $old_user_data );
  1764 		do_action( 'profile_update', $user_id, $old_user_data );
  2048 	} else {
  1765 	} else {
  2049 		/**
  1766 		/**
  2050 		 * Fires immediately after a new user is registered.
  1767 		 * Fires immediately after a new user is registered.
  2070  *
  1787  *
  2071  * @since 2.0.0
  1788  * @since 2.0.0
  2072  *
  1789  *
  2073  * @see wp_insert_user() For what fields can be set in $userdata.
  1790  * @see wp_insert_user() For what fields can be set in $userdata.
  2074  *
  1791  *
  2075  * @param mixed $userdata An array of user data or a user object of type stdClass or WP_User.
  1792  * @param object|WP_User $userdata An array of user data or a user object of type stdClass or WP_User.
  2076  * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
  1793  * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
  2077  */
  1794  */
  2078 function wp_update_user($userdata) {
  1795 function wp_update_user($userdata) {
  2079 	if ( $userdata instanceof stdClass ) {
  1796 	if ( $userdata instanceof stdClass ) {
  2080 		$userdata = get_object_vars( $userdata );
  1797 		$userdata = get_object_vars( $userdata );
  2101 	}
  1818 	}
  2102 
  1819 
  2103 	// Escape data pulled from DB.
  1820 	// Escape data pulled from DB.
  2104 	$user = add_magic_quotes( $user );
  1821 	$user = add_magic_quotes( $user );
  2105 
  1822 
  2106 	// If password is changing, hash it now.
  1823 	if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) {
  2107 	if ( ! empty($userdata['user_pass']) ) {
  1824 		// If password is changing, hash it now
  2108 		$plaintext_pass = $userdata['user_pass'];
  1825 		$plaintext_pass = $userdata['user_pass'];
  2109 		$userdata['user_pass'] = wp_hash_password($userdata['user_pass']);
  1826 		$userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] );
  2110 	}
  1827 
  2111 
  1828 		/**
  2112 	wp_cache_delete($user[ 'user_email' ], 'useremail');
  1829 		 * Filters whether to send the password change email.
       
  1830 		 *
       
  1831 		 * @since 4.3.0
       
  1832 		 *
       
  1833 		 * @see wp_insert_user() For `$user` and `$userdata` fields.
       
  1834 		 *
       
  1835 		 * @param bool  $send     Whether to send the email.
       
  1836 		 * @param array $user     The original user array.
       
  1837 		 * @param array $userdata The updated user array.
       
  1838 		 *
       
  1839 		 */
       
  1840 		$send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata );
       
  1841 	}
       
  1842 
       
  1843 	if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
       
  1844 		/**
       
  1845 		 * Filters whether to send the email change email.
       
  1846 		 *
       
  1847 		 * @since 4.3.0
       
  1848 		 *
       
  1849 		 * @see wp_insert_user() For `$user` and `$userdata` fields.
       
  1850 		 *
       
  1851 		 * @param bool  $send     Whether to send the email.
       
  1852 		 * @param array $user     The original user array.
       
  1853 		 * @param array $userdata The updated user array.
       
  1854 		 *
       
  1855 		 */
       
  1856 		$send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata );
       
  1857 	}
       
  1858 
       
  1859 	wp_cache_delete( $user['user_email'], 'useremail' );
       
  1860 	wp_cache_delete( $user['user_nicename'], 'userslugs' );
  2113 
  1861 
  2114 	// Merge old and new fields with new fields overwriting old ones.
  1862 	// Merge old and new fields with new fields overwriting old ones.
  2115 	$userdata = array_merge($user, $userdata);
  1863 	$userdata = array_merge( $user, $userdata );
  2116 	$user_id = wp_insert_user($userdata);
  1864 	$user_id = wp_insert_user( $userdata );
       
  1865 
       
  1866 	if ( ! is_wp_error( $user_id ) ) {
       
  1867 
       
  1868 		$blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
       
  1869 
       
  1870 		$switched_locale = false;
       
  1871 		if ( ! empty( $send_password_change_email ) || ! empty( $send_email_change_email ) ) {
       
  1872 			$switched_locale = switch_to_locale( get_user_locale( $user_id ) );
       
  1873 		}
       
  1874 
       
  1875 		if ( ! empty( $send_password_change_email ) ) {
       
  1876 			/* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
       
  1877 			$pass_change_text = __( 'Hi ###USERNAME###,
       
  1878 
       
  1879 This notice confirms that your password was changed on ###SITENAME###.
       
  1880 
       
  1881 If you did not change your password, please contact the Site Administrator at
       
  1882 ###ADMIN_EMAIL###
       
  1883 
       
  1884 This email has been sent to ###EMAIL###
       
  1885 
       
  1886 Regards,
       
  1887 All at ###SITENAME###
       
  1888 ###SITEURL###' );
       
  1889 
       
  1890 			$pass_change_email = array(
       
  1891 				'to'      => $user['user_email'],
       
  1892 				/* translators: User password change notification email subject. 1: Site name */
       
  1893 				'subject' => __( '[%s] Notice of Password Change' ),
       
  1894 				'message' => $pass_change_text,
       
  1895 				'headers' => '',
       
  1896 			);
       
  1897 
       
  1898 			/**
       
  1899 			 * Filters the contents of the email sent when the user's password is changed.
       
  1900 			 *
       
  1901 			 * @since 4.3.0
       
  1902 			 *
       
  1903 			 * @param array $pass_change_email {
       
  1904 			 *            Used to build wp_mail().
       
  1905 			 *            @type string $to      The intended recipients. Add emails in a comma separated string.
       
  1906 			 *            @type string $subject The subject of the email.
       
  1907 			 *            @type string $message The content of the email.
       
  1908 			 *                The following strings have a special meaning and will get replaced dynamically:
       
  1909 			 *                - ###USERNAME###    The current user's username.
       
  1910 			 *                - ###ADMIN_EMAIL### The admin email in case this was unexpected.
       
  1911 			 *                - ###EMAIL###       The user's email address.
       
  1912 			 *                - ###SITENAME###    The name of the site.
       
  1913 			 *                - ###SITEURL###     The URL to the site.
       
  1914 			 *            @type string $headers Headers. Add headers in a newline (\r\n) separated string.
       
  1915 			 *        }
       
  1916 			 * @param array $user     The original user array.
       
  1917 			 * @param array $userdata The updated user array.
       
  1918 			 *
       
  1919 			 */
       
  1920 			$pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata );
       
  1921 
       
  1922 			$pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] );
       
  1923 			$pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] );
       
  1924 			$pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] );
       
  1925 			$pass_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $pass_change_email['message'] );
       
  1926 			$pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] );
       
  1927 
       
  1928 			wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] );
       
  1929 		}
       
  1930 
       
  1931 		if ( ! empty( $send_email_change_email ) ) {
       
  1932 			/* translators: Do not translate USERNAME, ADMIN_EMAIL, NEW_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
       
  1933 			$email_change_text = __( 'Hi ###USERNAME###,
       
  1934 
       
  1935 This notice confirms that your email address on ###SITENAME### was changed to ###NEW_EMAIL###.
       
  1936 
       
  1937 If you did not change your email, please contact the Site Administrator at
       
  1938 ###ADMIN_EMAIL###
       
  1939 
       
  1940 This email has been sent to ###EMAIL###
       
  1941 
       
  1942 Regards,
       
  1943 All at ###SITENAME###
       
  1944 ###SITEURL###' );
       
  1945 
       
  1946 			$email_change_email = array(
       
  1947 				'to'      => $user['user_email'],
       
  1948 				/* translators: User email change notification email subject. 1: Site name */
       
  1949 				'subject' => __( '[%s] Notice of Email Change' ),
       
  1950 				'message' => $email_change_text,
       
  1951 				'headers' => '',
       
  1952 			);
       
  1953 
       
  1954 			/**
       
  1955 			 * Filters the contents of the email sent when the user's email is changed.
       
  1956 			 *
       
  1957 			 * @since 4.3.0
       
  1958 			 *
       
  1959 			 * @param array $email_change_email {
       
  1960 			 *            Used to build wp_mail().
       
  1961 			 *            @type string $to      The intended recipients.
       
  1962 			 *            @type string $subject The subject of the email.
       
  1963 			 *            @type string $message The content of the email.
       
  1964 			 *                The following strings have a special meaning and will get replaced dynamically:
       
  1965 			 *                - ###USERNAME###    The current user's username.
       
  1966 			 *                - ###ADMIN_EMAIL### The admin email in case this was unexpected.
       
  1967 			 *                - ###NEW_EMAIL###   The new email address.
       
  1968 			 *                - ###EMAIL###       The old email address.
       
  1969 			 *                - ###SITENAME###    The name of the site.
       
  1970 			 *                - ###SITEURL###     The URL to the site.
       
  1971 			 *            @type string $headers Headers.
       
  1972 			 *        }
       
  1973 			 * @param array $user The original user array.
       
  1974 			 * @param array $userdata The updated user array.
       
  1975 			 */
       
  1976 			$email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata );
       
  1977 
       
  1978 			$email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] );
       
  1979 			$email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] );
       
  1980 			$email_change_email['message'] = str_replace( '###NEW_EMAIL###', $userdata['user_email'], $email_change_email['message'] );
       
  1981 			$email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] );
       
  1982 			$email_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $email_change_email['message'] );
       
  1983 			$email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
       
  1984 
       
  1985 			wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] );
       
  1986 		}
       
  1987 
       
  1988 		if ( $switched_locale ) {
       
  1989 			restore_previous_locale();
       
  1990 		}
       
  1991 	}
  2117 
  1992 
  2118 	// Update the cookies if the password changed.
  1993 	// Update the cookies if the password changed.
  2119 	$current_user = wp_get_current_user();
  1994 	$current_user = wp_get_current_user();
  2120 	if ( $current_user->ID == $ID ) {
  1995 	if ( $current_user->ID == $ID ) {
  2121 		if ( isset($plaintext_pass) ) {
  1996 		if ( isset($plaintext_pass) ) {
  2137 
  2012 
  2138 /**
  2013 /**
  2139  * A simpler way of inserting a user into the database.
  2014  * A simpler way of inserting a user into the database.
  2140  *
  2015  *
  2141  * Creates a new user with just the username, password, and email. For more
  2016  * Creates a new user with just the username, password, and email. For more
  2142  * complex user creation use {@see wp_insert_user()} to specify more information.
  2017  * complex user creation use wp_insert_user() to specify more information.
  2143  *
  2018  *
  2144  * @since 2.0.0
  2019  * @since 2.0.0
  2145  * @see wp_insert_user() More complete way to create a new user
  2020  * @see wp_insert_user() More complete way to create a new user
  2146  *
  2021  *
  2147  * @param string $username The user's username.
  2022  * @param string $username The user's username.
  2148  * @param string $password The user's password.
  2023  * @param string $password The user's password.
  2149  * @param string $email    Optional. The user's email. Default empty.
  2024  * @param string $email    Optional. The user's email. Default empty.
  2150  * @return int The new user's ID.
  2025  * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
       
  2026  *                      be created.
  2151  */
  2027  */
  2152 function wp_create_user($username, $password, $email = '') {
  2028 function wp_create_user($username, $password, $email = '') {
  2153 	$user_login = wp_slash( $username );
  2029 	$user_login = wp_slash( $username );
  2154 	$user_email = wp_slash( $email    );
  2030 	$user_email = wp_slash( $email    );
  2155 	$user_pass = $password;
  2031 	$user_pass = $password;
  2157 	$userdata = compact('user_login', 'user_email', 'user_pass');
  2033 	$userdata = compact('user_login', 'user_email', 'user_pass');
  2158 	return wp_insert_user($userdata);
  2034 	return wp_insert_user($userdata);
  2159 }
  2035 }
  2160 
  2036 
  2161 /**
  2037 /**
  2162  * Return a list of meta keys that wp_insert_user() is supposed to set.
  2038  * Returns a list of meta keys to be (maybe) populated in wp_update_user().
       
  2039  *
       
  2040  * The list of keys returned via this function are dependent on the presence
       
  2041  * of those keys in the user meta data to be set.
  2163  *
  2042  *
  2164  * @since 3.3.0
  2043  * @since 3.3.0
  2165  * @access private
  2044  * @access private
  2166  *
  2045  *
  2167  * @param WP_User $user WP_User instance.
  2046  * @param WP_User $user WP_User instance.
  2168  * @return array
  2047  * @return array List of user keys to be populated in wp_update_user().
  2169  */
  2048  */
  2170 function _get_additional_user_keys( $user ) {
  2049 function _get_additional_user_keys( $user ) {
  2171 	$keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front' );
  2050 	$keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' );
  2172 	return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
  2051 	return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
  2173 }
  2052 }
  2174 
  2053 
  2175 /**
  2054 /**
  2176  * Set up the user contact methods.
  2055  * Set up the user contact methods.
  2191 			'jabber' => __( 'Jabber / Google Talk' )
  2070 			'jabber' => __( 'Jabber / Google Talk' )
  2192 		);
  2071 		);
  2193 	}
  2072 	}
  2194 
  2073 
  2195 	/**
  2074 	/**
  2196 	 * Filter the user contact methods.
  2075 	 * Filters the user contact methods.
  2197 	 *
  2076 	 *
  2198 	 * @since 2.9.0
  2077 	 * @since 2.9.0
  2199 	 *
  2078 	 *
  2200 	 * @param array   $methods Array of contact methods and their labels.
  2079 	 * @param array   $methods Array of contact methods and their labels.
  2201  	 * @param WP_User $user    WP_User object.
  2080  	 * @param WP_User $user    WP_User object.
  2204 }
  2083 }
  2205 
  2084 
  2206 /**
  2085 /**
  2207  * The old private function for setting up user contact methods.
  2086  * The old private function for setting up user contact methods.
  2208  *
  2087  *
       
  2088  * Use wp_get_user_contact_methods() instead.
       
  2089  *
  2209  * @since 2.9.0
  2090  * @since 2.9.0
  2210  * @access private
  2091  * @access private
       
  2092  *
       
  2093  * @param WP_User $user Optional. WP_User object. Default null.
       
  2094  * @return array Array of contact methods and their labels.
  2211  */
  2095  */
  2212 function _wp_get_user_contactmethods( $user = null ) {
  2096 function _wp_get_user_contactmethods( $user = null ) {
  2213 	return wp_get_user_contact_methods( $user );
  2097 	return wp_get_user_contact_methods( $user );
  2214 }
  2098 }
  2215 
  2099 
  2219  * @since 4.1.0
  2103  * @since 4.1.0
  2220  *
  2104  *
  2221  * @return string The password hint text.
  2105  * @return string The password hint text.
  2222  */
  2106  */
  2223 function wp_get_password_hint() {
  2107 function wp_get_password_hint() {
  2224 	$hint = __( 'Hint: The password should be at least seven characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ &amp; ).' );
  2108 	$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 ! " ? $ % ^ &amp; ).' );
  2225 
  2109 
  2226 	/**
  2110 	/**
  2227 	 * Filter the text describing the site's password complexity policy.
  2111 	 * Filters the text describing the site's password complexity policy.
  2228 	 *
  2112 	 *
  2229 	 * @since 4.1.0
  2113 	 * @since 4.1.0
  2230 	 *
  2114 	 *
  2231 	 * @param string $hint The password hint text.
  2115 	 * @param string $hint The password hint text.
  2232 	 */
  2116 	 */
  2233 	return apply_filters( 'password_hint', $hint );
  2117 	return apply_filters( 'password_hint', $hint );
       
  2118 }
       
  2119 
       
  2120 /**
       
  2121  * Creates, stores, then returns a password reset key for user.
       
  2122  *
       
  2123  * @since 4.4.0
       
  2124  *
       
  2125  * @global wpdb         $wpdb      WordPress database abstraction object.
       
  2126  * @global PasswordHash $wp_hasher Portable PHP password hashing framework.
       
  2127  *
       
  2128  * @param WP_User $user User to retrieve password reset key for.
       
  2129  *
       
  2130  * @return string|WP_Error Password reset key on success. WP_Error on error.
       
  2131  */
       
  2132 function get_password_reset_key( $user ) {
       
  2133 	global $wpdb, $wp_hasher;
       
  2134 
       
  2135 	/**
       
  2136 	 * Fires before a new password is retrieved.
       
  2137 	 *
       
  2138 	 * Use the {@see 'retrieve_password'} hook instead.
       
  2139 	 *
       
  2140 	 * @since 1.5.0
       
  2141 	 * @deprecated 1.5.1 Misspelled. Use 'retrieve_password' hook instead.
       
  2142 	 *
       
  2143 	 * @param string $user_login The user login name.
       
  2144 	 */
       
  2145 	do_action( 'retreive_password', $user->user_login );
       
  2146 
       
  2147 	/**
       
  2148 	 * Fires before a new password is retrieved.
       
  2149 	 *
       
  2150 	 * @since 1.5.1
       
  2151 	 *
       
  2152 	 * @param string $user_login The user login name.
       
  2153 	 */
       
  2154 	do_action( 'retrieve_password', $user->user_login );
       
  2155 
       
  2156 	$allow = true;
       
  2157 	if ( is_multisite() && is_user_spammy( $user ) ) {
       
  2158 		$allow = false;
       
  2159 	}
       
  2160 
       
  2161 	/**
       
  2162 	 * Filters whether to allow a password to be reset.
       
  2163 	 *
       
  2164 	 * @since 2.7.0
       
  2165 	 *
       
  2166 	 * @param bool $allow         Whether to allow the password to be reset. Default true.
       
  2167 	 * @param int  $user_data->ID The ID of the user attempting to reset a password.
       
  2168 	 */
       
  2169 	$allow = apply_filters( 'allow_password_reset', $allow, $user->ID );
       
  2170 
       
  2171 	if ( ! $allow ) {
       
  2172 		return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
       
  2173 	} elseif ( is_wp_error( $allow ) ) {
       
  2174 		return $allow;
       
  2175 	}
       
  2176 
       
  2177 	// Generate something random for a password reset key.
       
  2178 	$key = wp_generate_password( 20, false );
       
  2179 
       
  2180 	/**
       
  2181 	 * Fires when a password reset key is generated.
       
  2182 	 *
       
  2183 	 * @since 2.5.0
       
  2184 	 *
       
  2185 	 * @param string $user_login The username for the user.
       
  2186 	 * @param string $key        The generated password reset key.
       
  2187 	 */
       
  2188 	do_action( 'retrieve_password_key', $user->user_login, $key );
       
  2189 
       
  2190 	// Now insert the key, hashed, into the DB.
       
  2191 	if ( empty( $wp_hasher ) ) {
       
  2192 		require_once ABSPATH . WPINC . '/class-phpass.php';
       
  2193 		$wp_hasher = new PasswordHash( 8, true );
       
  2194 	}
       
  2195 	$hashed = time() . ':' . $wp_hasher->HashPassword( $key );
       
  2196 	$key_saved = $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) );
       
  2197 	if ( false === $key_saved ) {
       
  2198 		return new WP_Error( 'no_password_key_update', __( 'Could not save password reset key to database.' ) );
       
  2199 	}
       
  2200 
       
  2201 	return $key;
  2234 }
  2202 }
  2235 
  2203 
  2236 /**
  2204 /**
  2237  * Retrieves a user row based on password reset key and login
  2205  * Retrieves a user row based on password reset key and login
  2238  *
  2206  *
  2239  * A key is considered 'expired' if it exactly matches the value of the
  2207  * A key is considered 'expired' if it exactly matches the value of the
  2240  * user_activation_key field, rather than being matched after going through the
  2208  * user_activation_key field, rather than being matched after going through the
  2241  * hashing process. This field is now hashed; old values are no longer accepted
  2209  * hashing process. This field is now hashed; old values are no longer accepted
  2242  * but have a different WP_Error code so good user feedback can be provided.
  2210  * but have a different WP_Error code so good user feedback can be provided.
  2243  *
  2211  *
       
  2212  * @since 3.1.0
       
  2213  *
  2244  * @global wpdb         $wpdb      WordPress database object for queries.
  2214  * @global wpdb         $wpdb      WordPress database object for queries.
  2245  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
  2215  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
  2246  *
  2216  *
  2247  * @param string $key       Hash to validate sending user's password.
  2217  * @param string $key       Hash to validate sending user's password.
  2248  * @param string $login     The user login.
  2218  * @param string $login     The user login.
  2266 	if ( empty( $wp_hasher ) ) {
  2236 	if ( empty( $wp_hasher ) ) {
  2267 		require_once ABSPATH . WPINC . '/class-phpass.php';
  2237 		require_once ABSPATH . WPINC . '/class-phpass.php';
  2268 		$wp_hasher = new PasswordHash( 8, true );
  2238 		$wp_hasher = new PasswordHash( 8, true );
  2269 	}
  2239 	}
  2270 
  2240 
  2271 	if ( $wp_hasher->CheckPassword( $key, $row->user_activation_key ) )
  2241 	/**
       
  2242 	 * Filters the expiration time of password reset keys.
       
  2243 	 *
       
  2244 	 * @since 4.3.0
       
  2245 	 *
       
  2246 	 * @param int $expiration The expiration time in seconds.
       
  2247 	 */
       
  2248 	$expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS );
       
  2249 
       
  2250 	if ( false !== strpos( $row->user_activation_key, ':' ) ) {
       
  2251 		list( $pass_request_time, $pass_key ) = explode( ':', $row->user_activation_key, 2 );
       
  2252 		$expiration_time = $pass_request_time + $expiration_duration;
       
  2253 	} else {
       
  2254 		$pass_key = $row->user_activation_key;
       
  2255 		$expiration_time = false;
       
  2256 	}
       
  2257 
       
  2258 	if ( ! $pass_key ) {
       
  2259 		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
       
  2260 	}
       
  2261 
       
  2262 	$hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key );
       
  2263 
       
  2264 	if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
  2272 		return get_userdata( $row->ID );
  2265 		return get_userdata( $row->ID );
  2273 
  2266 	} elseif ( $hash_is_correct && $expiration_time ) {
  2274 	if ( $key === $row->user_activation_key ) {
  2267 		// Key has an expiration time that's passed
       
  2268 		return new WP_Error( 'expired_key', __( 'Invalid key' ) );
       
  2269 	}
       
  2270 
       
  2271 	if ( hash_equals( $row->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) {
  2275 		$return = new WP_Error( 'expired_key', __( 'Invalid key' ) );
  2272 		$return = new WP_Error( 'expired_key', __( 'Invalid key' ) );
  2276 		$user_id = $row->ID;
  2273 		$user_id = $row->ID;
  2277 
  2274 
  2278 		/**
  2275 		/**
  2279 		 * Filter the return value of check_password_reset_key() when an
  2276 		 * Filters the return value of check_password_reset_key() when an
  2280 		 * old-style key is used (plain-text key was stored in the database).
  2277 		 * old-style key is used.
  2281 		 *
  2278 		 *
  2282 		 * @since 3.7.0
  2279 		 * @since 3.7.0 Previously plain-text keys were stored in the database.
       
  2280 		 * @since 4.3.0 Previously key hashes were stored without an expiration time.
  2283 		 *
  2281 		 *
  2284 		 * @param WP_Error $return  A WP_Error object denoting an expired key.
  2282 		 * @param WP_Error $return  A WP_Error object denoting an expired key.
  2285 		 *                          Return a WP_User object to validate the key.
  2283 		 *                          Return a WP_User object to validate the key.
  2286 		 * @param int      $user_id The matched user ID.
  2284 		 * @param int      $user_id The matched user ID.
  2287 		 */
  2285 		 */
  2292 }
  2290 }
  2293 
  2291 
  2294 /**
  2292 /**
  2295  * Handles resetting the user's password.
  2293  * Handles resetting the user's password.
  2296  *
  2294  *
  2297  * @param object $user The user
  2295  * @since 2.5.0
       
  2296  *
       
  2297  * @param WP_User $user     The user
  2298  * @param string $new_pass New password for the user in plaintext
  2298  * @param string $new_pass New password for the user in plaintext
  2299  */
  2299  */
  2300 function reset_password( $user, $new_pass ) {
  2300 function reset_password( $user, $new_pass ) {
  2301 	/**
  2301 	/**
  2302 	 * Fires before the user's password is reset.
  2302 	 * Fires before the user's password is reset.
  2309 	do_action( 'password_reset', $user, $new_pass );
  2309 	do_action( 'password_reset', $user, $new_pass );
  2310 
  2310 
  2311 	wp_set_password( $new_pass, $user->ID );
  2311 	wp_set_password( $new_pass, $user->ID );
  2312 	update_user_option( $user->ID, 'default_password_nag', false, true );
  2312 	update_user_option( $user->ID, 'default_password_nag', false, true );
  2313 
  2313 
  2314 	wp_password_change_notification( $user );
  2314 	/**
       
  2315 	 * Fires after the user's password is reset.
       
  2316 	 *
       
  2317 	 * @since 4.4.0
       
  2318 	 *
       
  2319 	 * @param WP_User $user     The user.
       
  2320 	 * @param string  $new_pass New user password.
       
  2321 	 */
       
  2322 	do_action( 'after_password_reset', $user, $new_pass );
  2315 }
  2323 }
  2316 
  2324 
  2317 /**
  2325 /**
  2318  * Handles registering a new user.
  2326  * Handles registering a new user.
       
  2327  *
       
  2328  * @since 2.5.0
  2319  *
  2329  *
  2320  * @param string $user_login User's username for logging in
  2330  * @param string $user_login User's username for logging in
  2321  * @param string $user_email User's email address to send password and add
  2331  * @param string $user_email User's email address to send password and add
  2322  * @return int|WP_Error Either user's ID or error on failure.
  2332  * @return int|WP_Error Either user's ID or error on failure.
  2323  */
  2333  */
  2324 function register_new_user( $user_login, $user_email ) {
  2334 function register_new_user( $user_login, $user_email ) {
  2325 	$errors = new WP_Error();
  2335 	$errors = new WP_Error();
  2326 
  2336 
  2327 	$sanitized_user_login = sanitize_user( $user_login );
  2337 	$sanitized_user_login = sanitize_user( $user_login );
  2328 	/**
  2338 	/**
  2329 	 * Filter the email address of a user being registered.
  2339 	 * Filters the email address of a user being registered.
  2330 	 *
  2340 	 *
  2331 	 * @since 2.1.0
  2341 	 * @since 2.1.0
  2332 	 *
  2342 	 *
  2333 	 * @param string $user_email The email address of the new user.
  2343 	 * @param string $user_email The email address of the new user.
  2334 	 */
  2344 	 */
  2340 	} elseif ( ! validate_username( $user_login ) ) {
  2350 	} elseif ( ! validate_username( $user_login ) ) {
  2341 		$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
  2351 		$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
  2342 		$sanitized_user_login = '';
  2352 		$sanitized_user_login = '';
  2343 	} elseif ( username_exists( $sanitized_user_login ) ) {
  2353 	} elseif ( username_exists( $sanitized_user_login ) ) {
  2344 		$errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
  2354 		$errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
  2345 	}
  2355 
  2346 
  2356 	} else {
  2347 	// Check the e-mail address
  2357 		/** This filter is documented in wp-includes/user.php */
       
  2358 		$illegal_user_logins = array_map( 'strtolower', (array) apply_filters( 'illegal_user_logins', array() ) );
       
  2359 		if ( in_array( strtolower( $sanitized_user_login ), $illegal_user_logins ) ) {
       
  2360 			$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: Sorry, that username is not allowed.' ) );
       
  2361 		}
       
  2362 	}
       
  2363 
       
  2364 	// Check the email address
  2348 	if ( $user_email == '' ) {
  2365 	if ( $user_email == '' ) {
  2349 		$errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your e-mail address.' ) );
  2366 		$errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
  2350 	} elseif ( ! is_email( $user_email ) ) {
  2367 	} elseif ( ! is_email( $user_email ) ) {
  2351 		$errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn&#8217;t correct.' ) );
  2368 		$errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn&#8217;t correct.' ) );
  2352 		$user_email = '';
  2369 		$user_email = '';
  2353 	} elseif ( email_exists( $user_email ) ) {
  2370 	} elseif ( email_exists( $user_email ) ) {
  2354 		$errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
  2371 		$errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
  2366 	 *                                       or an existing username or email.
  2383 	 *                                       or an existing username or email.
  2367 	 */
  2384 	 */
  2368 	do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
  2385 	do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
  2369 
  2386 
  2370 	/**
  2387 	/**
  2371 	 * Filter the errors encountered when a new user is being registered.
  2388 	 * Filters the errors encountered when a new user is being registered.
  2372 	 *
  2389 	 *
  2373 	 * The filtered WP_Error object may, for example, contain errors for an invalid
  2390 	 * The filtered WP_Error object may, for example, contain errors for an invalid
  2374 	 * or existing username or email address. A WP_Error object should always returned,
  2391 	 * or existing username or email address. A WP_Error object should always returned,
  2375 	 * but may or may not contain errors.
  2392 	 * but may or may not contain errors.
  2376 	 *
  2393 	 *
  2395 		return $errors;
  2412 		return $errors;
  2396 	}
  2413 	}
  2397 
  2414 
  2398 	update_user_option( $user_id, 'default_password_nag', true, true ); //Set up the Password change nag.
  2415 	update_user_option( $user_id, 'default_password_nag', true, true ); //Set up the Password change nag.
  2399 
  2416 
  2400 	wp_new_user_notification( $user_id, $user_pass );
  2417 	/**
       
  2418 	 * Fires after a new user registration has been recorded.
       
  2419 	 *
       
  2420 	 * @since 4.4.0
       
  2421 	 *
       
  2422 	 * @param int $user_id ID of the newly registered user.
       
  2423 	 */
       
  2424 	do_action( 'register_new_user', $user_id );
  2401 
  2425 
  2402 	return $user_id;
  2426 	return $user_id;
       
  2427 }
       
  2428 
       
  2429 /**
       
  2430  * Initiates email notifications related to the creation of new users.
       
  2431  *
       
  2432  * Notifications are sent both to the site admin and to the newly created user.
       
  2433  *
       
  2434  * @since 4.4.0
       
  2435  * @since 4.6.0 Converted the `$notify` parameter to accept 'user' for sending
       
  2436  *              notifications only to the user created.
       
  2437  *
       
  2438  * @param int    $user_id ID of the newly created user.
       
  2439  * @param string $notify  Optional. Type of notification that should happen. Accepts 'admin'
       
  2440  *                        or an empty string (admin only), 'user', or 'both' (admin and user).
       
  2441  *                        Default 'both'.
       
  2442  */
       
  2443 function wp_send_new_user_notifications( $user_id, $notify = 'both' ) {
       
  2444 	wp_new_user_notification( $user_id, null, $notify );
  2403 }
  2445 }
  2404 
  2446 
  2405 /**
  2447 /**
  2406  * Retrieve the current session token from the logged_in cookie.
  2448  * Retrieve the current session token from the logged_in cookie.
  2407  *
  2449  *
  2458  */
  2500  */
  2459 function wp_destroy_all_sessions() {
  2501 function wp_destroy_all_sessions() {
  2460 	$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
  2502 	$manager = WP_Session_Tokens::get_instance( get_current_user_id() );
  2461 	$manager->destroy_all();
  2503 	$manager->destroy_all();
  2462 }
  2504 }
       
  2505 
       
  2506 /**
       
  2507  * Get the user IDs of all users with no role on this site.
       
  2508  *
       
  2509  * @since 4.4.0
       
  2510  * @since 4.9.0 The `$site_id` parameter was added to support multisite.
       
  2511  *
       
  2512  * @param int|null $site_id Optional. The site ID to get users with no role for. Defaults to the current site.
       
  2513  * @return array Array of user IDs.
       
  2514  */
       
  2515 function wp_get_users_with_no_role( $site_id = null ) {
       
  2516 	global $wpdb;
       
  2517 
       
  2518 	if ( ! $site_id ) {
       
  2519 		$site_id = get_current_blog_id();
       
  2520 	}
       
  2521 
       
  2522 	$prefix = $wpdb->get_blog_prefix( $site_id );
       
  2523 
       
  2524 	if ( is_multisite() && $site_id != get_current_blog_id() ) {
       
  2525 		switch_to_blog( $site_id );
       
  2526 		$role_names = wp_roles()->get_names();
       
  2527 		restore_current_blog();
       
  2528 	} else {
       
  2529 		$role_names = wp_roles()->get_names();
       
  2530 	}
       
  2531 
       
  2532 	$regex  = implode( '|', array_keys( $role_names ) );
       
  2533 	$regex  = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex );
       
  2534 	$users  = $wpdb->get_col( $wpdb->prepare( "
       
  2535 		SELECT user_id
       
  2536 		FROM $wpdb->usermeta
       
  2537 		WHERE meta_key = '{$prefix}capabilities'
       
  2538 		AND meta_value NOT REGEXP %s
       
  2539 	", $regex ) );
       
  2540 
       
  2541 	return $users;
       
  2542 }
       
  2543 
       
  2544 /**
       
  2545  * Retrieves the current user object.
       
  2546  *
       
  2547  * Will set the current user, if the current user is not set. The current user
       
  2548  * will be set to the logged-in person. If no user is logged-in, then it will
       
  2549  * set the current user to 0, which is invalid and won't have any permissions.
       
  2550  *
       
  2551  * This function is used by the pluggable functions wp_get_current_user() and
       
  2552  * get_currentuserinfo(), the latter of which is deprecated but used for backward
       
  2553  * compatibility.
       
  2554  *
       
  2555  * @since 4.5.0
       
  2556  * @access private
       
  2557  *
       
  2558  * @see wp_get_current_user()
       
  2559  * @global WP_User $current_user Checks if the current user is set.
       
  2560  *
       
  2561  * @return WP_User Current WP_User instance.
       
  2562  */
       
  2563 function _wp_get_current_user() {
       
  2564 	global $current_user;
       
  2565 
       
  2566 	if ( ! empty( $current_user ) ) {
       
  2567 		if ( $current_user instanceof WP_User ) {
       
  2568 			return $current_user;
       
  2569 		}
       
  2570 
       
  2571 		// Upgrade stdClass to WP_User
       
  2572 		if ( is_object( $current_user ) && isset( $current_user->ID ) ) {
       
  2573 			$cur_id = $current_user->ID;
       
  2574 			$current_user = null;
       
  2575 			wp_set_current_user( $cur_id );
       
  2576 			return $current_user;
       
  2577 		}
       
  2578 
       
  2579 		// $current_user has a junk value. Force to WP_User with ID 0.
       
  2580 		$current_user = null;
       
  2581 		wp_set_current_user( 0 );
       
  2582 		return $current_user;
       
  2583 	}
       
  2584 
       
  2585 	if ( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ) {
       
  2586 		wp_set_current_user( 0 );
       
  2587 		return $current_user;
       
  2588 	}
       
  2589 
       
  2590 	/**
       
  2591 	 * Filters the current user.
       
  2592 	 *
       
  2593 	 * The default filters use this to determine the current user from the
       
  2594 	 * request's cookies, if available.
       
  2595 	 *
       
  2596 	 * Returning a value of false will effectively short-circuit setting
       
  2597 	 * the current user.
       
  2598 	 *
       
  2599 	 * @since 3.9.0
       
  2600 	 *
       
  2601 	 * @param int|bool $user_id User ID if one has been determined, false otherwise.
       
  2602 	 */
       
  2603 	$user_id = apply_filters( 'determine_current_user', false );
       
  2604 	if ( ! $user_id ) {
       
  2605 		wp_set_current_user( 0 );
       
  2606 		return $current_user;
       
  2607 	}
       
  2608 
       
  2609 	wp_set_current_user( $user_id );
       
  2610 
       
  2611 	return $current_user;
       
  2612 }
       
  2613 
       
  2614 /**
       
  2615  * Send a confirmation request email when a change of user email address is attempted.
       
  2616  *
       
  2617  * @since 3.0.0
       
  2618  * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
       
  2619  *
       
  2620  * @global WP_Error $errors WP_Error object.
       
  2621  * @global wpdb     $wpdb   WordPress database object.
       
  2622  */
       
  2623 function send_confirmation_on_profile_email() {
       
  2624 	global $errors, $wpdb;
       
  2625 
       
  2626 	$current_user = wp_get_current_user();
       
  2627 	if ( ! is_object( $errors ) ) {
       
  2628 		$errors = new WP_Error();
       
  2629 	}
       
  2630 
       
  2631 	if ( $current_user->ID != $_POST['user_id'] ) {
       
  2632 		return false;
       
  2633 	}
       
  2634 
       
  2635 	if ( $current_user->user_email != $_POST['email'] ) {
       
  2636 		if ( ! is_email( $_POST['email'] ) ) {
       
  2637 			$errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address isn&#8217;t correct." ), array(
       
  2638 				'form-field' => 'email',
       
  2639 			) );
       
  2640 
       
  2641 			return;
       
  2642 		}
       
  2643 
       
  2644 		if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_email FROM {$wpdb->users} WHERE user_email=%s", $_POST['email'] ) ) ) {
       
  2645 			$errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address is already used." ), array(
       
  2646 				'form-field' => 'email',
       
  2647 			) );
       
  2648 			delete_user_meta( $current_user->ID, '_new_email' );
       
  2649 
       
  2650 			return;
       
  2651 		}
       
  2652 
       
  2653 		$hash           = md5( $_POST['email'] . time() . wp_rand() );
       
  2654 		$new_user_email = array(
       
  2655 			'hash'     => $hash,
       
  2656 			'newemail' => $_POST['email'],
       
  2657 		);
       
  2658 		update_user_meta( $current_user->ID, '_new_email', $new_user_email );
       
  2659 
       
  2660 		$sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
       
  2661 
       
  2662 		/* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
       
  2663 		$email_text = __( 'Howdy ###USERNAME###,
       
  2664 
       
  2665 You recently requested to have the email address on your account changed.
       
  2666 
       
  2667 If this is correct, please click on the following link to change it:
       
  2668 ###ADMIN_URL###
       
  2669 
       
  2670 You can safely ignore and delete this email if you do not want to
       
  2671 take this action.
       
  2672 
       
  2673 This email has been sent to ###EMAIL###
       
  2674 
       
  2675 Regards,
       
  2676 All at ###SITENAME###
       
  2677 ###SITEURL###' );
       
  2678 
       
  2679 		/**
       
  2680 		 * Filters the text of the email sent when a change of user email address is attempted.
       
  2681 		 *
       
  2682 		 * The following strings have a special meaning and will get replaced dynamically:
       
  2683 		 * ###USERNAME###  The current user's username.
       
  2684 		 * ###ADMIN_URL### The link to click on to confirm the email change.
       
  2685 		 * ###EMAIL###     The new email.
       
  2686 		 * ###SITENAME###  The name of the site.
       
  2687 		 * ###SITEURL###   The URL to the site.
       
  2688 		 *
       
  2689 		 * @since MU (3.0.0)
       
  2690 		 * @since 4.9.0 This filter is no longer Multisite specific.
       
  2691 		 *
       
  2692 		 * @param string $email_text     Text in the email.
       
  2693 		 * @param array  $new_user_email {
       
  2694 		 *     Data relating to the new user email address.
       
  2695 		 *
       
  2696 		 *     @type string $hash     The secure hash used in the confirmation link URL.
       
  2697 		 *     @type string $newemail The proposed new email address.
       
  2698 		 * }
       
  2699 		 */
       
  2700 		$content = apply_filters( 'new_user_email_content', $email_text, $new_user_email );
       
  2701 
       
  2702 		$content = str_replace( '###USERNAME###', $current_user->user_login, $content );
       
  2703 		$content = str_replace( '###ADMIN_URL###', esc_url( admin_url( 'profile.php?newuseremail=' . $hash ) ), $content );
       
  2704 		$content = str_replace( '###EMAIL###', $_POST['email'], $content );
       
  2705 		$content = str_replace( '###SITENAME###', $sitename, $content );
       
  2706 		$content = str_replace( '###SITEURL###', home_url(), $content );
       
  2707 
       
  2708 		wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), $sitename ), $content );
       
  2709 
       
  2710 		$_POST['email'] = $current_user->user_email;
       
  2711 	}
       
  2712 }
       
  2713 
       
  2714 /**
       
  2715  * Adds an admin notice alerting the user to check for confirmation request email
       
  2716  * after email address change.
       
  2717  *
       
  2718  * @since 3.0.0
       
  2719  * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
       
  2720  *
       
  2721  * @global string $pagenow
       
  2722  */
       
  2723 function new_user_email_admin_notice() {
       
  2724 	global $pagenow;
       
  2725 	if ( 'profile.php' === $pagenow && isset( $_GET['updated'] ) && $email = get_user_meta( get_current_user_id(), '_new_email', true ) ) {
       
  2726 		/* translators: %s: New email address */
       
  2727 		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>';
       
  2728 	}
       
  2729 }
       
  2730 
       
  2731 /**
       
  2732  * Get all user privacy request types.
       
  2733  *
       
  2734  * @since 4.9.6
       
  2735  * @access private
       
  2736  *
       
  2737  * @return array List of core privacy action types.
       
  2738  */
       
  2739 function _wp_privacy_action_request_types() {
       
  2740 	return array(
       
  2741 		'export_personal_data',
       
  2742 		'remove_personal_data',
       
  2743 	);
       
  2744 }
       
  2745 
       
  2746 /**
       
  2747  * Registers the personal data exporter for users.
       
  2748  *
       
  2749  * @since 4.9.6
       
  2750  *
       
  2751  * @param array $exporters  An array of personal data exporters.
       
  2752  * @return array An array of personal data exporters.
       
  2753  */
       
  2754 function wp_register_user_personal_data_exporter( $exporters ) {
       
  2755 	$exporters['wordpress-user'] = array(
       
  2756 		'exporter_friendly_name' => __( 'WordPress User' ),
       
  2757 		'callback'               => 'wp_user_personal_data_exporter',
       
  2758 	);
       
  2759 
       
  2760 	return $exporters;
       
  2761 }
       
  2762 
       
  2763 /**
       
  2764  * Finds and exports personal data associated with an email address from the user and user_meta table.
       
  2765  *
       
  2766  * @since 4.9.6
       
  2767  *
       
  2768  * @param string $email_address  The users email address.
       
  2769  * @return array An array of personal data.
       
  2770  */
       
  2771 function wp_user_personal_data_exporter( $email_address ) {
       
  2772 	$email_address = trim( $email_address );
       
  2773 
       
  2774 	$data_to_export = array();
       
  2775 
       
  2776 	$user = get_user_by( 'email', $email_address );
       
  2777 
       
  2778 	if ( ! $user ) {
       
  2779 		return array(
       
  2780 			'data' => array(),
       
  2781 			'done' => true,
       
  2782 		);
       
  2783 	}
       
  2784 
       
  2785 	$user_meta = get_user_meta( $user->ID );
       
  2786 
       
  2787 	$user_prop_to_export = array(
       
  2788 		'ID'              => __( 'User ID' ),
       
  2789 		'user_login'      => __( 'User Login Name' ),
       
  2790 		'user_nicename'   => __( 'User Nice Name' ),
       
  2791 		'user_email'      => __( 'User Email' ),
       
  2792 		'user_url'        => __( 'User URL' ),
       
  2793 		'user_registered' => __( 'User Registration Date' ),
       
  2794 		'display_name'    => __( 'User Display Name' ),
       
  2795 		'nickname'        => __( 'User Nickname' ),
       
  2796 		'first_name'      => __( 'User First Name' ),
       
  2797 		'last_name'       => __( 'User Last Name' ),
       
  2798 		'description'     => __( 'User Description' ),
       
  2799 	);
       
  2800 
       
  2801 	$user_data_to_export = array();
       
  2802 
       
  2803 	foreach ( $user_prop_to_export as $key => $name ) {
       
  2804 		$value = '';
       
  2805 
       
  2806 		switch ( $key ) {
       
  2807 			case 'ID':
       
  2808 			case 'user_login':
       
  2809 			case 'user_nicename':
       
  2810 			case 'user_email':
       
  2811 			case 'user_url':
       
  2812 			case 'user_registered':
       
  2813 			case 'display_name':
       
  2814 				$value = $user->data->$key;
       
  2815 				break;
       
  2816 			case 'nickname':
       
  2817 			case 'first_name':
       
  2818 			case 'last_name':
       
  2819 			case 'description':
       
  2820 				$value = $user_meta[ $key ][0];
       
  2821 				break;
       
  2822 		}
       
  2823 
       
  2824 		if ( ! empty( $value ) ) {
       
  2825 			$user_data_to_export[] = array(
       
  2826 				'name'  => $name,
       
  2827 				'value' => $value,
       
  2828 			);
       
  2829 		}
       
  2830 	}
       
  2831 
       
  2832 	$data_to_export[] = array(
       
  2833 		'group_id'    => 'user',
       
  2834 		'group_label' => __( 'User' ),
       
  2835 		'item_id'     => "user-{$user->ID}",
       
  2836 		'data'        => $user_data_to_export,
       
  2837 	);
       
  2838 
       
  2839 	return array(
       
  2840 		'data' => $data_to_export,
       
  2841 		'done' => true,
       
  2842 	);
       
  2843 }
       
  2844 
       
  2845 /**
       
  2846  * Update log when privacy request is confirmed.
       
  2847  *
       
  2848  * @since 4.9.6
       
  2849  * @access private
       
  2850  *
       
  2851  * @param int $request_id ID of the request.
       
  2852  */
       
  2853 function _wp_privacy_account_request_confirmed( $request_id ) {
       
  2854 	$request_data = wp_get_user_request_data( $request_id );
       
  2855 
       
  2856 	if ( ! $request_data ) {
       
  2857 		return;
       
  2858 	}
       
  2859 
       
  2860 	if ( ! in_array( $request_data->status, array( 'request-pending', 'request-failed' ), true ) ) {
       
  2861 		return;
       
  2862 	}
       
  2863 
       
  2864 	update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() );
       
  2865 	wp_update_post( array(
       
  2866 		'ID'          => $request_id,
       
  2867 		'post_status' => 'request-confirmed',
       
  2868 	) );
       
  2869 }
       
  2870 
       
  2871 /**
       
  2872  * Notify the site administrator via email when a request is confirmed.
       
  2873  *
       
  2874  * Without this, the admin would have to manually check the site to see if any
       
  2875  * action was needed on their part yet.
       
  2876  *
       
  2877  * @since 4.9.6
       
  2878  *
       
  2879  * @param int $request_id The ID of the request.
       
  2880  */
       
  2881 function _wp_privacy_send_request_confirmation_notification( $request_id ) {
       
  2882 	$request_data = wp_get_user_request_data( $request_id );
       
  2883 
       
  2884 	if ( ! is_a( $request_data, 'WP_User_Request' ) || 'request-confirmed' !== $request_data->status ) {
       
  2885 		return;
       
  2886 	}
       
  2887 
       
  2888 	$already_notified = (bool) get_post_meta( $request_id, '_wp_admin_notified', true );
       
  2889 
       
  2890 	if ( $already_notified ) {
       
  2891 		return;
       
  2892 	}
       
  2893 
       
  2894 	$manage_url         = add_query_arg( 'page', $request_data->action_name, admin_url( 'tools.php' ) );
       
  2895 	$action_description = wp_user_request_action_description( $request_data->action_name );
       
  2896 
       
  2897 	/**
       
  2898 	 * Filters the recipient of the data request confirmation notification.
       
  2899 	 *
       
  2900 	 * In a Multisite environment, this will default to the email address of the
       
  2901 	 * network admin because, by default, single site admins do not have the
       
  2902 	 * capabilities required to process requests. Some networks may wish to
       
  2903 	 * delegate those capabilities to a single-site admin, or a dedicated person
       
  2904 	 * responsible for managing privacy requests.
       
  2905 	 *
       
  2906 	 * @since 4.9.6
       
  2907 	 *
       
  2908 	 * @param string          $admin_email  The email address of the notification recipient.
       
  2909 	 * @param WP_User_Request $request_data The request that is initiating the notification.
       
  2910 	 */
       
  2911 	$admin_email = apply_filters( 'user_request_confirmed_email_to', get_site_option( 'admin_email' ), $request_data );
       
  2912 
       
  2913 	$email_data = array(
       
  2914 		'request'     => $request_data,
       
  2915 		'user_email'  => $request_data->email,
       
  2916 		'description' => $action_description,
       
  2917 		'manage_url'  => $manage_url,
       
  2918 		'sitename'    => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
       
  2919 		'siteurl'     => home_url(),
       
  2920 		'admin_email' => $admin_email,
       
  2921 	);
       
  2922 
       
  2923 	/* translators: Do not translate SITENAME, USER_EMAIL, DESCRIPTION, MANAGE_URL, SITEURL; those are placeholders. */
       
  2924 	$email_text = __(
       
  2925 		'Howdy,
       
  2926 
       
  2927 A user data privacy request has been confirmed on ###SITENAME###:
       
  2928 
       
  2929 User: ###USER_EMAIL###
       
  2930 Request: ###DESCRIPTION###
       
  2931 
       
  2932 You can view and manage these data privacy requests here:
       
  2933 
       
  2934 ###MANAGE_URL###
       
  2935 
       
  2936 Regards,
       
  2937 All at ###SITENAME###
       
  2938 ###SITEURL###'
       
  2939 	);
       
  2940 
       
  2941 	/**
       
  2942 	 * Filters the body of the user request confirmation email.
       
  2943 	 *
       
  2944 	 * The email is sent to an administrator when an user request is confirmed.
       
  2945 	 * The following strings have a special meaning and will get replaced dynamically:
       
  2946 	 *
       
  2947 	 * ###SITENAME###    The name of the site.
       
  2948 	 * ###USER_EMAIL###  The user email for the request.
       
  2949 	 * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
       
  2950 	 * ###MANAGE_URL###  The URL to manage requests.
       
  2951 	 * ###SITEURL###     The URL to the site.
       
  2952 	 *
       
  2953 	 * @since 4.9.6
       
  2954 	 *
       
  2955 	 * @param string $email_text Text in the email.
       
  2956 	 * @param array  $email_data {
       
  2957 	 *     Data relating to the account action email.
       
  2958 	 *
       
  2959 	 *     @type WP_User_Request $request     User request object.
       
  2960 	 *     @type string          $user_email  The email address confirming a request
       
  2961 	 *     @type string          $description Description of the action being performed so the user knows what the email is for.
       
  2962 	 *     @type string          $manage_url  The link to click manage privacy requests of this type.
       
  2963 	 *     @type string          $sitename    The site name sending the mail.
       
  2964 	 *     @type string          $siteurl     The site URL sending the mail.
       
  2965 	 *     @type string          $admin_email The administrator email receiving the mail.
       
  2966 	 * }
       
  2967 	 */
       
  2968 	$content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data );
       
  2969 
       
  2970 	$content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
       
  2971 	$content = str_replace( '###USER_EMAIL###', $email_data['user_email'], $content );
       
  2972 	$content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
       
  2973 	$content = str_replace( '###MANAGE_URL###', esc_url_raw( $email_data['manage_url'] ), $content );
       
  2974 	$content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
       
  2975 
       
  2976 	$subject = sprintf(
       
  2977 		/* translators: 1: Site name. 2: Name of the confirmed action. */
       
  2978 		__( '[%1$s] Action Confirmed: %2$s' ),
       
  2979 		$email_data['sitename'],
       
  2980 		$action_description
       
  2981 	);
       
  2982 
       
  2983 	/**
       
  2984 	 * Filters the subject of the user request confirmation email.
       
  2985 	 *
       
  2986 	 * @since 4.9.8
       
  2987 	 *
       
  2988 	 * @param string $subject    The email subject.
       
  2989 	 * @param string $sitename   The name of the site.
       
  2990 	 * @param array  $email_data {
       
  2991 	 *     Data relating to the account action email.
       
  2992 	 *
       
  2993 	 *     @type WP_User_Request $request     User request object.
       
  2994 	 *     @type string          $user_email  The email address confirming a request
       
  2995 	 *     @type string          $description Description of the action being performed so the user knows what the email is for.
       
  2996 	 *     @type string          $manage_url  The link to click manage privacy requests of this type.
       
  2997 	 *     @type string          $sitename    The site name sending the mail.
       
  2998 	 *     @type string          $siteurl     The site URL sending the mail.
       
  2999 	 *     @type string          $admin_email The administrator email receiving the mail.
       
  3000 	 * }
       
  3001 	 */
       
  3002 	$subject = apply_filters( 'user_request_confirmed_email_subject', $subject, $email_data['sitename'], $email_data );
       
  3003 
       
  3004 	$email_sent = wp_mail( $email_data['admin_email'], $subject, $content );
       
  3005 
       
  3006 	if ( $email_sent ) {
       
  3007 		update_post_meta( $request_id, '_wp_admin_notified', true );
       
  3008 	}
       
  3009 }
       
  3010 
       
  3011 /**
       
  3012  * Notify the user when their erasure request is fulfilled.
       
  3013  *
       
  3014  * Without this, the user would never know if their data was actually erased.
       
  3015  *
       
  3016  * @since 4.9.6
       
  3017  *
       
  3018  * @param int $request_id The privacy request post ID associated with this request.
       
  3019  */
       
  3020 function _wp_privacy_send_erasure_fulfillment_notification( $request_id ) {
       
  3021 	$request_data = wp_get_user_request_data( $request_id );
       
  3022 
       
  3023 	if ( ! is_a( $request_data, 'WP_User_Request' ) || 'request-completed' !== $request_data->status ) {
       
  3024 		return;
       
  3025 	}
       
  3026 
       
  3027 	$already_notified = (bool) get_post_meta( $request_id, '_wp_user_notified', true );
       
  3028 
       
  3029 	if ( $already_notified ) {
       
  3030 		return;
       
  3031 	}
       
  3032 
       
  3033 	/**
       
  3034 	 * Filters the recipient of the data erasure fulfillment notification.
       
  3035 	 *
       
  3036 	 * @since 4.9.6
       
  3037 	 *
       
  3038 	 * @param string          $user_email   The email address of the notification recipient.
       
  3039 	 * @param WP_User_Request $request_data The request that is initiating the notification.
       
  3040 	 */
       
  3041 	$user_email = apply_filters( 'user_erasure_fulfillment_email_to', $request_data->email, $request_data );
       
  3042 
       
  3043 	$email_data = array(
       
  3044 		'request'            => $request_data,
       
  3045 		'message_recipient'  => $user_email,
       
  3046 		'privacy_policy_url' => get_privacy_policy_url(),
       
  3047 		'sitename'           => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
       
  3048 		'siteurl'            => home_url(),
       
  3049 	);
       
  3050 
       
  3051 	$subject  = sprintf(
       
  3052 		/* translators: %s: Site name. */
       
  3053 		__( '[%s] Erasure Request Fulfilled' ),
       
  3054 		$email_data['sitename']
       
  3055 	);
       
  3056 
       
  3057 	/**
       
  3058 	 * Filters the subject of the email sent when an erasure request is completed.
       
  3059 	 *
       
  3060 	 * @since 4.9.8
       
  3061 	 *
       
  3062 	 * @param string $subject    The email subject.
       
  3063 	 * @param string $sitename   The name of the site.
       
  3064 	 * @param array  $email_data {
       
  3065 	 *     Data relating to the account action email.
       
  3066 	 *
       
  3067 	 *     @type WP_User_Request $request            User request object.
       
  3068 	 *     @type string          $message_recipient  The address that the email will be sent to. Defaults
       
  3069 	 *                                               to the value of `$request->email`, but can be changed
       
  3070 	 *                                               by the `user_erasure_fulfillment_email_to` filter.
       
  3071 	 *     @type string          $privacy_policy_url Privacy policy URL.
       
  3072 	 *     @type string          $sitename           The site name sending the mail.
       
  3073 	 *     @type string          $siteurl            The site URL sending the mail.
       
  3074 	 * }
       
  3075 	 */
       
  3076 	$subject = apply_filters( 'user_erasure_complete_email_subject', $subject, $email_data['sitename'], $email_data );
       
  3077 
       
  3078 	if ( empty( $email_data['privacy_policy_url'] ) ) {
       
  3079 		/* translators: Do not translate SITENAME, SITEURL; those are placeholders. */
       
  3080 		$email_text = __(
       
  3081 			'Howdy,
       
  3082 
       
  3083 Your request to erase your personal data on ###SITENAME### has been completed.
       
  3084 
       
  3085 If you have any follow-up questions or concerns, please contact the site administrator.
       
  3086 
       
  3087 Regards,
       
  3088 All at ###SITENAME###
       
  3089 ###SITEURL###'
       
  3090 		);
       
  3091 	} else {
       
  3092 		/* translators: Do not translate SITENAME, SITEURL, PRIVACY_POLICY_URL; those are placeholders. */
       
  3093 		$email_text = __(
       
  3094 			'Howdy,
       
  3095 
       
  3096 Your request to erase your personal data on ###SITENAME### has been completed.
       
  3097 
       
  3098 If you have any follow-up questions or concerns, please contact the site administrator.
       
  3099 
       
  3100 For more information, you can also read our privacy policy: ###PRIVACY_POLICY_URL###
       
  3101 
       
  3102 Regards,
       
  3103 All at ###SITENAME###
       
  3104 ###SITEURL###'
       
  3105 		);
       
  3106 	}
       
  3107 
       
  3108 	/**
       
  3109 	 * Filters the body of the data erasure fulfillment notification.
       
  3110 	 *
       
  3111 	 * The email is sent to a user when a their data erasure request is fulfilled
       
  3112 	 * by an administrator.
       
  3113 	 *
       
  3114 	 * The following strings have a special meaning and will get replaced dynamically:
       
  3115 	 *
       
  3116 	 * ###SITENAME###           The name of the site.
       
  3117 	 * ###PRIVACY_POLICY_URL### Privacy policy page URL.
       
  3118 	 * ###SITEURL###            The URL to the site.
       
  3119 	 *
       
  3120 	 * @since 4.9.6
       
  3121 	 *
       
  3122 	 * @param string $email_text Text in the email.
       
  3123 	 * @param array  $email_data {
       
  3124 	 *     Data relating to the account action email.
       
  3125 	 *
       
  3126 	 *     @type WP_User_Request $request            User request object.
       
  3127 	 *     @type string          $message_recipient  The address that the email will be sent to. Defaults
       
  3128 	 *                                               to the value of `$request->email`, but can be changed
       
  3129 	 *                                               by the `user_erasure_fulfillment_email_to` filter.
       
  3130 	 *     @type string          $privacy_policy_url Privacy policy URL.
       
  3131 	 *     @type string          $sitename           The site name sending the mail.
       
  3132 	 *     @type string          $siteurl            The site URL sending the mail.
       
  3133 	 * }
       
  3134 	 */
       
  3135 	$content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data );
       
  3136 
       
  3137 	$content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
       
  3138 	$content = str_replace( '###PRIVACY_POLICY_URL###', $email_data['privacy_policy_url'], $content );
       
  3139 	$content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
       
  3140 
       
  3141 	$email_sent = wp_mail( $user_email, $subject, $content );
       
  3142 
       
  3143 	if ( $email_sent ) {
       
  3144 		update_post_meta( $request_id, '_wp_user_notified', true );
       
  3145 	}
       
  3146 }
       
  3147 
       
  3148 /**
       
  3149  * Return request confirmation message HTML.
       
  3150  *
       
  3151  * @since 4.9.6
       
  3152  * @access private
       
  3153  *
       
  3154  * @param int $request_id The request ID being confirmed.
       
  3155  * @return string $message The confirmation message.
       
  3156  */
       
  3157 function _wp_privacy_account_request_confirmed_message( $request_id ) {
       
  3158 	$request = wp_get_user_request_data( $request_id );
       
  3159 
       
  3160 	$message = '<p class="success">' . __( 'Action has been confirmed.' ) . '</p>';
       
  3161 	$message .= '<p>' . __( 'The site administrator has been notified and will fulfill your request as soon as possible.' ) . '</p>';
       
  3162 
       
  3163 	if ( $request && in_array( $request->action_name, _wp_privacy_action_request_types(), true ) ) {
       
  3164 		if ( 'export_personal_data' === $request->action_name ) {
       
  3165 			$message = '<p class="success">' . __( 'Thanks for confirming your export request.' ) . '</p>';
       
  3166 			$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>';
       
  3167 		} elseif ( 'remove_personal_data' === $request->action_name ) {
       
  3168 			$message = '<p class="success">' . __( 'Thanks for confirming your erasure request.' ) . '</p>';
       
  3169 			$message .= '<p>' . __( 'The site administrator has been notified. You will receive an email confirmation when they erase your data.' ) . '</p>';
       
  3170 		}
       
  3171 	}
       
  3172 
       
  3173 	/**
       
  3174 	 * Filters the message displayed to a user when they confirm a data request.
       
  3175 	 *
       
  3176 	 * @since 4.9.6
       
  3177 	 *
       
  3178 	 * @param string $message    The message to the user.
       
  3179 	 * @param int    $request_id The ID of the request being confirmed.
       
  3180 	 */
       
  3181 	$message = apply_filters( 'user_request_action_confirmed_message', $message, $request_id );
       
  3182 
       
  3183 	return $message;
       
  3184 }
       
  3185 
       
  3186 /**
       
  3187  * Create and log a user request to perform a specific action.
       
  3188  *
       
  3189  * Requests are stored inside a post type named `user_request` since they can apply to both
       
  3190  * users on the site, or guests without a user account.
       
  3191  *
       
  3192  * @since 4.9.6
       
  3193  *
       
  3194  * @param string $email_address User email address. This can be the address of a registered or non-registered user.
       
  3195  * @param string $action_name   Name of the action that is being confirmed. Required.
       
  3196  * @param array  $request_data  Misc data you want to send with the verification request and pass to the actions once the request is confirmed.
       
  3197  * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure.
       
  3198  */
       
  3199 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) {
       
  3200 	$email_address = sanitize_email( $email_address );
       
  3201 	$action_name   = sanitize_key( $action_name );
       
  3202 
       
  3203 	if ( ! is_email( $email_address ) ) {
       
  3204 		return new WP_Error( 'invalid_email', __( 'Invalid email address.' ) );
       
  3205 	}
       
  3206 
       
  3207 	if ( ! $action_name ) {
       
  3208 		return new WP_Error( 'invalid_action', __( 'Invalid action name.' ) );
       
  3209 	}
       
  3210 
       
  3211 	$user    = get_user_by( 'email', $email_address );
       
  3212 	$user_id = $user && ! is_wp_error( $user ) ? $user->ID : 0;
       
  3213 
       
  3214 	// Check for duplicates.
       
  3215 	$requests_query = new WP_Query( array(
       
  3216 		'post_type'     => 'user_request',
       
  3217 		'post_name__in' => array( $action_name ),  // Action name stored in post_name column.
       
  3218 		'title'         => $email_address, // Email address stored in post_title column.
       
  3219 		'post_status'   => 'any',
       
  3220 		'fields'        => 'ids',
       
  3221 	) );
       
  3222 
       
  3223 	if ( $requests_query->found_posts ) {
       
  3224 		return new WP_Error( 'duplicate_request', __( 'A request for this email address already exists.' ) );
       
  3225 	}
       
  3226 
       
  3227 	$request_id = wp_insert_post( array(
       
  3228 		'post_author'   => $user_id,
       
  3229 		'post_name'     => $action_name,
       
  3230 		'post_title'    => $email_address,
       
  3231 		'post_content'  => wp_json_encode( $request_data ),
       
  3232 		'post_status'   => 'request-pending',
       
  3233 		'post_type'     => 'user_request',
       
  3234 		'post_date'     => current_time( 'mysql', false ),
       
  3235 		'post_date_gmt' => current_time( 'mysql', true ),
       
  3236 	), true );
       
  3237 
       
  3238 	return $request_id;
       
  3239 }
       
  3240 
       
  3241 /**
       
  3242  * Get action description from the name and return a string.
       
  3243  *
       
  3244  * @since 4.9.6
       
  3245  *
       
  3246  * @param string $action_name Action name of the request.
       
  3247  * @return string Human readable action name.
       
  3248  */
       
  3249 function wp_user_request_action_description( $action_name ) {
       
  3250 	switch ( $action_name ) {
       
  3251 		case 'export_personal_data':
       
  3252 			$description = __( 'Export Personal Data' );
       
  3253 			break;
       
  3254 		case 'remove_personal_data':
       
  3255 			$description = __( 'Erase Personal Data' );
       
  3256 			break;
       
  3257 		default:
       
  3258 			/* translators: %s: action name */
       
  3259 			$description = sprintf( __( 'Confirm the "%s" action' ), $action_name );
       
  3260 			break;
       
  3261 	}
       
  3262 
       
  3263 	/**
       
  3264 	 * Filters the user action description.
       
  3265 	 *
       
  3266 	 * @since 4.9.6
       
  3267 	 *
       
  3268 	 * @param string $description The default description.
       
  3269 	 * @param string $action_name The name of the request.
       
  3270 	 */
       
  3271 	return apply_filters( 'user_request_action_description', $description, $action_name );
       
  3272 }
       
  3273 
       
  3274 /**
       
  3275  * Send a confirmation request email to confirm an action.
       
  3276  *
       
  3277  * If the request is not already pending, it will be updated.
       
  3278  *
       
  3279  * @since 4.9.6
       
  3280  *
       
  3281  * @param string $request_id ID of the request created via wp_create_user_request().
       
  3282  * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object.
       
  3283  */
       
  3284 function wp_send_user_request( $request_id ) {
       
  3285 	$request_id = absint( $request_id );
       
  3286 	$request    = wp_get_user_request_data( $request_id );
       
  3287 
       
  3288 	if ( ! $request ) {
       
  3289 		return new WP_Error( 'user_request_error', __( 'Invalid request.' ) );
       
  3290 	}
       
  3291 
       
  3292 	$email_data = array(
       
  3293 		'request'     => $request,
       
  3294 		'email'       => $request->email,
       
  3295 		'description' => wp_user_request_action_description( $request->action_name ),
       
  3296 		'confirm_url' => add_query_arg( array(
       
  3297 			'action'      => 'confirmaction',
       
  3298 			'request_id'  => $request_id,
       
  3299 			'confirm_key' => wp_generate_user_request_key( $request_id ),
       
  3300 		), wp_login_url() ),
       
  3301 		'sitename'    => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
       
  3302 		'siteurl'     => home_url(),
       
  3303 	);
       
  3304 
       
  3305 	/* translators: Do not translate DESCRIPTION, CONFIRM_URL, SITENAME, SITEURL: those are placeholders. */
       
  3306 	$email_text = __(
       
  3307 		'Howdy,
       
  3308 
       
  3309 A request has been made to perform the following action on your account:
       
  3310 
       
  3311      ###DESCRIPTION###
       
  3312 
       
  3313 To confirm this, please click on the following link:
       
  3314 ###CONFIRM_URL###
       
  3315 
       
  3316 You can safely ignore and delete this email if you do not want to
       
  3317 take this action.
       
  3318 
       
  3319 Regards,
       
  3320 All at ###SITENAME###
       
  3321 ###SITEURL###'
       
  3322 	);
       
  3323 
       
  3324 	/**
       
  3325 	 * Filters the text of the email sent when an account action is attempted.
       
  3326 	 *
       
  3327 	 * The following strings have a special meaning and will get replaced dynamically:
       
  3328 	 *
       
  3329 	 * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
       
  3330 	 * ###CONFIRM_URL### The link to click on to confirm the account action.
       
  3331 	 * ###SITENAME###    The name of the site.
       
  3332 	 * ###SITEURL###     The URL to the site.
       
  3333 	 *
       
  3334 	 * @since 4.9.6
       
  3335 	 *
       
  3336 	 * @param string $email_text Text in the email.
       
  3337 	 * @param array  $email_data {
       
  3338 	 *     Data relating to the account action email.
       
  3339 	 *
       
  3340 	 *     @type WP_User_Request $request     User request object.
       
  3341 	 *     @type string          $email       The email address this is being sent to.
       
  3342 	 *     @type string          $description Description of the action being performed so the user knows what the email is for.
       
  3343 	 *     @type string          $confirm_url The link to click on to confirm the account action.
       
  3344 	 *     @type string          $sitename    The site name sending the mail.
       
  3345 	 *     @type string          $siteurl     The site URL sending the mail.
       
  3346 	 * }
       
  3347 	 */
       
  3348 	$content = apply_filters( 'user_request_action_email_content', $email_text, $email_data );
       
  3349 
       
  3350 	$content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
       
  3351 	$content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content );
       
  3352 	$content = str_replace( '###EMAIL###', $email_data['email'], $content );
       
  3353 	$content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
       
  3354 	$content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
       
  3355 
       
  3356 	/* translators: Privacy data request subject. 1: Site name, 2: Name of the action */
       
  3357 	$subject = sprintf( __( '[%1$s] Confirm Action: %2$s' ), $email_data['sitename'], $email_data['description'] );
       
  3358 
       
  3359 	/**
       
  3360 	 * Filters the subject of the email sent when an account action is attempted.
       
  3361 	 *
       
  3362 	 * @since 4.9.6
       
  3363 	 *
       
  3364 	 * @param string $subject    The email subject.
       
  3365 	 * @param string $sitename   The name of the site.
       
  3366 	 * @param array  $email_data {
       
  3367 	 *     Data relating to the account action email.
       
  3368 	 *
       
  3369 	 *     @type WP_User_Request $request     User request object.
       
  3370 	 *     @type string          $email       The email address this is being sent to.
       
  3371 	 *     @type string          $description Description of the action being performed so the user knows what the email is for.
       
  3372 	 *     @type string          $confirm_url The link to click on to confirm the account action.
       
  3373 	 *     @type string          $sitename    The site name sending the mail.
       
  3374 	 *     @type string          $siteurl     The site URL sending the mail.
       
  3375 	 * }
       
  3376 	 */
       
  3377 	$subject = apply_filters( 'user_request_action_email_subject', $subject, $email_data['sitename'], $email_data );
       
  3378 
       
  3379 	return wp_mail( $email_data['email'], $subject, $content );
       
  3380 }
       
  3381 
       
  3382 /**
       
  3383  * Returns a confirmation key for a user action and stores the hashed version for future comparison.
       
  3384  *
       
  3385  * @since 4.9.6
       
  3386  *
       
  3387  * @param int $request_id Request ID.
       
  3388  * @return string Confirmation key.
       
  3389  */
       
  3390 function wp_generate_user_request_key( $request_id ) {
       
  3391 	global $wp_hasher;
       
  3392 
       
  3393 	// Generate something random for a confirmation key.
       
  3394 	$key = wp_generate_password( 20, false );
       
  3395 
       
  3396 	// Return the key, hashed.
       
  3397 	if ( empty( $wp_hasher ) ) {
       
  3398 		require_once ABSPATH . WPINC . '/class-phpass.php';
       
  3399 		$wp_hasher = new PasswordHash( 8, true );
       
  3400 	}
       
  3401 
       
  3402 	wp_update_post( array(
       
  3403 		'ID'                => $request_id,
       
  3404 		'post_status'       => 'request-pending',
       
  3405 		'post_password'     => $wp_hasher->HashPassword( $key ),
       
  3406 		'post_modified'     => current_time( 'mysql', false ),
       
  3407 		'post_modified_gmt' => current_time( 'mysql', true ),
       
  3408 	) );
       
  3409 
       
  3410 	return $key;
       
  3411 }
       
  3412 
       
  3413 /**
       
  3414  * Validate a user request by comparing the key with the request's key.
       
  3415  *
       
  3416  * @since 4.9.6
       
  3417  *
       
  3418  * @param string $request_id ID of the request being confirmed.
       
  3419  * @param string $key        Provided key to validate.
       
  3420  * @return bool|WP_Error WP_Error on failure, true on success.
       
  3421  */
       
  3422 function wp_validate_user_request_key( $request_id, $key ) {
       
  3423 	global $wp_hasher;
       
  3424 
       
  3425 	$request_id = absint( $request_id );
       
  3426 	$request    = wp_get_user_request_data( $request_id );
       
  3427 
       
  3428 	if ( ! $request ) {
       
  3429 		return new WP_Error( 'user_request_error', __( 'Invalid request.' ) );
       
  3430 	}
       
  3431 
       
  3432 	if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) {
       
  3433 		return __( 'This link has expired.' );
       
  3434 	}
       
  3435 
       
  3436 	if ( empty( $key ) ) {
       
  3437 		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
       
  3438 	}
       
  3439 
       
  3440 	if ( empty( $wp_hasher ) ) {
       
  3441 		require_once ABSPATH . WPINC . '/class-phpass.php';
       
  3442 		$wp_hasher = new PasswordHash( 8, true );
       
  3443 	}
       
  3444 
       
  3445 	$key_request_time = $request->modified_timestamp;
       
  3446 	$saved_key        = $request->confirm_key;
       
  3447 
       
  3448 	if ( ! $saved_key ) {
       
  3449 		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
       
  3450 	}
       
  3451 
       
  3452 	if ( ! $key_request_time ) {
       
  3453 		return new WP_Error( 'invalid_key', __( 'Invalid action' ) );
       
  3454 	}
       
  3455 
       
  3456 	/**
       
  3457 	 * Filters the expiration time of confirm keys.
       
  3458 	 *
       
  3459 	 * @since 4.9.6
       
  3460 	 *
       
  3461 	 * @param int $expiration The expiration time in seconds.
       
  3462 	 */
       
  3463 	$expiration_duration = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS );
       
  3464 	$expiration_time     = $key_request_time + $expiration_duration;
       
  3465 
       
  3466 	if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) {
       
  3467 		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
       
  3468 	}
       
  3469 
       
  3470 	if ( ! $expiration_time || time() > $expiration_time ) {
       
  3471 		return new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
       
  3472 	}
       
  3473 
       
  3474 	return true;
       
  3475 }
       
  3476 
       
  3477 /**
       
  3478  * Return data about a user request.
       
  3479  *
       
  3480  * @since 4.9.6
       
  3481  *
       
  3482  * @param int $request_id Request ID to get data about.
       
  3483  * @return WP_User_Request|false
       
  3484  */
       
  3485 function wp_get_user_request_data( $request_id ) {
       
  3486 	$request_id = absint( $request_id );
       
  3487 	$post       = get_post( $request_id );
       
  3488 
       
  3489 	if ( ! $post || 'user_request' !== $post->post_type ) {
       
  3490 		return false;
       
  3491 	}
       
  3492 
       
  3493 	return new WP_User_Request( $post );
       
  3494 }
       
  3495 
       
  3496 /**
       
  3497  * WP_User_Request class.
       
  3498  *
       
  3499  * Represents user request data loaded from a WP_Post object.
       
  3500  *
       
  3501  * @since 4.9.6
       
  3502  */
       
  3503 final class WP_User_Request {
       
  3504 	/**
       
  3505 	 * Request ID.
       
  3506 	 *
       
  3507 	 * @var int
       
  3508 	 */
       
  3509 	public $ID = 0;
       
  3510 
       
  3511 	/**
       
  3512 	 * User ID.
       
  3513 	 *
       
  3514 	 * @var int
       
  3515 	 */
       
  3516 
       
  3517 	public $user_id = 0;
       
  3518 
       
  3519 	/**
       
  3520 	 * User email.
       
  3521 	 *
       
  3522 	 * @var int
       
  3523 	 */
       
  3524 	public $email = '';
       
  3525 
       
  3526 	/**
       
  3527 	 * Action name.
       
  3528 	 *
       
  3529 	 * @var string
       
  3530 	 */
       
  3531 	public $action_name = '';
       
  3532 
       
  3533 	/**
       
  3534 	 * Current status.
       
  3535 	 *
       
  3536 	 * @var string
       
  3537 	 */
       
  3538 	public $status = '';
       
  3539 
       
  3540 	/**
       
  3541 	 * Timestamp this request was created.
       
  3542 	 *
       
  3543 	 * @var int|null
       
  3544 	 */
       
  3545 	public $created_timestamp = null;
       
  3546 
       
  3547 	/**
       
  3548 	 * Timestamp this request was last modified.
       
  3549 	 *
       
  3550 	 * @var int|null
       
  3551 	 */
       
  3552 	public $modified_timestamp = null;
       
  3553 
       
  3554 	/**
       
  3555 	 * Timestamp this request was confirmed.
       
  3556 	 *
       
  3557 	 * @var int
       
  3558 	 */
       
  3559 	public $confirmed_timestamp = null;
       
  3560 
       
  3561 	/**
       
  3562 	 * Timestamp this request was completed.
       
  3563 	 *
       
  3564 	 * @var int
       
  3565 	 */
       
  3566 	public $completed_timestamp = null;
       
  3567 
       
  3568 	/**
       
  3569 	 * Misc data assigned to this request.
       
  3570 	 *
       
  3571 	 * @var array
       
  3572 	 */
       
  3573 	public $request_data = array();
       
  3574 
       
  3575 	/**
       
  3576 	 * Key used to confirm this request.
       
  3577 	 *
       
  3578 	 * @var string
       
  3579 	 */
       
  3580 	public $confirm_key = '';
       
  3581 
       
  3582 	/**
       
  3583 	 * Constructor.
       
  3584 	 *
       
  3585 	 * @since 4.9.6
       
  3586 	 *
       
  3587 	 * @param WP_Post|object $post Post object.
       
  3588 	 */
       
  3589 	public function __construct( $post ) {
       
  3590 		$this->ID                  = $post->ID;
       
  3591 		$this->user_id             = $post->post_author;
       
  3592 		$this->email               = $post->post_title;
       
  3593 		$this->action_name         = $post->post_name;
       
  3594 		$this->status              = $post->post_status;
       
  3595 		$this->created_timestamp   = strtotime( $post->post_date_gmt );
       
  3596 		$this->modified_timestamp  = strtotime( $post->post_modified_gmt );
       
  3597 		$this->confirmed_timestamp = (int) get_post_meta( $post->ID, '_wp_user_request_confirmed_timestamp', true );
       
  3598 		$this->completed_timestamp = (int) get_post_meta( $post->ID, '_wp_user_request_completed_timestamp', true );
       
  3599 		$this->request_data        = json_decode( $post->post_content, true );
       
  3600 		$this->confirm_key         = $post->post_password;
       
  3601 	}
       
  3602 }