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

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