[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-users-controller.php (source)

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


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref