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