ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-rest-users-controller.php (45745B)


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