wp/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php
changeset 7 cf61fcea0001
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     1 <?php
       
     2 /**
       
     3  * REST API: WP_REST_Users_Controller class
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage REST_API
       
     7  * @since 4.7.0
       
     8  */
       
     9 
       
    10 /**
       
    11  * Core class used to manage users via the REST API.
       
    12  *
       
    13  * @since 4.7.0
       
    14  *
       
    15  * @see WP_REST_Controller
       
    16  */
       
    17 class WP_REST_Users_Controller extends WP_REST_Controller {
       
    18 
       
    19 	/**
       
    20 	 * Instance of a user meta fields object.
       
    21 	 *
       
    22 	 * @since 4.7.0
       
    23 	 * @var WP_REST_User_Meta_Fields
       
    24 	 */
       
    25 	protected $meta;
       
    26 
       
    27 	/**
       
    28 	 * Constructor.
       
    29 	 *
       
    30 	 * @since 4.7.0
       
    31 	 */
       
    32 	public function __construct() {
       
    33 		$this->namespace = 'wp/v2';
       
    34 		$this->rest_base = 'users';
       
    35 
       
    36 		$this->meta = new WP_REST_User_Meta_Fields();
       
    37 	}
       
    38 
       
    39 	/**
       
    40 	 * Registers the routes for the objects of the controller.
       
    41 	 *
       
    42 	 * @since 4.7.0
       
    43 	 *
       
    44 	 * @see register_rest_route()
       
    45 	 */
       
    46 	public function register_routes() {
       
    47 
       
    48 		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
       
    49 			array(
       
    50 				'methods'             => WP_REST_Server::READABLE,
       
    51 				'callback'            => array( $this, 'get_items' ),
       
    52 				'permission_callback' => array( $this, 'get_items_permissions_check' ),
       
    53 				'args'                => $this->get_collection_params(),
       
    54 			),
       
    55 			array(
       
    56 				'methods'             => WP_REST_Server::CREATABLE,
       
    57 				'callback'            => array( $this, 'create_item' ),
       
    58 				'permission_callback' => array( $this, 'create_item_permissions_check' ),
       
    59 				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
       
    60 			),
       
    61 			'schema' => array( $this, 'get_public_item_schema' ),
       
    62 		) );
       
    63 
       
    64 		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
       
    65 			'args' => array(
       
    66 				'id' => array(
       
    67 					'description' => __( 'Unique identifier for the user.' ),
       
    68 					'type'        => 'integer',
       
    69 				),
       
    70 			),
       
    71 			array(
       
    72 				'methods'             => WP_REST_Server::READABLE,
       
    73 				'callback'            => array( $this, 'get_item' ),
       
    74 				'permission_callback' => array( $this, 'get_item_permissions_check' ),
       
    75 				'args'                => array(
       
    76 					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
       
    77 				),
       
    78 			),
       
    79 			array(
       
    80 				'methods'             => WP_REST_Server::EDITABLE,
       
    81 				'callback'            => array( $this, 'update_item' ),
       
    82 				'permission_callback' => array( $this, 'update_item_permissions_check' ),
       
    83 				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
       
    84 			),
       
    85 			array(
       
    86 				'methods'             => WP_REST_Server::DELETABLE,
       
    87 				'callback'            => array( $this, 'delete_item' ),
       
    88 				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
       
    89 				'args'                => array(
       
    90 					'force'    => array(
       
    91 						'type'        => 'boolean',
       
    92 						'default'     => false,
       
    93 						'description' => __( 'Required to be true, as users do not support trashing.' ),
       
    94 					),
       
    95 					'reassign' => array(
       
    96 						'type'        => 'integer',
       
    97 						'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ),
       
    98 						'required'    => true,
       
    99 						'sanitize_callback' => array( $this, 'check_reassign' ),
       
   100 					),
       
   101 				),
       
   102 			),
       
   103 			'schema' => array( $this, 'get_public_item_schema' ),
       
   104 		) );
       
   105 
       
   106 		register_rest_route( $this->namespace, '/' . $this->rest_base . '/me', array(
       
   107 			array(
       
   108 				'methods'             => WP_REST_Server::READABLE,
       
   109 				'callback'            => array( $this, 'get_current_item' ),
       
   110 				'args'                => array(
       
   111 					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
       
   112 				),
       
   113 			),
       
   114 			array(
       
   115 				'methods'             => WP_REST_Server::EDITABLE,
       
   116 				'callback'            => array( $this, 'update_current_item' ),
       
   117 				'permission_callback' => array( $this, 'update_current_item_permissions_check' ),
       
   118 				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
       
   119 			),
       
   120 			array(
       
   121 				'methods'             => WP_REST_Server::DELETABLE,
       
   122 				'callback'            => array( $this, 'delete_current_item' ),
       
   123 				'permission_callback' => array( $this, 'delete_current_item_permissions_check' ),
       
   124 				'args'                => array(
       
   125 					'force'    => array(
       
   126 						'type'        => 'boolean',
       
   127 						'default'     => false,
       
   128 						'description' => __( 'Required to be true, as users do not support trashing.' ),
       
   129 					),
       
   130 					'reassign' => array(
       
   131 						'type'        => 'integer',
       
   132 						'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ),
       
   133 						'required'    => true,
       
   134 						'sanitize_callback' => array( $this, 'check_reassign' ),
       
   135 					),
       
   136 				),
       
   137 			),
       
   138 			'schema' => array( $this, 'get_public_item_schema' ),
       
   139 		));
       
   140 	}
       
   141 
       
   142 	/**
       
   143 	 * Checks for a valid value for the reassign parameter when deleting users.
       
   144 	 *
       
   145 	 * The value can be an integer, 'false', false, or ''.
       
   146 	 *
       
   147 	 * @since 4.7.0
       
   148 	 *
       
   149 	 * @param int|bool        $value   The value passed to the reassign parameter.
       
   150 	 * @param WP_REST_Request $request Full details about the request.
       
   151 	 * @param string          $param   The parameter that is being sanitized.
       
   152 	 *
       
   153 	 * @return int|bool|WP_Error
       
   154 	 */
       
   155 	public function check_reassign( $value, $request, $param ) {
       
   156 		if ( is_numeric( $value ) ) {
       
   157 			return $value;
       
   158 		}
       
   159 
       
   160 		if ( empty( $value ) || false === $value || 'false' === $value ) {
       
   161 			return false;
       
   162 		}
       
   163 
       
   164 		return new WP_Error( 'rest_invalid_param', __( 'Invalid user parameter(s).' ), array( 'status' => 400 ) );
       
   165 	}
       
   166 
       
   167 	/**
       
   168 	 * Permissions check for getting all users.
       
   169 	 *
       
   170 	 * @since 4.7.0
       
   171 	 *
       
   172 	 * @param WP_REST_Request $request Full details about the request.
       
   173 	 * @return true|WP_Error True if the request has read access, otherwise WP_Error object.
       
   174 	 */
       
   175 	public function get_items_permissions_check( $request ) {
       
   176 		// Check if roles is specified in GET request and if user can list users.
       
   177 		if ( ! empty( $request['roles'] ) && ! current_user_can( 'list_users' ) ) {
       
   178 			return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to filter users by role.' ), array( 'status' => rest_authorization_required_code() ) );
       
   179 		}
       
   180 
       
   181 		if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
       
   182 			return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) );
       
   183 		}
       
   184 
       
   185 		if ( in_array( $request['orderby'], array( 'email', 'registered_date' ), true ) && ! current_user_can( 'list_users' ) ) {
       
   186 			return new WP_Error( 'rest_forbidden_orderby', __( 'Sorry, you are not allowed to order users by this parameter.' ), array( 'status' => rest_authorization_required_code() ) );
       
   187 		}
       
   188 
       
   189 		if ( 'authors' === $request['who'] ) {
       
   190 			$can_view = false;
       
   191 			$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
       
   192 			foreach ( $types as $type ) {
       
   193 				if ( post_type_supports( $type->name, 'author' )
       
   194 					&& current_user_can( $type->cap->edit_posts ) ) {
       
   195 					$can_view = true;
       
   196 				}
       
   197 			}
       
   198 			if ( ! $can_view ) {
       
   199 				return new WP_Error( 'rest_forbidden_who', __( 'Sorry, you are not allowed to query users by this parameter.' ), array( 'status' => rest_authorization_required_code() ) );
       
   200 			}
       
   201 		}
       
   202 
       
   203 		return true;
       
   204 	}
       
   205 
       
   206 	/**
       
   207 	 * Retrieves all users.
       
   208 	 *
       
   209 	 * @since 4.7.0
       
   210 	 *
       
   211 	 * @param WP_REST_Request $request Full details about the request.
       
   212 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   213 	 */
       
   214 	public function get_items( $request ) {
       
   215 
       
   216 		// Retrieve the list of registered collection query parameters.
       
   217 		$registered = $this->get_collection_params();
       
   218 
       
   219 		/*
       
   220 		 * This array defines mappings between public API query parameters whose
       
   221 		 * values are accepted as-passed, and their internal WP_Query parameter
       
   222 		 * name equivalents (some are the same). Only values which are also
       
   223 		 * present in $registered will be set.
       
   224 		 */
       
   225 		$parameter_mappings = array(
       
   226 			'exclude'  => 'exclude',
       
   227 			'include'  => 'include',
       
   228 			'order'    => 'order',
       
   229 			'per_page' => 'number',
       
   230 			'search'   => 'search',
       
   231 			'roles'    => 'role__in',
       
   232 			'slug'     => 'nicename__in',
       
   233 		);
       
   234 
       
   235 		$prepared_args = array();
       
   236 
       
   237 		/*
       
   238 		 * For each known parameter which is both registered and present in the request,
       
   239 		 * set the parameter's value on the query $prepared_args.
       
   240 		 */
       
   241 		foreach ( $parameter_mappings as $api_param => $wp_param ) {
       
   242 			if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
       
   243 				$prepared_args[ $wp_param ] = $request[ $api_param ];
       
   244 			}
       
   245 		}
       
   246 
       
   247 		if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) {
       
   248 			$prepared_args['offset'] = $request['offset'];
       
   249 		} else {
       
   250 			$prepared_args['offset']  = ( $request['page'] - 1 ) * $prepared_args['number'];
       
   251 		}
       
   252 
       
   253 		if ( isset( $registered['orderby'] ) ) {
       
   254 			$orderby_possibles = array(
       
   255 				'id'              => 'ID',
       
   256 				'include'         => 'include',
       
   257 				'name'            => 'display_name',
       
   258 				'registered_date' => 'registered',
       
   259 				'slug'            => 'user_nicename',
       
   260 				'include_slugs'   => 'nicename__in',
       
   261 				'email'           => 'user_email',
       
   262 				'url'             => 'user_url',
       
   263 			);
       
   264 			$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
       
   265 		}
       
   266 
       
   267 		if ( isset( $registered['who'] ) && ! empty( $request['who'] ) && 'authors' === $request['who'] ) {
       
   268 			$prepared_args['who'] = 'authors';
       
   269 		} elseif ( ! current_user_can( 'list_users' ) ) {
       
   270 			$prepared_args['has_published_posts'] = get_post_types( array( 'show_in_rest' => true ), 'names' );
       
   271 		}
       
   272 
       
   273 		if ( ! empty( $prepared_args['search'] ) ) {
       
   274 			$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
       
   275 		}
       
   276 		/**
       
   277 		 * Filters WP_User_Query arguments when querying users via the REST API.
       
   278 		 *
       
   279 		 * @link https://developer.wordpress.org/reference/classes/wp_user_query/
       
   280 		 *
       
   281 		 * @since 4.7.0
       
   282 		 *
       
   283 		 * @param array           $prepared_args Array of arguments for WP_User_Query.
       
   284 		 * @param WP_REST_Request $request       The current request.
       
   285 		 */
       
   286 		$prepared_args = apply_filters( 'rest_user_query', $prepared_args, $request );
       
   287 
       
   288 		$query = new WP_User_Query( $prepared_args );
       
   289 
       
   290 		$users = array();
       
   291 
       
   292 		foreach ( $query->results as $user ) {
       
   293 			$data = $this->prepare_item_for_response( $user, $request );
       
   294 			$users[] = $this->prepare_response_for_collection( $data );
       
   295 		}
       
   296 
       
   297 		$response = rest_ensure_response( $users );
       
   298 
       
   299 		// Store pagination values for headers then unset for count query.
       
   300 		$per_page = (int) $prepared_args['number'];
       
   301 		$page     = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
       
   302 
       
   303 		$prepared_args['fields'] = 'ID';
       
   304 
       
   305 		$total_users = $query->get_total();
       
   306 
       
   307 		if ( $total_users < 1 ) {
       
   308 			// Out-of-bounds, run the query again without LIMIT for total count.
       
   309 			unset( $prepared_args['number'], $prepared_args['offset'] );
       
   310 			$count_query = new WP_User_Query( $prepared_args );
       
   311 			$total_users = $count_query->get_total();
       
   312 		}
       
   313 
       
   314 		$response->header( 'X-WP-Total', (int) $total_users );
       
   315 
       
   316 		$max_pages = ceil( $total_users / $per_page );
       
   317 
       
   318 		$response->header( 'X-WP-TotalPages', (int) $max_pages );
       
   319 
       
   320 		$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
       
   321 		if ( $page > 1 ) {
       
   322 			$prev_page = $page - 1;
       
   323 
       
   324 			if ( $prev_page > $max_pages ) {
       
   325 				$prev_page = $max_pages;
       
   326 			}
       
   327 
       
   328 			$prev_link = add_query_arg( 'page', $prev_page, $base );
       
   329 			$response->link_header( 'prev', $prev_link );
       
   330 		}
       
   331 		if ( $max_pages > $page ) {
       
   332 			$next_page = $page + 1;
       
   333 			$next_link = add_query_arg( 'page', $next_page, $base );
       
   334 
       
   335 			$response->link_header( 'next', $next_link );
       
   336 		}
       
   337 
       
   338 		return $response;
       
   339 	}
       
   340 
       
   341 	/**
       
   342 	 * Get the user, if the ID is valid.
       
   343 	 *
       
   344 	 * @since 4.7.2
       
   345 	 *
       
   346 	 * @param int $id Supplied ID.
       
   347 	 * @return WP_User|WP_Error True if ID is valid, WP_Error otherwise.
       
   348 	 */
       
   349 	protected function get_user( $id ) {
       
   350 		$error = new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) );
       
   351 		if ( (int) $id <= 0 ) {
       
   352 			return $error;
       
   353 		}
       
   354 
       
   355 		$user = get_userdata( (int) $id );
       
   356 		if ( empty( $user ) || ! $user->exists() ) {
       
   357 			return $error;
       
   358 		}
       
   359 
       
   360 		if ( is_multisite() && ! is_user_member_of_blog( $user->ID ) ) {
       
   361 			return $error;
       
   362 		}
       
   363 
       
   364 		return $user;
       
   365 	}
       
   366 
       
   367 	/**
       
   368 	 * Checks if a given request has access to read a user.
       
   369 	 *
       
   370 	 * @since 4.7.0
       
   371 	 *
       
   372 	 * @param WP_REST_Request $request Full details about the request.
       
   373 	 * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
       
   374 	 */
       
   375 	public function get_item_permissions_check( $request ) {
       
   376 		$user = $this->get_user( $request['id'] );
       
   377 		if ( is_wp_error( $user ) ) {
       
   378 			return $user;
       
   379 		}
       
   380 
       
   381 		$types = get_post_types( array( 'show_in_rest' => true ), 'names' );
       
   382 
       
   383 		if ( get_current_user_id() === $user->ID ) {
       
   384 			return true;
       
   385 		}
       
   386 
       
   387 		if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
       
   388 			return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) );
       
   389 		} elseif ( ! count_user_posts( $user->ID, $types ) && ! current_user_can( 'edit_user', $user->ID ) && ! current_user_can( 'list_users' ) ) {
       
   390 			return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) );
       
   391 		}
       
   392 
       
   393 		return true;
       
   394 	}
       
   395 
       
   396 	/**
       
   397 	 * Retrieves a single user.
       
   398 	 *
       
   399 	 * @since 4.7.0
       
   400 	 *
       
   401 	 * @param WP_REST_Request $request Full details about the request.
       
   402 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   403 	 */
       
   404 	public function get_item( $request ) {
       
   405 		$user = $this->get_user( $request['id'] );
       
   406 		if ( is_wp_error( $user ) ) {
       
   407 			return $user;
       
   408 		}
       
   409 
       
   410 		$user = $this->prepare_item_for_response( $user, $request );
       
   411 		$response = rest_ensure_response( $user );
       
   412 
       
   413 		return $response;
       
   414 	}
       
   415 
       
   416 	/**
       
   417 	 * Retrieves the current user.
       
   418 	 *
       
   419 	 * @since 4.7.0
       
   420 	 *
       
   421 	 * @param WP_REST_Request $request Full details about the request.
       
   422 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   423 	 */
       
   424 	public function get_current_item( $request ) {
       
   425 		$current_user_id = get_current_user_id();
       
   426 
       
   427 		if ( empty( $current_user_id ) ) {
       
   428 			return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) );
       
   429 		}
       
   430 
       
   431 		$user     = wp_get_current_user();
       
   432 		$response = $this->prepare_item_for_response( $user, $request );
       
   433 		$response = rest_ensure_response( $response );
       
   434 
       
   435 
       
   436 		return $response;
       
   437 	}
       
   438 
       
   439 	/**
       
   440 	 * Checks if a given request has access create users.
       
   441 	 *
       
   442 	 * @since 4.7.0
       
   443 	 *
       
   444 	 * @param WP_REST_Request $request Full details about the request.
       
   445 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
       
   446 	 */
       
   447 	public function create_item_permissions_check( $request ) {
       
   448 
       
   449 		if ( ! current_user_can( 'create_users' ) ) {
       
   450 			return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create new users.' ), array( 'status' => rest_authorization_required_code() ) );
       
   451 		}
       
   452 
       
   453 		return true;
       
   454 	}
       
   455 
       
   456 	/**
       
   457 	 * Creates a single user.
       
   458 	 *
       
   459 	 * @since 4.7.0
       
   460 	 *
       
   461 	 * @param WP_REST_Request $request Full details about the request.
       
   462 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   463 	 */
       
   464 	public function create_item( $request ) {
       
   465 		if ( ! empty( $request['id'] ) ) {
       
   466 			return new WP_Error( 'rest_user_exists', __( 'Cannot create existing user.' ), array( 'status' => 400 ) );
       
   467 		}
       
   468 
       
   469 		$schema = $this->get_item_schema();
       
   470 
       
   471 		if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) {
       
   472 			$check_permission = $this->check_role_update( $request['id'], $request['roles'] );
       
   473 
       
   474 			if ( is_wp_error( $check_permission ) ) {
       
   475 				return $check_permission;
       
   476 			}
       
   477 		}
       
   478 
       
   479 		$user = $this->prepare_item_for_database( $request );
       
   480 
       
   481 		if ( is_multisite() ) {
       
   482 			$ret = wpmu_validate_user_signup( $user->user_login, $user->user_email );
       
   483 
       
   484 			if ( is_wp_error( $ret['errors'] ) && ! empty( $ret['errors']->errors ) ) {
       
   485 				$error = new WP_Error( 'rest_invalid_param', __( 'Invalid user parameter(s).' ), array( 'status' => 400 ) );
       
   486 				foreach ( $ret['errors']->errors as $code => $messages ) {
       
   487 					foreach ( $messages as $message ) {
       
   488 						$error->add( $code, $message );
       
   489 					}
       
   490 					if ( $error_data = $error->get_error_data( $code ) ) {
       
   491 						$error->add_data( $error_data, $code );
       
   492 					}
       
   493 				}
       
   494 				return $error;
       
   495 			}
       
   496 		}
       
   497 
       
   498 		if ( is_multisite() ) {
       
   499 			$user_id = wpmu_create_user( $user->user_login, $user->user_pass, $user->user_email );
       
   500 
       
   501 			if ( ! $user_id ) {
       
   502 				return new WP_Error( 'rest_user_create', __( 'Error creating new user.' ), array( 'status' => 500 ) );
       
   503 			}
       
   504 
       
   505 			$user->ID = $user_id;
       
   506 			$user_id  = wp_update_user( wp_slash( (array) $user ) );
       
   507 
       
   508 			if ( is_wp_error( $user_id ) ) {
       
   509 				return $user_id;
       
   510 			}
       
   511 
       
   512 			$result= add_user_to_blog( get_site()->id, $user_id, '' );
       
   513 			if ( is_wp_error( $result ) ) {
       
   514 				return $result;
       
   515 			}
       
   516 		} else {
       
   517 			$user_id = wp_insert_user( wp_slash( (array) $user ) );
       
   518 
       
   519 			if ( is_wp_error( $user_id ) ) {
       
   520 				return $user_id;
       
   521 			}
       
   522 		}
       
   523 
       
   524 		$user = get_user_by( 'id', $user_id );
       
   525 
       
   526 		/**
       
   527 		 * Fires immediately after a user is created or updated via the REST API.
       
   528 		 *
       
   529 		 * @since 4.7.0
       
   530 		 *
       
   531 		 * @param WP_User         $user     Inserted or updated user object.
       
   532 		 * @param WP_REST_Request $request  Request object.
       
   533 		 * @param bool            $creating True when creating a user, false when updating.
       
   534 		 */
       
   535 		do_action( 'rest_insert_user', $user, $request, true );
       
   536 
       
   537 		if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) {
       
   538 			array_map( array( $user, 'add_role' ), $request['roles'] );
       
   539 		}
       
   540 
       
   541 		if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
       
   542 			$meta_update = $this->meta->update_value( $request['meta'], $user_id );
       
   543 
       
   544 			if ( is_wp_error( $meta_update ) ) {
       
   545 				return $meta_update;
       
   546 			}
       
   547 		}
       
   548 
       
   549 		$user = get_user_by( 'id', $user_id );
       
   550 		$fields_update = $this->update_additional_fields_for_object( $user, $request );
       
   551 
       
   552 		if ( is_wp_error( $fields_update ) ) {
       
   553 			return $fields_update;
       
   554 		}
       
   555 
       
   556 		$request->set_param( 'context', 'edit' );
       
   557 
       
   558 		$response = $this->prepare_item_for_response( $user, $request );
       
   559 		$response = rest_ensure_response( $response );
       
   560 
       
   561 		$response->set_status( 201 );
       
   562 		$response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user_id ) ) );
       
   563 
       
   564 		return $response;
       
   565 	}
       
   566 
       
   567 	/**
       
   568 	 * Checks if a given request has access to update a user.
       
   569 	 *
       
   570 	 * @since 4.7.0
       
   571 	 *
       
   572 	 * @param WP_REST_Request $request Full details about the request.
       
   573 	 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
       
   574 	 */
       
   575 	public function update_item_permissions_check( $request ) {
       
   576 		$user = $this->get_user( $request['id'] );
       
   577 		if ( is_wp_error( $user ) ) {
       
   578 			return $user;
       
   579 		}
       
   580 
       
   581 		if ( ! empty( $request['roles'] ) ) {
       
   582 			if ( ! current_user_can( 'promote_user', $user->ID ) ) {
       
   583 				return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of this user.' ), array( 'status' => rest_authorization_required_code() ) );
       
   584 			}
       
   585 
       
   586 			$request_params = array_keys( $request->get_params() );
       
   587 			sort( $request_params );
       
   588 			// If only 'id' and 'roles' are specified (we are only trying to
       
   589 			// edit roles), then only the 'promote_user' cap is required.
       
   590 			if ( $request_params === array( 'id', 'roles' ) ) {
       
   591 				return true;
       
   592 			}
       
   593 		}
       
   594 
       
   595 		if ( ! current_user_can( 'edit_user', $user->ID ) ) {
       
   596 			return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this user.' ), array( 'status' => rest_authorization_required_code() ) );
       
   597 		}
       
   598 
       
   599 		return true;
       
   600 	}
       
   601 
       
   602 	/**
       
   603 	 * Updates a single user.
       
   604 	 *
       
   605 	 * @since 4.7.0
       
   606 	 *
       
   607 	 * @param WP_REST_Request $request Full details about the request.
       
   608 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   609 	 */
       
   610 	public function update_item( $request ) {
       
   611 		$user = $this->get_user( $request['id'] );
       
   612 		if ( is_wp_error( $user ) ) {
       
   613 			return $user;
       
   614 		}
       
   615 
       
   616 		$id = $user->ID;
       
   617 
       
   618 		if ( ! $user ) {
       
   619 			return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) );
       
   620 		}
       
   621 
       
   622 		if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) {
       
   623 			return new WP_Error( 'rest_user_invalid_email', __( 'Invalid email address.' ), array( 'status' => 400 ) );
       
   624 		}
       
   625 
       
   626 		if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) {
       
   627 			return new WP_Error( 'rest_user_invalid_argument', __( "Username isn't editable." ), array( 'status' => 400 ) );
       
   628 		}
       
   629 
       
   630 		if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) {
       
   631 			return new WP_Error( 'rest_user_invalid_slug', __( 'Invalid slug.' ), array( 'status' => 400 ) );
       
   632 		}
       
   633 
       
   634 		if ( ! empty( $request['roles'] ) ) {
       
   635 			$check_permission = $this->check_role_update( $id, $request['roles'] );
       
   636 
       
   637 			if ( is_wp_error( $check_permission ) ) {
       
   638 				return $check_permission;
       
   639 			}
       
   640 		}
       
   641 
       
   642 		$user = $this->prepare_item_for_database( $request );
       
   643 
       
   644 		// Ensure we're operating on the same user we already checked.
       
   645 		$user->ID = $id;
       
   646 
       
   647 		$user_id = wp_update_user( wp_slash( (array) $user ) );
       
   648 
       
   649 		if ( is_wp_error( $user_id ) ) {
       
   650 			return $user_id;
       
   651 		}
       
   652 
       
   653 		$user = get_user_by( 'id', $user_id );
       
   654 
       
   655 		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
       
   656 		do_action( 'rest_insert_user', $user, $request, false );
       
   657 
       
   658 		if ( ! empty( $request['roles'] ) ) {
       
   659 			array_map( array( $user, 'add_role' ), $request['roles'] );
       
   660 		}
       
   661 
       
   662 		$schema = $this->get_item_schema();
       
   663 
       
   664 		if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
       
   665 			$meta_update = $this->meta->update_value( $request['meta'], $id );
       
   666 
       
   667 			if ( is_wp_error( $meta_update ) ) {
       
   668 				return $meta_update;
       
   669 			}
       
   670 		}
       
   671 
       
   672 		$user = get_user_by( 'id', $user_id );
       
   673 		$fields_update = $this->update_additional_fields_for_object( $user, $request );
       
   674 
       
   675 		if ( is_wp_error( $fields_update ) ) {
       
   676 			return $fields_update;
       
   677 		}
       
   678 
       
   679 		$request->set_param( 'context', 'edit' );
       
   680 
       
   681 		$response = $this->prepare_item_for_response( $user, $request );
       
   682 		$response = rest_ensure_response( $response );
       
   683 
       
   684 		return $response;
       
   685 	}
       
   686 
       
   687 	/**
       
   688 	 * Checks if a given request has access to update the current user.
       
   689 	 *
       
   690 	 * @since 4.7.0
       
   691 	 *
       
   692 	 * @param WP_REST_Request $request Full details about the request.
       
   693 	 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
       
   694 	 */
       
   695 	public function update_current_item_permissions_check( $request ) {
       
   696 		$request['id'] = get_current_user_id();
       
   697 
       
   698 		return $this->update_item_permissions_check( $request );
       
   699 	}
       
   700 
       
   701 	/**
       
   702 	 * Updates the current user.
       
   703 	 *
       
   704 	 * @since 4.7.0
       
   705 	 *
       
   706 	 * @param WP_REST_Request $request Full details about the request.
       
   707 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   708 	 */
       
   709 	function update_current_item( $request ) {
       
   710 		$request['id'] = get_current_user_id();
       
   711 
       
   712 		return $this->update_item( $request );
       
   713 	}
       
   714 
       
   715 	/**
       
   716 	 * Checks if a given request has access delete a user.
       
   717 	 *
       
   718 	 * @since 4.7.0
       
   719 	 *
       
   720 	 * @param WP_REST_Request $request Full details about the request.
       
   721 	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
       
   722 	 */
       
   723 	public function delete_item_permissions_check( $request ) {
       
   724 		$user = $this->get_user( $request['id'] );
       
   725 		if ( is_wp_error( $user ) ) {
       
   726 			return $user;
       
   727 		}
       
   728 
       
   729 		if ( ! current_user_can( 'delete_user', $user->ID ) ) {
       
   730 			return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => rest_authorization_required_code() ) );
       
   731 		}
       
   732 
       
   733 		return true;
       
   734 	}
       
   735 
       
   736 	/**
       
   737 	 * Deletes a single user.
       
   738 	 *
       
   739 	 * @since 4.7.0
       
   740 	 *
       
   741 	 * @param WP_REST_Request $request Full details about the request.
       
   742 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   743 	 */
       
   744 	public function delete_item( $request ) {
       
   745 		// We don't support delete requests in multisite.
       
   746 		if ( is_multisite() ) {
       
   747 			return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 501 ) );
       
   748 		}
       
   749 		$user = $this->get_user( $request['id'] );
       
   750 		if ( is_wp_error( $user ) ) {
       
   751 			return $user;
       
   752 		}
       
   753 
       
   754 		$id       = $user->ID;
       
   755 		$reassign = false === $request['reassign'] ? null : absint( $request['reassign'] );
       
   756 		$force    = isset( $request['force'] ) ? (bool) $request['force'] : false;
       
   757 
       
   758 		// We don't support trashing for users.
       
   759 		if ( ! $force ) {
       
   760 			/* translators: %s: force=true */
       
   761 			return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Users do not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) );
       
   762 		}
       
   763 
       
   764 		if ( ! empty( $reassign ) ) {
       
   765 			if ( $reassign === $id || ! get_userdata( $reassign ) ) {
       
   766 				return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user ID for reassignment.' ), array( 'status' => 400 ) );
       
   767 			}
       
   768 		}
       
   769 
       
   770 		$request->set_param( 'context', 'edit' );
       
   771 
       
   772 		$previous = $this->prepare_item_for_response( $user, $request );
       
   773 
       
   774 		/** Include admin user functions to get access to wp_delete_user() */
       
   775 		require_once ABSPATH . 'wp-admin/includes/user.php';
       
   776 
       
   777 		$result = wp_delete_user( $id, $reassign );
       
   778 
       
   779 		if ( ! $result ) {
       
   780 			return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 500 ) );
       
   781 		}
       
   782 
       
   783 		$response = new WP_REST_Response();
       
   784 		$response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) );
       
   785 
       
   786 		/**
       
   787 		 * Fires immediately after a user is deleted via the REST API.
       
   788 		 *
       
   789 		 * @since 4.7.0
       
   790 		 *
       
   791 		 * @param WP_User          $user     The user data.
       
   792 		 * @param WP_REST_Response $response The response returned from the API.
       
   793 		 * @param WP_REST_Request  $request  The request sent to the API.
       
   794 		 */
       
   795 		do_action( 'rest_delete_user', $user, $response, $request );
       
   796 
       
   797 		return $response;
       
   798 	}
       
   799 
       
   800 	/**
       
   801 	 * Checks if a given request has access to delete the current user.
       
   802 	 *
       
   803 	 * @since 4.7.0
       
   804 	 *
       
   805 	 * @param WP_REST_Request $request Full details about the request.
       
   806 	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
       
   807 	 */
       
   808 	public function delete_current_item_permissions_check( $request ) {
       
   809 		$request['id'] = get_current_user_id();
       
   810 
       
   811 		return $this->delete_item_permissions_check( $request );
       
   812 	}
       
   813 
       
   814 	/**
       
   815 	 * Deletes the current user.
       
   816 	 *
       
   817 	 * @since 4.7.0
       
   818 	 *
       
   819 	 * @param WP_REST_Request $request Full details about the request.
       
   820 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   821 	 */
       
   822 	function delete_current_item( $request ) {
       
   823 		$request['id'] = get_current_user_id();
       
   824 
       
   825 		return $this->delete_item( $request );
       
   826 	}
       
   827 
       
   828 	/**
       
   829 	 * Prepares a single user output for response.
       
   830 	 *
       
   831 	 * @since 4.7.0
       
   832 	 *
       
   833 	 * @param WP_User         $user    User object.
       
   834 	 * @param WP_REST_Request $request Request object.
       
   835 	 * @return WP_REST_Response Response object.
       
   836 	 */
       
   837 	public function prepare_item_for_response( $user, $request ) {
       
   838 
       
   839 		$data   = array();
       
   840 		$fields = $this->get_fields_for_response( $request );
       
   841 
       
   842 		if ( in_array( 'id', $fields, true ) ) {
       
   843 			$data['id'] = $user->ID;
       
   844 		}
       
   845 
       
   846 		if ( in_array( 'username', $fields, true ) ) {
       
   847 			$data['username'] = $user->user_login;
       
   848 		}
       
   849 
       
   850 		if ( in_array( 'name', $fields, true ) ) {
       
   851 			$data['name'] = $user->display_name;
       
   852 		}
       
   853 
       
   854 		if ( in_array( 'first_name', $fields, true ) ) {
       
   855 			$data['first_name'] = $user->first_name;
       
   856 		}
       
   857 
       
   858 		if ( in_array( 'last_name', $fields, true ) ) {
       
   859 			$data['last_name'] = $user->last_name;
       
   860 		}
       
   861 
       
   862 		if ( in_array( 'email', $fields, true ) ) {
       
   863 			$data['email'] = $user->user_email;
       
   864 		}
       
   865 
       
   866 		if ( in_array( 'url', $fields, true ) ) {
       
   867 			$data['url'] = $user->user_url;
       
   868 		}
       
   869 
       
   870 		if ( in_array( 'description', $fields, true ) ) {
       
   871 			$data['description'] = $user->description;
       
   872 		}
       
   873 
       
   874 		if ( in_array( 'link', $fields, true ) ) {
       
   875 			$data['link'] = get_author_posts_url( $user->ID, $user->user_nicename );
       
   876 		}
       
   877 
       
   878 		if ( in_array( 'locale', $fields, true ) ) {
       
   879 			$data['locale'] = get_user_locale( $user );
       
   880 		}
       
   881 
       
   882 		if ( in_array( 'nickname', $fields, true ) ) {
       
   883 			$data['nickname'] = $user->nickname;
       
   884 		}
       
   885 
       
   886 		if ( in_array( 'slug', $fields, true ) ) {
       
   887 			$data['slug'] = $user->user_nicename;
       
   888 		}
       
   889 
       
   890 		if ( in_array( 'roles', $fields, true ) ) {
       
   891 			// Defensively call array_values() to ensure an array is returned.
       
   892 			$data['roles'] = array_values( $user->roles );
       
   893 		}
       
   894 
       
   895 		if ( in_array( 'registered_date', $fields, true ) ) {
       
   896 			$data['registered_date'] = date( 'c', strtotime( $user->user_registered ) );
       
   897 		}
       
   898 
       
   899 		if ( in_array( 'capabilities', $fields, true ) ) {
       
   900 			$data['capabilities'] = (object) $user->allcaps;
       
   901 		}
       
   902 
       
   903 		if ( in_array( 'extra_capabilities', $fields, true ) ) {
       
   904 			$data['extra_capabilities'] = (object) $user->caps;
       
   905 		}
       
   906 
       
   907 		if ( in_array( 'avatar_urls', $fields, true ) ) {
       
   908 			$data['avatar_urls'] = rest_get_avatar_urls( $user->user_email );
       
   909 		}
       
   910 
       
   911 		if ( in_array( 'meta', $fields, true ) ) {
       
   912 			$data['meta'] = $this->meta->get_value( $user->ID, $request );
       
   913 		}
       
   914 
       
   915 		$context = ! empty( $request['context'] ) ? $request['context'] : 'embed';
       
   916 
       
   917 		$data = $this->add_additional_fields_to_object( $data, $request );
       
   918 		$data = $this->filter_response_by_context( $data, $context );
       
   919 
       
   920 		// Wrap the data in a response object.
       
   921 		$response = rest_ensure_response( $data );
       
   922 
       
   923 		$response->add_links( $this->prepare_links( $user ) );
       
   924 
       
   925 		/**
       
   926 		 * Filters user data returned from the REST API.
       
   927 		 *
       
   928 		 * @since 4.7.0
       
   929 		 *
       
   930 		 * @param WP_REST_Response $response The response object.
       
   931 		 * @param object           $user     User object used to create response.
       
   932 		 * @param WP_REST_Request  $request  Request object.
       
   933 		 */
       
   934 		return apply_filters( 'rest_prepare_user', $response, $user, $request );
       
   935 	}
       
   936 
       
   937 	/**
       
   938 	 * Prepares links for the user request.
       
   939 	 *
       
   940 	 * @since 4.7.0
       
   941 	 *
       
   942 	 * @param WP_Post $user User object.
       
   943 	 * @return array Links for the given user.
       
   944 	 */
       
   945 	protected function prepare_links( $user ) {
       
   946 		$links = array(
       
   947 			'self' => array(
       
   948 				'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user->ID ) ),
       
   949 			),
       
   950 			'collection' => array(
       
   951 				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
       
   952 			),
       
   953 		);
       
   954 
       
   955 		return $links;
       
   956 	}
       
   957 
       
   958 	/**
       
   959 	 * Prepares a single user for creation or update.
       
   960 	 *
       
   961 	 * @since 4.7.0
       
   962 	 *
       
   963 	 * @param WP_REST_Request $request Request object.
       
   964 	 * @return object $prepared_user User object.
       
   965 	 */
       
   966 	protected function prepare_item_for_database( $request ) {
       
   967 		$prepared_user = new stdClass;
       
   968 
       
   969 		$schema = $this->get_item_schema();
       
   970 
       
   971 		// required arguments.
       
   972 		if ( isset( $request['email'] ) && ! empty( $schema['properties']['email'] ) ) {
       
   973 			$prepared_user->user_email = $request['email'];
       
   974 		}
       
   975 
       
   976 		if ( isset( $request['username'] ) && ! empty( $schema['properties']['username'] ) ) {
       
   977 			$prepared_user->user_login = $request['username'];
       
   978 		}
       
   979 
       
   980 		if ( isset( $request['password'] ) && ! empty( $schema['properties']['password'] ) ) {
       
   981 			$prepared_user->user_pass = $request['password'];
       
   982 		}
       
   983 
       
   984 		// optional arguments.
       
   985 		if ( isset( $request['id'] ) ) {
       
   986 			$prepared_user->ID = absint( $request['id'] );
       
   987 		}
       
   988 
       
   989 		if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) {
       
   990 			$prepared_user->display_name = $request['name'];
       
   991 		}
       
   992 
       
   993 		if ( isset( $request['first_name'] ) && ! empty( $schema['properties']['first_name'] ) ) {
       
   994 			$prepared_user->first_name = $request['first_name'];
       
   995 		}
       
   996 
       
   997 		if ( isset( $request['last_name'] ) && ! empty( $schema['properties']['last_name'] ) ) {
       
   998 			$prepared_user->last_name = $request['last_name'];
       
   999 		}
       
  1000 
       
  1001 		if ( isset( $request['nickname'] ) && ! empty( $schema['properties']['nickname'] ) ) {
       
  1002 			$prepared_user->nickname = $request['nickname'];
       
  1003 		}
       
  1004 
       
  1005 		if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) {
       
  1006 			$prepared_user->user_nicename = $request['slug'];
       
  1007 		}
       
  1008 
       
  1009 		if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) {
       
  1010 			$prepared_user->description = $request['description'];
       
  1011 		}
       
  1012 
       
  1013 		if ( isset( $request['url'] ) && ! empty( $schema['properties']['url'] ) ) {
       
  1014 			$prepared_user->user_url = $request['url'];
       
  1015 		}
       
  1016 
       
  1017 		if ( isset( $request['locale'] ) && ! empty( $schema['properties']['locale'] ) ) {
       
  1018 			$prepared_user->locale = $request['locale'];
       
  1019 		}
       
  1020 
       
  1021 		// setting roles will be handled outside of this function.
       
  1022 		if ( isset( $request['roles'] ) ) {
       
  1023 			$prepared_user->role = false;
       
  1024 		}
       
  1025 
       
  1026 		/**
       
  1027 		 * Filters user data before insertion via the REST API.
       
  1028 		 *
       
  1029 		 * @since 4.7.0
       
  1030 		 *
       
  1031 		 * @param object          $prepared_user User object.
       
  1032 		 * @param WP_REST_Request $request       Request object.
       
  1033 		 */
       
  1034 		return apply_filters( 'rest_pre_insert_user', $prepared_user, $request );
       
  1035 	}
       
  1036 
       
  1037 	/**
       
  1038 	 * Determines if the current user is allowed to make the desired roles change.
       
  1039 	 *
       
  1040 	 * @since 4.7.0
       
  1041 	 *
       
  1042 	 * @param integer $user_id User ID.
       
  1043 	 * @param array   $roles   New user roles.
       
  1044 	 * @return true|WP_Error True if the current user is allowed to make the role change,
       
  1045 	 *                       otherwise a WP_Error object.
       
  1046 	 */
       
  1047 	protected function check_role_update( $user_id, $roles ) {
       
  1048 		global $wp_roles;
       
  1049 
       
  1050 		foreach ( $roles as $role ) {
       
  1051 
       
  1052 			if ( ! isset( $wp_roles->role_objects[ $role ] ) ) {
       
  1053 				/* translators: %s: role key */
       
  1054 				return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 400 ) );
       
  1055 			}
       
  1056 
       
  1057 			$potential_role = $wp_roles->role_objects[ $role ];
       
  1058 
       
  1059 			/*
       
  1060 			 * Don't let anyone with 'edit_users' (admins) edit their own role to something without it.
       
  1061 			 * Multisite super admins can freely edit their blog roles -- they possess all caps.
       
  1062 			 */
       
  1063 			if ( ! ( is_multisite()
       
  1064 				&& current_user_can( 'manage_sites' ) )
       
  1065 				&& get_current_user_id() === $user_id
       
  1066 				&& ! $potential_role->has_cap( 'edit_users' )
       
  1067 			) {
       
  1068 				return new WP_Error( 'rest_user_invalid_role', __( 'Sorry, you are not allowed to give users that role.' ), array( 'status' => rest_authorization_required_code() ) );
       
  1069 			}
       
  1070 
       
  1071 			/** Include admin functions to get access to get_editable_roles() */
       
  1072 			require_once ABSPATH . 'wp-admin/includes/admin.php';
       
  1073 
       
  1074 			// The new role must be editable by the logged-in user.
       
  1075 			$editable_roles = get_editable_roles();
       
  1076 
       
  1077 			if ( empty( $editable_roles[ $role ] ) ) {
       
  1078 				return new WP_Error( 'rest_user_invalid_role', __( 'Sorry, you are not allowed to give users that role.' ), array( 'status' => 403 ) );
       
  1079 			}
       
  1080 		}
       
  1081 
       
  1082 		return true;
       
  1083 	}
       
  1084 
       
  1085 	/**
       
  1086 	 * Check a username for the REST API.
       
  1087 	 *
       
  1088 	 * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
       
  1089 	 *
       
  1090 	 * @since 4.7.0
       
  1091 	 *
       
  1092 	 * @param  mixed            $value   The username submitted in the request.
       
  1093 	 * @param  WP_REST_Request  $request Full details about the request.
       
  1094 	 * @param  string           $param   The parameter name.
       
  1095 	 * @return WP_Error|string The sanitized username, if valid, otherwise an error.
       
  1096 	 */
       
  1097 	public function check_username( $value, $request, $param ) {
       
  1098 		$username = (string) $value;
       
  1099 
       
  1100 		if ( ! validate_username( $username ) ) {
       
  1101 			return new WP_Error( 'rest_user_invalid_username', __( 'Username contains invalid characters.' ), array( 'status' => 400 ) );
       
  1102 		}
       
  1103 
       
  1104 		/** This filter is documented in wp-includes/user.php */
       
  1105 		$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
       
  1106 
       
  1107 		if ( in_array( strtolower( $username ), array_map( 'strtolower', $illegal_logins ) ) ) {
       
  1108 			return new WP_Error( 'rest_user_invalid_username', __( 'Sorry, that username is not allowed.' ), array( 'status' => 400 ) );
       
  1109 		}
       
  1110 
       
  1111 		return $username;
       
  1112 	}
       
  1113 
       
  1114 	/**
       
  1115 	 * Check a user password for the REST API.
       
  1116 	 *
       
  1117 	 * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
       
  1118 	 *
       
  1119 	 * @since 4.7.0
       
  1120 	 *
       
  1121 	 * @param  mixed            $value   The password submitted in the request.
       
  1122 	 * @param  WP_REST_Request  $request Full details about the request.
       
  1123 	 * @param  string           $param   The parameter name.
       
  1124 	 * @return WP_Error|string The sanitized password, if valid, otherwise an error.
       
  1125 	 */
       
  1126 	public function check_user_password( $value, $request, $param ) {
       
  1127 		$password = (string) $value;
       
  1128 
       
  1129 		if ( empty( $password ) ) {
       
  1130 			return new WP_Error( 'rest_user_invalid_password', __( 'Passwords cannot be empty.' ), array( 'status' => 400 ) );
       
  1131 		}
       
  1132 
       
  1133 		if ( false !== strpos( $password, "\\" ) ) {
       
  1134 			return new WP_Error( 'rest_user_invalid_password', __( 'Passwords cannot contain the "\\" character.' ), array( 'status' => 400 ) );
       
  1135 		}
       
  1136 
       
  1137 		return $password;
       
  1138 	}
       
  1139 
       
  1140 	/**
       
  1141 	 * Retrieves the user's schema, conforming to JSON Schema.
       
  1142 	 *
       
  1143 	 * @since 4.7.0
       
  1144 	 *
       
  1145 	 * @return array Item schema data.
       
  1146 	 */
       
  1147 	public function get_item_schema() {
       
  1148 		$schema = array(
       
  1149 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
       
  1150 			'title'      => 'user',
       
  1151 			'type'       => 'object',
       
  1152 			'properties' => array(
       
  1153 				'id'          => array(
       
  1154 					'description' => __( 'Unique identifier for the user.' ),
       
  1155 					'type'        => 'integer',
       
  1156 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1157 					'readonly'    => true,
       
  1158 				),
       
  1159 				'username'    => array(
       
  1160 					'description' => __( 'Login name for the user.' ),
       
  1161 					'type'        => 'string',
       
  1162 					'context'     => array( 'edit' ),
       
  1163 					'required'    => true,
       
  1164 					'arg_options' => array(
       
  1165 						'sanitize_callback' => array( $this, 'check_username' ),
       
  1166 					),
       
  1167 				),
       
  1168 				'name'        => array(
       
  1169 					'description' => __( 'Display name for the user.' ),
       
  1170 					'type'        => 'string',
       
  1171 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1172 					'arg_options' => array(
       
  1173 						'sanitize_callback' => 'sanitize_text_field',
       
  1174 					),
       
  1175 				),
       
  1176 				'first_name'  => array(
       
  1177 					'description' => __( 'First name for the user.' ),
       
  1178 					'type'        => 'string',
       
  1179 					'context'     => array( 'edit' ),
       
  1180 					'arg_options' => array(
       
  1181 						'sanitize_callback' => 'sanitize_text_field',
       
  1182 					),
       
  1183 				),
       
  1184 				'last_name'   => array(
       
  1185 					'description' => __( 'Last name for the user.' ),
       
  1186 					'type'        => 'string',
       
  1187 					'context'     => array( 'edit' ),
       
  1188 					'arg_options' => array(
       
  1189 						'sanitize_callback' => 'sanitize_text_field',
       
  1190 					),
       
  1191 				),
       
  1192 				'email'       => array(
       
  1193 					'description' => __( 'The email address for the user.' ),
       
  1194 					'type'        => 'string',
       
  1195 					'format'      => 'email',
       
  1196 					'context'     => array( 'edit' ),
       
  1197 					'required'    => true,
       
  1198 				),
       
  1199 				'url'         => array(
       
  1200 					'description' => __( 'URL of the user.' ),
       
  1201 					'type'        => 'string',
       
  1202 					'format'      => 'uri',
       
  1203 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1204 				),
       
  1205 				'description' => array(
       
  1206 					'description' => __( 'Description of the user.' ),
       
  1207 					'type'        => 'string',
       
  1208 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1209 				),
       
  1210 				'link'        => array(
       
  1211 					'description' => __( 'Author URL of the user.' ),
       
  1212 					'type'        => 'string',
       
  1213 					'format'      => 'uri',
       
  1214 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1215 					'readonly'    => true,
       
  1216 				),
       
  1217 				'locale'    => array(
       
  1218 					'description' => __( 'Locale for the user.' ),
       
  1219 					'type'        => 'string',
       
  1220 					'enum'        => array_merge( array( '', 'en_US' ), get_available_languages() ),
       
  1221 					'context'     => array( 'edit' ),
       
  1222 				),
       
  1223 				'nickname'    => array(
       
  1224 					'description' => __( 'The nickname for the user.' ),
       
  1225 					'type'        => 'string',
       
  1226 					'context'     => array( 'edit' ),
       
  1227 					'arg_options' => array(
       
  1228 						'sanitize_callback' => 'sanitize_text_field',
       
  1229 					),
       
  1230 				),
       
  1231 				'slug'        => array(
       
  1232 					'description' => __( 'An alphanumeric identifier for the user.' ),
       
  1233 					'type'        => 'string',
       
  1234 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1235 					'arg_options' => array(
       
  1236 						'sanitize_callback' => array( $this, 'sanitize_slug' ),
       
  1237 					),
       
  1238 				),
       
  1239 				'registered_date' => array(
       
  1240 					'description' => __( 'Registration date for the user.' ),
       
  1241 					'type'        => 'string',
       
  1242 					'format'      => 'date-time',
       
  1243 					'context'     => array( 'edit' ),
       
  1244 					'readonly'    => true,
       
  1245 				),
       
  1246 				'roles'           => array(
       
  1247 					'description' => __( 'Roles assigned to the user.' ),
       
  1248 					'type'        => 'array',
       
  1249 					'items'       => array(
       
  1250 						'type'    => 'string',
       
  1251 					),
       
  1252 					'context'     => array( 'edit' ),
       
  1253 				),
       
  1254 				'password'        => array(
       
  1255 					'description' => __( 'Password for the user (never included).' ),
       
  1256 					'type'        => 'string',
       
  1257 					'context'     => array(), // Password is never displayed.
       
  1258 					'required'    => true,
       
  1259 					'arg_options' => array(
       
  1260 						'sanitize_callback' => array( $this, 'check_user_password' ),
       
  1261 					),
       
  1262 				),
       
  1263 				'capabilities'    => array(
       
  1264 					'description' => __( 'All capabilities assigned to the user.' ),
       
  1265 					'type'        => 'object',
       
  1266 					'context'     => array( 'edit' ),
       
  1267 					'readonly'    => true,
       
  1268 				),
       
  1269 				'extra_capabilities' => array(
       
  1270 					'description' => __( 'Any extra capabilities assigned to the user.' ),
       
  1271 					'type'        => 'object',
       
  1272 					'context'     => array( 'edit' ),
       
  1273 					'readonly'    => true,
       
  1274 				),
       
  1275 			),
       
  1276 		);
       
  1277 
       
  1278 		if ( get_option( 'show_avatars' ) ) {
       
  1279 			$avatar_properties = array();
       
  1280 
       
  1281 			$avatar_sizes = rest_get_avatar_sizes();
       
  1282 
       
  1283 			foreach ( $avatar_sizes as $size ) {
       
  1284 				$avatar_properties[ $size ] = array(
       
  1285 					/* translators: %d: avatar image size in pixels */
       
  1286 					'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),
       
  1287 					'type'        => 'string',
       
  1288 					'format'      => 'uri',
       
  1289 					'context'     => array( 'embed', 'view', 'edit' ),
       
  1290 				);
       
  1291 			}
       
  1292 
       
  1293 			$schema['properties']['avatar_urls']  = array(
       
  1294 				'description' => __( 'Avatar URLs for the user.' ),
       
  1295 				'type'        => 'object',
       
  1296 				'context'     => array( 'embed', 'view', 'edit' ),
       
  1297 				'readonly'    => true,
       
  1298 				'properties'  => $avatar_properties,
       
  1299 			);
       
  1300 		}
       
  1301 
       
  1302 		$schema['properties']['meta'] = $this->meta->get_field_schema();
       
  1303 
       
  1304 		return $this->add_additional_fields_schema( $schema );
       
  1305 	}
       
  1306 
       
  1307 	/**
       
  1308 	 * Retrieves the query params for collections.
       
  1309 	 *
       
  1310 	 * @since 4.7.0
       
  1311 	 *
       
  1312 	 * @return array Collection parameters.
       
  1313 	 */
       
  1314 	public function get_collection_params() {
       
  1315 		$query_params = parent::get_collection_params();
       
  1316 
       
  1317 		$query_params['context']['default'] = 'view';
       
  1318 
       
  1319 		$query_params['exclude'] = array(
       
  1320 			'description'        => __( 'Ensure result set excludes specific IDs.' ),
       
  1321 			'type'               => 'array',
       
  1322 			'items'              => array(
       
  1323 				'type'           => 'integer',
       
  1324 			),
       
  1325 			'default'            => array(),
       
  1326 		);
       
  1327 
       
  1328 		$query_params['include'] = array(
       
  1329 			'description'        => __( 'Limit result set to specific IDs.' ),
       
  1330 			'type'               => 'array',
       
  1331 			'items'              => array(
       
  1332 				'type'           => 'integer',
       
  1333 			),
       
  1334 			'default'            => array(),
       
  1335 		);
       
  1336 
       
  1337 		$query_params['offset'] = array(
       
  1338 			'description'        => __( 'Offset the result set by a specific number of items.' ),
       
  1339 			'type'               => 'integer',
       
  1340 		);
       
  1341 
       
  1342 		$query_params['order'] = array(
       
  1343 			'default'            => 'asc',
       
  1344 			'description'        => __( 'Order sort attribute ascending or descending.' ),
       
  1345 			'enum'               => array( 'asc', 'desc' ),
       
  1346 			'type'               => 'string',
       
  1347 		);
       
  1348 
       
  1349 		$query_params['orderby'] = array(
       
  1350 			'default'            => 'name',
       
  1351 			'description'        => __( 'Sort collection by object attribute.' ),
       
  1352 			'enum'               => array(
       
  1353 				'id',
       
  1354 				'include',
       
  1355 				'name',
       
  1356 				'registered_date',
       
  1357 				'slug',
       
  1358 				'include_slugs',
       
  1359 				'email',
       
  1360 				'url',
       
  1361 			),
       
  1362 			'type'               => 'string',
       
  1363 		);
       
  1364 
       
  1365 		$query_params['slug']    = array(
       
  1366 			'description'        => __( 'Limit result set to users with one or more specific slugs.' ),
       
  1367 			'type'               => 'array',
       
  1368 			'items'              => array(
       
  1369 				'type'               => 'string',
       
  1370 			),
       
  1371 		);
       
  1372 
       
  1373 		$query_params['roles']   = array(
       
  1374 			'description'        => __( 'Limit result set to users matching at least one specific role provided. Accepts csv list or single role.' ),
       
  1375 			'type'               => 'array',
       
  1376 			'items'              => array(
       
  1377 				'type'           => 'string',
       
  1378 			),
       
  1379 		);
       
  1380 
       
  1381 		$query_params['who'] = array(
       
  1382 			'description' => __( 'Limit result set to users who are considered authors.' ),
       
  1383 			'type'        => 'string',
       
  1384 			'enum'        => array(
       
  1385 				'authors',
       
  1386 			),
       
  1387 		);
       
  1388 
       
  1389 		/**
       
  1390 		 * Filter collection parameters for the users controller.
       
  1391 		 *
       
  1392 		 * This filter registers the collection parameter, but does not map the
       
  1393 		 * collection parameter to an internal WP_User_Query parameter.  Use the
       
  1394 		 * `rest_user_query` filter to set WP_User_Query arguments.
       
  1395 		 *
       
  1396 		 * @since 4.7.0
       
  1397 		 *
       
  1398 		 * @param array $query_params JSON Schema-formatted collection parameters.
       
  1399 		 */
       
  1400 		return apply_filters( 'rest_user_collection_params', $query_params );
       
  1401 	}
       
  1402 }