Keine Beschreibung

class-wp-rest-users-controller.php 45KB

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