<?php

/**
 * REST API: WP_REST_Users_Controller class
 *
 * @package    WordPress
 * @subpackage REST_API
 * @since      4.7.0
 */

/**
 * Class used to manage ProjectHuddle users via the REST API.
 *
 * @since 2.7.0
 *
 * @see   WP_REST_Controller
 */
class PH_REST_Users_Controller extends WP_REST_Controller
{

	/**
	 * Instance of a user meta fields object.
	 *
	 * @since  4.7.0
	 * @access protected
	 * @var WP_REST_User_Meta_Fields
	 */
	protected $meta;

	/**
	 * Constructor.
	 *
	 * @since  4.7.0
	 * @access public
	 */
	public function __construct()
	{
		$this->namespace = 'projecthuddle/v2';
		$this->rest_base = 'users';

		$this->meta = new WP_REST_User_Meta_Fields();
	}

	/**
	 * Registers the routes for the objects of the controller.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @see    register_rest_route()
	 */
	public function register_routes()
	{

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array($this, 'get_items'),
					'permission_callback' => array($this, 'get_items_permissions_check'),
					'args'                => $this->get_collection_params(),
				),
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array($this, 'create_item'),
					'permission_callback' => array($this, 'create_item_permissions_check'),
					'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
				),
				'schema' => array($this, 'get_public_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				'args'   => array(
					'id' => array(
						'description' => __('Unique identifier for the user.'),
						'type'        => 'integer',
					),
				),
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array($this, 'get_item'),
					'permission_callback' => array($this, 'get_item_permissions_check'),
					'args'                => array(
						'context' => $this->get_context_param(array('default' => 'view')),
					),
				),
				array(
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => array($this, 'update_item'),
					'permission_callback' => array($this, 'update_item_permissions_check'),
					'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
				),
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array($this, 'delete_item'),
					'permission_callback' => array($this, 'delete_item_permissions_check'),
					'args'                => array(
						'force'    => array(
							'type'        => 'boolean',
							'default'     => false,
							'description' => __('Required to be true, as users do not support trashing.'),
						),
						'reassign' => array(
							'type'              => 'integer',
							'description'       => __('Reassign the deleted user\'s posts and links to this user ID.'),
							'required'          => true,
							'sanitize_callback' => array($this, 'check_reassign'),
						),
					),
				),
				'schema' => array($this, 'get_public_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/me',
			array(
				array(
					'methods'  => WP_REST_Server::READABLE,
					'callback' => array($this, 'get_current_item'),
					'permission_callback' => '__return_true',
					'args'     => array(
						'context' => $this->get_context_param(array('default' => 'view')),
					),
				),
				array(
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => array($this, 'update_current_item'),
					'permission_callback' => array($this, 'update_current_item_permissions_check'),
					'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
				),
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array($this, 'delete_current_item'),
					'permission_callback' => array($this, 'delete_current_item_permissions_check'),
					'args'                => array(
						'force'    => array(
							'type'        => 'boolean',
							'default'     => false,
							'description' => __('Required to be true, as users do not support trashing.'),
						),
						'reassign' => array(
							'type'              => 'integer',
							'description'       => __('Reassign the deleted user\'s posts and links to this user ID.'),
							'required'          => true,
							'sanitize_callback' => array($this, 'check_reassign'),
						),
					),
				),
				'schema' => array($this, 'get_public_item_schema'),
			)
		);

		// sign in route
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/login',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array($this, 'login'),
					'permission_callback' => array($this, 'login_permissions_check'),
					'args'                => array(
						'context' => $this->get_context_param(array('default' => 'view')),
						'access_token' => array(
							'type'        => 'string',
							'default'     => '',
							'description' => __('Access send access token optionally if cookie is not stored.'),
						),
					),
				),
				'schema' => array($this, 'get_login_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/logout',
			[
				[
					'methods' => WP_REST_Server::CREATABLE,
					'callback' => [$this, 'logout'],
					'permission_callback' => [$this, 'logout_permissions_check'],
				]
			]
		);

		// reset password route
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/reset',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array($this, 'reset'),
					'permission_callback' => array($this, 'reset_permissions_check'),
					'args'                => array(
						'context' => $this->get_context_param(array('default' => 'view')),
					),
				),
			)
		);
	}

	/**
	 * Checks for a valid value for the reassign parameter when deleting users.
	 *
	 * The value can be an integer, 'false', false, or ''.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param int|bool        $value   The value passed to the reassign parameter.
	 * @param WP_REST_Request $request Full details about the request.
	 * @param string          $param   The parameter that is being sanitized.
	 *
	 * @return int|bool|WP_Error
	 */
	public function check_reassign($value, $request, $param)
	{
		if (is_numeric($value)) {
			return $value;
		}

		if (empty($value) || false === $value || 'false' === $value) {
			return false;
		}

		return new WP_Error('rest_invalid_param', __('Invalid user parameter(s).'), array('status' => 400));
	}

	/**
	 * Permissions check for getting all users.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has read access, otherwise WP_Error object.
	 */
	public function get_items_permissions_check($request)
	{
		// Check if roles is specified in GET request and if user can list users.
		if (!empty($request['roles']) && !current_user_can('list_users')) {
			return new WP_Error('rest_user_cannot_view', __('Sorry, you are not allowed to filter users by role.'), array('status' => rest_authorization_required_code()));
		}

		if ('edit' === $request['context'] && !current_user_can('list_users')) {
			return new WP_Error('rest_forbidden_context', __('Sorry, you are not allowed to list users.'), array('status' => rest_authorization_required_code()));
		}

		if (in_array(
			$request['orderby'],
			array(
				'email',
				'registered_date',
			),
			true
		) && !current_user_can('list_users')) {
			return new WP_Error('rest_forbidden_orderby', __('Sorry, you are not allowed to order users by this parameter.'), array('status' => rest_authorization_required_code()));
		}

		return true;
	}

	/**
	 * Retrieves all users.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function get_items($request)
	{

		// Retrieve the list of registered collection query parameters.
		$registered = $this->get_collection_params();

		/*
		 * This array defines mappings between public API query parameters whose
		 * values are accepted as-passed, and their internal WP_Query parameter
		 * name equivalents (some are the same). Only values which are also
		 * present in $registered will be set.
		 */
		$parameter_mappings = array(
			'exclude'  => 'exclude',
			'include'  => 'include',
			'order'    => 'order',
			'per_page' => 'number',
			'search'   => 'search',
			'roles'    => 'role__in',
			'slug'     => 'nicename__in',
		);

		$prepared_args = array();

		/*
		 * For each known parameter which is both registered and present in the request,
		 * set the parameter's value on the query $prepared_args.
		 */
		foreach ($parameter_mappings as $api_param => $wp_param) {
			if (isset($registered[$api_param], $request[$api_param])) {
				$prepared_args[$wp_param] = $request[$api_param];
			}
		}

		if (isset($registered['offset']) && !empty($request['offset'])) {
			$prepared_args['offset'] = $request['offset'];
		} else {
			$prepared_args['offset'] = ($request['page'] - 1) * $prepared_args['number'];
		}

		if (isset($registered['orderby'])) {
			$orderby_possibles        = array(
				'id'              => 'ID',
				'include'         => 'include',
				'name'            => 'display_name',
				'registered_date' => 'registered',
				'slug'            => 'user_nicename',
				'email'           => 'user_email',
				'url'             => 'user_url',
			);
			$prepared_args['orderby'] = $orderby_possibles[$request['orderby']];
		}

		// if current user can't list all names
		if (!current_user_can('list_users')) {
			// default to only those with published posts
			$prepared_args['has_published_posts'] = get_post_types(array('show_in_rest' => true), 'names');

			// if we have a project arg
			if (isset($registered['project'])) {
				// get the request
				$project =  !empty($request['project']) ? $request['project'] : 0;

				// use current post if project is not defined
				global $post;
				if (!$project  && is_a($post, 'WP_Post')) {
					$project = $post->ID;
				}

				// let user get all users if they are a project member
				if (!empty($project) && ph_user_is_member($project)) {
					unset($prepared_args['has_published_posts']);
				}

				// let them list users if they have a valid access token
				if ($project && $access = PH()->session->get('project_access')) {
					$token = isset($access[$project]) ? $access[$project] : false;
					$validator = new PH_Permissions_Controller($post->ID, '', $token, '', '');
					if ($validator->visitor_can_access()) {
						unset($prepared_args['has_published_posts']);
					}
				}
			}
		} else {
			unset($prepared_args['has_published_posts']);
		}

		if (!empty($prepared_args['search'])) {
			$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
		}

		// if we are querying by project, include only relevant user ids in query.
		if (isset($registered['project']) && !empty($request['project'])) {
			$ids = ph_get_project_member_ids($request['project']);

			$prepared_args['include'] = !empty($prepared_args['include']) ? $prepared_args['include'] : array();

			// always include author
			if ($author_id = get_post_field('post_author', $request['project'])) {
				$prepared_args['include'][] = $author_id;
			}

			// include only member user ids.
			if (is_array($prepared_args['include'])) {
				$prepared_args['include'] = array_merge($ids, $prepared_args['include']);
			} else {
				$prepared_args['include'] = array($ids);
			}
		}

		/**
		 * Filters WP_User_Query arguments when querying users via the REST API.
		 *
		 * @link  https://developer.wordpress.org/reference/classes/wp_user_query/
		 *
		 * @since 4.7.0
		 *
		 * @param array           $prepared_args Array of arguments for WP_User_Query.
		 * @param WP_REST_Request $request       The current request.
		 */
		$prepared_args = apply_filters('ph_rest_user_query', $prepared_args, $request);

		$query = new WP_User_Query($prepared_args);

		$users = array();

		foreach ($query->results as $user) {
			$data    = $this->prepare_item_for_response($user, $request);
			$users[] = $this->prepare_response_for_collection($data);
		}

		$response = rest_ensure_response($users);

		// Store pagination values for headers then unset for count query.
		$per_page = (int) $prepared_args['number'];
		$page     = ceil((((int) $prepared_args['offset']) / $per_page) + 1);

		$prepared_args['fields'] = 'ID';

		$total_users = $query->get_total();

		if ($total_users < 1) {
			// Out-of-bounds, run the query again without LIMIT for total count.
			unset($prepared_args['number'], $prepared_args['offset']);
			$count_query = new WP_User_Query($prepared_args);
			$total_users = $count_query->get_total();
		}

		$response->header('X-WP-Total', (int) $total_users);

		$max_pages = ceil($total_users / $per_page);

		$response->header('X-WP-TotalPages', (int) $max_pages);

		$base = add_query_arg($request->get_query_params(), rest_url(sprintf('%s/%s', $this->namespace, $this->rest_base)));
		if ($page > 1) {
			$prev_page = $page - 1;

			if ($prev_page > $max_pages) {
				$prev_page = $max_pages;
			}

			$prev_link = add_query_arg('page', $prev_page, $base);
			$response->link_header('prev', $prev_link);
		}
		if ($max_pages > $page) {
			$next_page = $page + 1;
			$next_link = add_query_arg('page', $next_page, $base);

			$response->link_header('next', $next_link);
		}

		return $response;
	}

	/**
	 * Get the user, if the ID is valid.
	 *
	 * @since 4.7.2
	 *
	 * @param int $id Supplied ID.
	 *
	 * @return WP_User|WP_Error True if ID is valid, WP_Error otherwise.
	 */
	protected function get_user($id)
	{
		$error = new WP_Error('rest_user_invalid_id', __('Invalid user ID.'), array('status' => 404));
		if ((int) $id <= 0) {
			return $error;
		}

		$user = get_userdata((int) $id);
		if (empty($user) || !$user->exists()) {
			return $error;
		}

		if (is_multisite() && !is_user_member_of_blog($user->ID)) {
			return $error;
		}

		return $user;
	}

	/**
	 * Checks if a given request has access to read a user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
	 */
	public function get_item_permissions_check($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		$types = get_post_types(array('show_in_rest' => true), 'names');

		if (get_current_user_id() === $user->ID) {
			return true;
		}

		// does user have edit permissions and is looking for edit context?
		if ('edit' === $request['context'] && !current_user_can('list_users')) {
			return new WP_Error('rest_user_cannot_view', __('Sorry, you are not allowed to list users.'), array('status' => rest_authorization_required_code()));
		}
		// TODO: maybe only show if user is member of project
		return true;
	}

	/**
	 * Retrieves a single user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function get_item($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		$user     = $this->prepare_item_for_response($user, $request);
		$response = rest_ensure_response($user);

		return $response;
	}

	/**
	 * Retrieves the current user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function get_current_item($request)
	{
		$current_user_id = get_current_user_id();

		if (empty($current_user_id)) {
			return new WP_Error('rest_not_logged_in', __('You are not currently logged in.'), array('status' => 401));
		}

		$user     = wp_get_current_user();
		$response = $this->prepare_item_for_response($user, $request);
		$response = rest_ensure_response($response);

		return $response;
	}

	/**
	 * Checks if a given request has access create users.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
	 */
	public function create_item_permissions_check($request)
	{
		// need a valid project_id
		if (!$project = get_post($request['project_id'])) {
			return new WP_Error('rest_invalid_project_id', __('You must provide a valid project id to create the user for that project.', 'project-huddle'), array('status' => $this->authorization_status_code()));
		}

		// if no access token is sent via api, check the user's session
		if (!$access_token = $request['access_token']) {
			$access       = PH()->session->get('project_access');
			$access_token = isset($access[$project->ID]) ? $access[$project->ID] : false;
		}

		// newup validatation controller
		$validator = new PH_Permissions_Controller($project->ID, $request['_signature'], $access_token, $request['email']);
		// check if visitor can access
		$has_access = $validator->visitor_can_access();

		// return permissions or WP_Error
		if (!is_wp_error($has_access) && $has_access) {
			return true;
		} else {
			return $has_access;
		}
	}

	/**
	 * Creates a single user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function create_item($request)
	{
		if (!empty($request['id'])) {
			return new WP_Error('rest_user_exists', __('Cannot create existing user.'), array('status' => 400));
		}

		$prepared_user = $this->prepare_item_for_database($request);

		// while username exists, append an additional number to make sure username is unique
		$count = 0;
		do {
			$unique_username = sanitize_user($prepared_user->user_login);
			if ($count) {
				$unique_username = $prepared_user->user_login . '-' . $count;
				$unique_username = sanitize_user($unique_username);
			}
			$count++;
		} while (username_exists($unique_username));

		// create user if it doesn't exist
		if (!email_exists(sanitize_email($request['email']))) {
			if (isset($request['password']) && $request['password']) {
				$password = $request['password'];
			} else {
				$password = wp_generate_password();
			}
			$user_id = wp_create_user($unique_username, $password, sanitize_email($request['email']));
			$created = false;
		} else {
			// force error
			$user_id = new WP_Error('existing_user_email', __('Sorry, that email address is already used!'));
		}

		// check for errors
		if (is_wp_error($user_id)) {
			$can_login = false;

			// bail for everything except if the user email already exists
			if ('existing_user_email' !== $user_id->get_error_code()) {
				return $user_id;
			}

			// get project id from request
			$project = get_post($request['project_id']);

			// unset access token
			unset($request['access_token']);

			// does project allow users to login without password?
			$can_login = ph_project_allow_login_without_password($project->ID, $request['_signature'], $request['email'], true);

			// get user by email
			$user = get_user_by('email', sanitize_email($request['email']));

			// make sure it's a valid user
			if (!is_a($user, 'WP_User')) {
				return $user_id; // return original email already exists if user doesn't have access
			}

			// bail for potentially dangerous operations, just in case
			if (user_can($user, 'manage_options') || user_can($user, 'promote_users') || user_can($user, 'edit_posts')) {
				return $user_id;
			}

			// user must be able to login using the access token permission
			if (!user_can($user, 'login_with_access_token')) {
				return $user_id; // return original email already exists if user doesn't have access
			}
		} else {
			$can_login = true;
			// force display name and role
			wp_update_user(
				array(
					'ID'           => $user_id,
					'display_name' => esc_sql($prepared_user->user_login),
					'role'         => 'project_client', // force project client
				)
			);

			// get user by id
			$user    = get_user_by('id', $user_id);
			$created = true;
		}

		if (!$can_login) {
			return $user_id;
		}

		// signon user
		wp_signon(
			array(
				'user_login'    => $user->user_login,
				'user_password' => $password,
				'remember'      => true,
			)
		);

		/**
		 * Fires immediately after a user is created or updated via the REST API.
		 *
		 * @since 4.7.0
		 *
		 * @param WP_User         $user     Inserted or updated user object.
		 * @param WP_REST_Request $request  Request object.
		 * @param bool            $creating True when creating a user, false when updating.
		 */
		if ($created) {
			do_action('ph_rest_insert_user', $user, $request, true);
		}

		// Log user in.
		wp_set_current_user($user->ID);

		// Set cookies.
		PH()->auth->setAuthCookie($user->ID);

		// add member to project
		ph_add_member_to_project(array(
			'user_id' => $user->ID,
			'project_id' =>  $request['project_id']
		));

		// get the user after login
		$request['id']      = $user->ID;
		$request['context'] = 'edit'; // use edit context to return permissions
		$response           = $this->get_item($request);

		// Send a refreshed nonce in header.
		$response->header('X-WP-Nonce', wp_create_nonce('wp_rest'));

		// return token data
		return $response;
	}

	/**
	 * Checks if a given request has access to update a user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
	 */
	public function update_item_permissions_check($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		if (!current_user_can('edit_user', $user->ID)) {
			return new WP_Error('rest_cannot_edit', __('Sorry, you are not allowed to edit this user.'), array('status' => rest_authorization_required_code()));
		}

		if (!empty($request['roles']) && !current_user_can('edit_users')) {
			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()));
		}

		return true;
	}

	/**
	 * Updates a single user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function update_item($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		$id = $user->ID;

		if (!$user) {
			return new WP_Error('rest_user_invalid_id', __('Invalid user ID.'), array('status' => 404));
		}

		if (email_exists($request['email']) && $request['email'] !== $user->user_email) {
			return new WP_Error('rest_user_invalid_email', __('Invalid email address.'), array('status' => 400));
		}

		if (!empty($request['username']) && $request['username'] !== $user->user_login) {
			return new WP_Error('rest_user_invalid_argument', __("Username isn't editable."), array('status' => 400));
		}

		if (!empty($request['slug']) && $request['slug'] !== $user->user_nicename && get_user_by('slug', $request['slug'])) {
			return new WP_Error('rest_user_invalid_slug', __('Invalid slug.'), array('status' => 400));
		}

		if (!empty($request['roles'])) {
			$check_permission = $this->check_role_update($id, $request['roles']);

			if (is_wp_error($check_permission)) {
				return $check_permission;
			}
		}

		$user = $this->prepare_item_for_database($request);

		// Ensure we're operating on the same user we already checked.
		$user->ID = $id;

		$user_id = wp_update_user(wp_slash((array) $user));

		if (is_wp_error($user_id)) {
			return $user_id;
		}

		$user = get_user_by('id', $user_id);

		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
		do_action('rest_insert_user', $user, $request, false);

		if (!empty($request['roles'])) {
			array_map(array($user, 'add_role'), $request['roles']);
		}

		$schema = $this->get_item_schema();

		if (!empty($schema['properties']['meta']) && isset($request['meta'])) {
			$meta_update = $this->meta->update_value($request['meta'], $id);

			if (is_wp_error($meta_update)) {
				return $meta_update;
			}
		}

		$user          = get_user_by('id', $user_id);
		$fields_update = $this->update_additional_fields_for_object($user, $request);

		if (is_wp_error($fields_update)) {
			return $fields_update;
		}

		$request->set_param('context', 'edit');

		$response = $this->prepare_item_for_response($user, $request);
		$response = rest_ensure_response($response);

		return $response;
	}

	/**
	 * Checks if a given request has access to update the current user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
	 */
	public function update_current_item_permissions_check($request)
	{
		$request['id'] = get_current_user_id();

		return $this->update_item_permissions_check($request);
	}

	/**
	 * Updates the current user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	function update_current_item($request)
	{
		$request['id'] = get_current_user_id();

		return $this->update_item($request);
	}

	/**
	 * Checks if a given request has access delete a user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
	 */
	public function delete_item_permissions_check($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		if (!current_user_can('delete_user', $user->ID)) {
			return new WP_Error('rest_user_cannot_delete', __('Sorry, you are not allowed to delete this user.'), array('status' => rest_authorization_required_code()));
		}

		return true;
	}

	/**
	 * Deletes a single user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function delete_item($request)
	{
		// We don't support delete requests in multisite.
		if (is_multisite()) {
			return new WP_Error('rest_cannot_delete', __('The user cannot be deleted.'), array('status' => 501));
		}
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		$id       = $user->ID;
		$reassign = false === $request['reassign'] ? null : absint($request['reassign']);
		$force    = isset($request['force']) ? (bool) $request['force'] : false;

		// We don't support trashing for users.
		if (!$force) {
			return new WP_Error('rest_trash_not_supported', __('Users do not support trashing. Set force=true to delete.'), array('status' => 501));
		}

		if (!empty($reassign)) {
			if ($reassign === $id || !get_userdata($reassign)) {
				return new WP_Error('rest_user_invalid_reassign', __('Invalid user ID for reassignment.'), array('status' => 400));
			}
		}

		$request->set_param('context', 'edit');

		$previous = $this->prepare_item_for_response($user, $request);

		/** Include admin user functions to get access to wp_delete_user() */
		require_once ABSPATH . 'wp-admin/includes/user.php';

		$result = wp_delete_user($id, $reassign);

		if (!$result) {
			return new WP_Error('rest_cannot_delete', __('The user cannot be deleted.'), array('status' => 500));
		}

		$response = new WP_REST_Response();
		$response->set_data(
			array(
				'deleted'  => true,
				'previous' => $previous->get_data(),
			)
		);

		/**
		 * Fires immediately after a user is deleted via the REST API.
		 *
		 * @since 4.7.0
		 *
		 * @param WP_User          $user     The user data.
		 * @param WP_REST_Response $response The response returned from the API.
		 * @param WP_REST_Request  $request  The request sent to the API.
		 */
		do_action('rest_delete_user', $user, $response, $request);

		return $response;
	}

	/**
	 * Checks if a given request has access to delete the current user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
	 */
	public function delete_current_item_permissions_check($request)
	{
		$request['id'] = get_current_user_id();

		return $this->delete_item_permissions_check($request);
	}

	/**
	 * Deletes the current user.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	function delete_current_item($request)
	{
		$request['id'] = get_current_user_id();

		return $this->delete_item($request);
	}

	public function logout_permissions_check()
	{
		if (!is_user_logged_in()) {
			return new WP_Error('not_logged_in', 'You are not logged in.');
		}
		return true;
	}

	public function logout()
	{
		wp_logout();
		return;
	}

	/**
	 * Anyone can sign in
	 *
	 * @param $request
	 *
	 * @return bool|WP_Error
	 */
	function login_permissions_check($request)
	{
		return true; // let anyone sign in
	}

	/**
	 * Signin user using username and password
	 *
	 * @param $request
	 *
	 * @return WP_User|WP_Error User or error loging in
	 */
	function login($request)
	{
		$creds = array(
			'user_login'    => $request['username'],
			'user_password' => $request['password'],
			'remember'      => true,
		);

		$user = wp_signon($creds, false);

		if (is_wp_error($user)) {
			return $user;
		}

		// Log user in.
		wp_set_current_user($user->ID);
		$_set_cookies = true; // for the closures.

		// Set the (secure) auth cookie immediately. We need only the first and last
		// arguments; hence I renamed the other three, namely `$a`, `$b`, and `$c`.
		add_action(
			'set_auth_cookie',
			function ($auth_cookie, $a, $b, $c, $scheme) use ($_set_cookies) {
				if ($_set_cookies) {
					$_COOKIE['secure_auth' === $scheme ? SECURE_AUTH_COOKIE : AUTH_COOKIE] = $auth_cookie;
				}
			},
			10,
			5
		);

		// Set the logged-in cookie immediately. `wp_create_nonce()` relies upon this
		// cookie; hence, we must also set it.
		add_action(
			'set_logged_in_cookie',
			function ($logged_in_cookie) use ($_set_cookies) {
				if ($_set_cookies) {
					$_COOKIE[LOGGED_IN_COOKIE] = $logged_in_cookie;
				}
			}
		);

		// Set cookies.
		wp_set_auth_cookie($user->ID);
		$_set_cookies = false;

		// get the user after login.
		$request['id']      = $user->ID;
		$request['context'] = 'edit'; // use edit context to return permissions.
		$response           = $this->get_item($request);

		if (is_wp_error($response)) {
			return $response;
		}

		// Send a refreshed nonce in header.
		$response->header('X-WP-Nonce', wp_create_nonce('wp_rest'));

		// send response.
		return $response;
	}

	/**
	 * Anyone can sign in
	 *
	 * @param $request
	 *
	 * @return bool|WP_Error
	 */
	function reset_permissions_check($request)
	{
		return true; // let anyone sign in
	}

	/**
	 * Signin user using username and password
	 *
	 * @param $request
	 *
	 * @return boolean
	 */
	function reset($request)
	{
		$creds['user_login'] = $request['username'];
		do_action('ph_sent_user_password_reset', $creds['user_login'], $request);

		return true;
	}

	function suppression_permissions_check($request)
	{
		$user = $this->get_user($request['id']);
		if (is_wp_error($user)) {
			return $user;
		}

		if ($user->ID !== get_current_user_id()) {
			return new WP_Error('rest_user_invalid', __('Sorry, you are not allowed add or remove others from suppression lists.'), array('status' => rest_authorization_required_code()));
		}

		return true;
	}

	/**
	 * Prepares a single user output for response.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param WP_User         $user    User object.
	 * @param WP_REST_Request $request Request object.
	 *
	 * @return WP_REST_Response Response object.
	 */
	public function prepare_item_for_response($user, $request)
	{

		$data   = array();
		$schema = $this->get_item_schema();

		if (!empty($schema['properties']['id'])) {
			$data['id'] = $user->ID;
		}

		if (!empty($schema['properties']['username'])) {
			$data['username'] = $user->user_login;
		}

		if (!empty($schema['properties']['name'])) {
			$data['name'] = $user->display_name;
		}

		if (!empty($schema['properties']['first_name'])) {
			$data['first_name'] = $user->first_name;
		}

		if (!empty($schema['properties']['last_name'])) {
			$data['last_name'] = $user->last_name;
		}

		if (!empty($schema['properties']['email'])) {
			$data['email'] = $user->user_email;
		}

		if (!empty($schema['properties']['url'])) {
			$data['url'] = $user->user_url;
		}

		if (!empty($schema['properties']['description'])) {
			$data['description'] = $user->description;
		}

		if (!empty($schema['properties']['link'])) {
			$data['link'] = get_author_posts_url($user->ID, $user->user_nicename);
		}

		if (!empty($schema['properties']['locale'])) {
			$data['locale'] = get_user_locale($user);
		}

		if (!empty($schema['properties']['nickname'])) {
			$data['nickname'] = $user->nickname;
		}

		if (!empty($schema['properties']['slug'])) {
			$data['slug'] = $user->user_nicename;
		}

		if (!empty($schema['properties']['roles'])) {
			// Defensively call array_values() to ensure an array is returned.
			$data['roles'] = array_values($user->roles);
		}

		if (!empty($schema['properties']['registered_date'])) {
			$data['registered_date'] = date('c', strtotime($user->user_registered));
		}

		if (!empty($schema['properties']['capabilities'])) {
			$data['capabilities'] = (object) $user->allcaps;
		}

		if (!empty($schema['properties']['extra_capabilities'])) {
			$data['extra_capabilities'] = (object) $user->caps;
		}

		if (!empty($schema['properties']['avatar_urls'])) {
			$data['avatar_urls'] = rest_get_avatar_urls($user->user_email);
		}

		if (!empty($schema['properties']['meta'])) {
			$data['meta'] = $this->meta->get_value($user->ID, $request);
		}

		$context = !empty($request['context']) ? $request['context'] : 'embed';

		$data = $this->add_additional_fields_to_object($data, $request);
		$data = $this->filter_response_by_context($data, $context);

		// Wrap the data in a response object.
		$response = rest_ensure_response($data);

		$response->add_links($this->prepare_links($user));

		/**
		 * Filters user data returned from the REST API.
		 *
		 * @since 4.7.0
		 *
		 * @param WP_REST_Response $response The response object.
		 * @param object           $user     User object used to create response.
		 * @param WP_REST_Request  $request  Request object.
		 */
		return apply_filters('rest_prepare_user', $response, $user, $request);
	}

	/**
	 * Prepares links for the user request.
	 *
	 * @since  4.7.0
	 * @access protected
	 *
	 * @param WP_Post $user User object.
	 *
	 * @return array Links for the given user.
	 */
	protected function prepare_links($user)
	{
		$links = array(
			'self'       => array(
				'href' => rest_url(sprintf('%s/%s/%d', $this->namespace, $this->rest_base, $user->ID)),
			),
			'collection' => array(
				'href' => rest_url(sprintf('%s/%s', $this->namespace, $this->rest_base)),
			),
		);

		return $links;
	}

	/**
	 * Prepares a single user for creation or update.
	 *
	 * @since  4.7.0
	 * @access protected
	 *
	 * @param WP_REST_Request $request Request object.
	 *
	 * @return object $prepared_user User object.
	 */
	protected function prepare_item_for_database($request)
	{
		$prepared_user = new stdClass();

		$schema = $this->get_item_schema();

		// required arguments.
		if (isset($request['email']) && !empty($schema['properties']['email'])) {
			$prepared_user->user_email = $request['email'];
		}

		if (isset($request['username']) && !empty($schema['properties']['username'])) {
			$prepared_user->user_login = rtrim($request['username']);
		}

		if (isset($request['password']) && !empty($schema['properties']['password'])) {
			$prepared_user->user_pass = $request['password'];
		}

		// optional arguments.
		if (isset($request['id'])) {
			$prepared_user->ID = absint($request['id']);
		}

		if (isset($request['name']) && !empty($schema['properties']['name'])) {
			$prepared_user->display_name = $request['name'];
		}

		if (isset($request['first_name']) && !empty($schema['properties']['first_name'])) {
			$prepared_user->first_name = $request['first_name'];
		}

		if (isset($request['last_name']) && !empty($schema['properties']['last_name'])) {
			$prepared_user->last_name = $request['last_name'];
		}

		if (isset($request['nickname']) && !empty($schema['properties']['nickname'])) {
			$prepared_user->nickname = $request['nickname'];
		}

		if (isset($request['slug']) && !empty($schema['properties']['slug'])) {
			$prepared_user->user_nicename = $request['slug'];
		}

		if (isset($request['description']) && !empty($schema['properties']['description'])) {
			$prepared_user->description = $request['description'];
		}

		if (isset($request['url']) && !empty($schema['properties']['url'])) {
			$prepared_user->user_url = $request['url'];
		}

		if (isset($request['locale']) && !empty($schema['properties']['locale'])) {
			$prepared_user->locale = $request['locale'];
		}

		// setting roles will be handled outside of this function.
		if (isset($request['roles'])) {
			$prepared_user->role = false;
		}

		/**
		 * Filters user data before insertion via the REST API.
		 *
		 * @since 4.7.0
		 *
		 * @param object          $prepared_user User object.
		 * @param WP_REST_Request $request       Request object.
		 */
		return apply_filters('rest_pre_insert_user', $prepared_user, $request);
	}

	/**
	 * Determines if the current user is allowed to make the desired roles change.
	 *
	 * @since  4.7.0
	 * @access protected
	 *
	 * @param integer $user_id User ID.
	 * @param array   $roles   New user roles.
	 *
	 * @return true|WP_Error True if the current user is allowed to make the role change,
	 *                       otherwise a WP_Error object.
	 */
	protected function check_role_update($user_id, $roles)
	{
		global $wp_roles;

		foreach ($roles as $role) {

			if (!isset($wp_roles->role_objects[$role])) {
				/* translators: %s: role key */
				return new WP_Error('rest_user_invalid_role', sprintf(__('The role %s does not exist.'), $role), array('status' => 400));
			}

			$potential_role = $wp_roles->role_objects[$role];

			/*
			 * Don't let anyone with 'edit_users' (admins) edit their own role to something without it.
			 * Multisite super admins can freely edit their blog roles -- they possess all caps.
			 */
			if (
				!(is_multisite()
					&& current_user_can('manage_sites'))
				&& get_current_user_id() === $user_id
				&& !$potential_role->has_cap('edit_users')
			) {
				return new WP_Error('rest_user_invalid_role', __('Sorry, you are not allowed to give users that role.'), array('status' => rest_authorization_required_code()));
			}

			/** Include admin functions to get access to get_editable_roles() */
			require_once ABSPATH . 'wp-admin/includes/admin.php';

			// The new role must be editable by the logged-in user.
			$editable_roles = get_editable_roles();

			if (empty($editable_roles[$role])) {
				return new WP_Error('rest_user_invalid_role', __('Sorry, you are not allowed to give users that role.'), array('status' => 403));
			}
		}

		return true;
	}

	/**
	 * Check a username for the REST API.
	 *
	 * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param  mixed           $value   The username submitted in the request.
	 * @param  WP_REST_Request $request Full details about the request.
	 * @param  string          $param   The parameter name.
	 *
	 * @return WP_Error|string The sanitized username, if valid, otherwise an error.
	 */
	public function check_username($value, $request, $param)
	{
		$username = (string) $value;

		if (!$this->validate_username($username)) {
			return new WP_Error('rest_user_invalid_username', __('Username contains invalid characters.'), array('status' => 400));
		}

		/** This filter is documented in wp-includes/user.php */
		$illegal_logins = (array) apply_filters('illegal_user_logins', array());

		if (in_array(strtolower($username), array_map('strtolower', $illegal_logins))) {
			return new WP_Error('rest_user_invalid_username', __('Sorry, that username is not allowed.'), array('status' => 400));
		}

		return $username;
	}

	/**
	 * Roll our on validate username function to prevent themes and other plugins from filtering
	 *
	 * @param $username
	 *
	 * @return boolean
	 */
	protected function validate_username($username)
	{
		$sanitized = $this->sanitize_user($username, true);
		$valid     = ($sanitized == $username && !empty($sanitized));

		/**
		 * Filters whether the provided username is valid or not.
		 *
		 * @since 2.0.1
		 *
		 * @param bool   $valid    Whether given username is valid.
		 * @param string $username Username to check.
		 */
		return apply_filters('ph_validate_username', $valid, $username);
	}

	/**
	 * Roll our on validate username function to prevent themes and other plugins from filtering
	 *
	 * @param      $username
	 * @param bool     $strict
	 *
	 * @return string
	 */
	protected function sanitize_user($username, $strict = false)
	{
		$raw_username = $username;
		$username     = wp_strip_all_tags($username);
		$username     = remove_accents($username);
		// Kill octets
		$username = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $username);
		$username = preg_replace('/&.+?;/', '', $username); // Kill entities

		// If strict, reduce to ASCII for max portability.
		if ($strict) {
			$username = preg_replace('|[^a-z0-9 _.\-@]|i', '', $username);
		}

		$username = trim($username);
		// Consolidate contiguous whitespace
		$username = preg_replace('|\s+|', ' ', $username);

		/**
		 * Filters a sanitized username string.
		 *
		 * @since 2.0.1
		 *
		 * @param string $username     Sanitized username.
		 * @param string $raw_username The username prior to sanitization.
		 * @param bool   $strict       Whether to limit the sanitization to specific characters. Default false.
		 */
		return apply_filters('ph_sanitize_user', $username, $raw_username, $strict);
	}

	/**
	 * Check a user password for the REST API.
	 *
	 * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @param  mixed           $value   The password submitted in the request.
	 * @param  WP_REST_Request $request Full details about the request.
	 * @param  string          $param   The parameter name.
	 *
	 * @return WP_Error|string The sanitized password, if valid, otherwise an error.
	 */
	public function check_user_password($value, $request, $param)
	{
		$password = (string) $value;

		if (empty($password)) {
			return new WP_Error('rest_user_invalid_password', __('Passwords cannot be empty.'), array('status' => 400));
		}

		if (false !== strpos($password, '\\')) {
			return new WP_Error('rest_user_invalid_password', __('Passwords cannot contain the "\\" character.'), array('status' => 400));
		}

		return $password;
	}

	/**
	 * Retrieves the user's schema, conforming to JSON Schema.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @return array Item schema data.
	 */
	public function get_item_schema()
	{
		$schema = array(
			'$schema'    => 'http://json-schema.org/schema#',
			'title'      => 'user',
			'type'       => 'object',
			'properties' => array(
				'id'                 => array(
					'description' => __('Unique identifier for the user.'),
					'type'        => 'integer',
					'context'     => array('embed', 'view', 'edit'),
					'readonly'    => true,
				),
				'username'           => array(
					'description' => __('Login name for the user.'),
					'type'        => 'string',
					'context'     => array('edit'),
					'required'    => false,
				),
				'name'               => array(
					'description' => __('Display name for the user.'),
					'type'        => 'string',
					'context'     => array('embed', 'view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'first_name'         => array(
					'description' => __('First name for the user.'),
					'type'        => 'string',
					'context'     => array('edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'last_name'          => array(
					'description' => __('Last name for the user.'),
					'type'        => 'string',
					'context'     => array('edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'email'              => array(
					'description' => __('The email address for the user.'),
					'type'        => 'string',
					'format'      => 'email',
					'context'     => array('edit'),
					'required'    => true,
				),
				'url'                => array(
					'description' => __('URL of the user.'),
					'type'        => 'string',
					'format'      => 'uri',
					'context'     => array('embed', 'view', 'edit'),
				),
				'description'        => array(
					'description' => __('Description of the user.'),
					'type'        => 'string',
					'context'     => array('embed', 'view', 'edit'),
				),
				'link'               => array(
					'description' => __('Author URL of the user.'),
					'type'        => 'string',
					'format'      => 'uri',
					'context'     => array('embed', 'view', 'edit'),
					'readonly'    => true,
				),
				'locale'             => array(
					'description' => __('Locale for the user.'),
					'type'        => 'string',
					'enum'        => array_merge(array('', 'en_US'), get_available_languages()),
					'context'     => array('edit'),
				),
				'nickname'           => array(
					'description' => __('The nickname for the user.'),
					'type'        => 'string',
					'context'     => array('edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'slug'               => array(
					'description' => __('An alphanumeric identifier for the user.'),
					'type'        => 'string',
					'context'     => array('embed', 'view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => array($this, 'sanitize_slug'),
					),
				),
				'registered_date'    => array(
					'description' => __('Registration date for the user.'),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array('edit'),
					'readonly'    => true,
				),
				'roles'              => array(
					'description' => __('Roles assigned to the user.'),
					'type'        => 'array',
					'items'       => array(
						'type' => 'string',
					),
					'context'     => array('edit'),
				),
				'password'           => array(
					'description' => __('Password for the user (never included).'),
					'type'        => 'string',
					'context'     => array(), // Password is never displayed.
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => array($this, 'check_user_password'),
					),
				),
				'capabilities'       => array(
					'description' => __('All capabilities assigned to the user.'),
					'type'        => 'object',
					'context'     => array('edit'),
					'readonly'    => true,
				),
				'extra_capabilities' => array(
					'description' => __('Any extra capabilities assigned to the user.'),
					'type'        => 'object',
					'context'     => array('edit'),
					'readonly'    => true,
				),
				// 'access_token' => array(
				// 	'description' => __( 'Pass an access token with a specific user' ),
				// 	'type'        => 'string',
				// 	'context'     => array(),
				// ),
				// 'project_id' => array(
				// 	'description' => __( 'Pass a project id for access requests.' ),
				// 	'type'        => 'integer',
				// 	'context'     => array( 'embed', 'view', 'edit' ),
				// 	'default' => 0
				// ),
			),
		);

		$avatar_properties = array();
		$avatar_sizes      = rest_get_avatar_sizes();

		foreach ($avatar_sizes as $size) {
			$avatar_properties[$size] = array(
				/* translators: %d: avatar image size in pixels */
				'description' => sprintf(__('Avatar URL with image size of %d pixels.'), $size),
				'type'        => 'string',
				'format'      => 'uri',
				'context'     => array('embed', 'view', 'edit'),
			);
		}

		$schema['properties']['avatar_urls'] = array(
			'description' => __('Avatar URLs for the user.'),
			'type'        => 'object',
			'context'     => array('embed', 'view', 'edit'),
			'readonly'    => true,
			'properties'  => $avatar_properties,
		);

		$schema['properties']['meta'] = $this->meta->get_field_schema();

		return $this->add_additional_fields_schema($schema);
	}

	/**
	 * Retrieves the user's schema, conforming to JSON Schema.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @return array Item schema data.
	 */
	public function get_login_schema()
	{
		$schema = array(
			'$schema'    => 'http://json-schema.org/schema#',
			'title'      => 'user',
			'type'       => 'object',
			'properties' => array(
				'username' => array(
					'description' => __('Login name for the user.'),
					'type'        => 'string',
					'context'     => array('edit'),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => array($this, 'check_username'),
					),
				),
				'password' => array(
					'description' => __('Password for the user (never included).'),
					'type'        => 'string',
					'context'     => array(), // Password is never displayed.
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => array($this, 'check_user_password'),
					),
				),
			),
		);

		$avatar_properties = array();

		$avatar_sizes = rest_get_avatar_sizes();

		foreach ($avatar_sizes as $size) {
			$avatar_properties[$size] = array(
				/* translators: %d: avatar image size in pixels */
				'description' => sprintf(__('Avatar URL with image size of %d pixels.'), $size),
				'type'        => 'string',
				'format'      => 'uri',
				'context'     => array('embed', 'view', 'edit'),
			);
		}

		$schema['properties']['avatar_urls'] = array(
			'description' => __('Avatar URLs for the user.'),
			'type'        => 'object',
			'context'     => array('embed', 'view', 'edit'),
			'readonly'    => true,
			'properties'  => $avatar_properties,
		);

		$schema['properties']['meta'] = $this->meta->get_field_schema();

		return $this->add_additional_fields_schema($schema);
	}

	/**
	 * Retrieves the query params for collections.
	 *
	 * @since  4.7.0
	 * @access public
	 *
	 * @return array Collection parameters.
	 */
	public function get_collection_params()
	{
		$query_params = parent::get_collection_params();

		$query_params['context']['default'] = 'view';

		$query_params['exclude'] = array(
			'description' => __('Ensure result set excludes specific IDs.'),
			'type'        => 'array',
			'items'       => array(
				'type' => 'integer',
			),
			'default'     => array(),
		);

		$query_params['include'] = array(
			'description' => __('Limit result set to specific IDs.'),
			'type'        => 'array',
			'items'       => array(
				'type' => 'integer',
			),
			'default'     => array(),
		);

		$query_params['offset'] = array(
			'description' => __('Offset the result set by a specific number of items.'),
			'type'        => 'integer',
		);

		$query_params['order'] = array(
			'default'     => 'asc',
			'description' => __('Order sort attribute ascending or descending.'),
			'enum'        => array('asc', 'desc'),
			'type'        => 'string',
		);

		$query_params['orderby'] = array(
			'default'     => 'name',
			'description' => __('Sort collection by object attribute.'),
			'enum'        => array(
				'id',
				'include',
				'name',
				'registered_date',
				'slug',
				'email',
				'url',
			),
			'type'        => 'string',
		);

		$query_params['slug'] = array(
			'description' => __('Limit result set to users with one or more specific slugs.'),
			'type'        => 'array',
			'items'       => array(
				'type' => 'string',
			),
		);

		$query_params['roles'] = array(
			'description' => __('Limit result set to users matching at least one specific role provided. Accepts csv list or single role.'),
			'type'        => 'array',
			'items'       => array(
				'type' => 'string',
			),
		);

		$query_params['project'] = array(
			'description' => __('Limit result set to users who are members of a project.'),
			'type'        => 'integer',
		);

		$query_params['show_name'] = array(
			'default'     => false,
			'description' => __('Show the persons name even if they are logged in as themselves.'),
			'type'        => 'boolean',
		);

		/**
		 * Filter collection parameters for the users controller.
		 *
		 * This filter registers the collection parameter, but does not map the
		 * collection parameter to an internal WP_User_Query parameter.  Use the
		 * `rest_user_query` filter to set WP_User_Query arguments.
		 *
		 * @since 4.7.0
		 *
		 * @param array $query_params JSON Schema-formatted collection parameters.
		 */
		return apply_filters('rest_user_collection_params', $query_params);
	}

	// Sets up the proper HTTP status code for authorization.
	public function authorization_status_code()
	{

		$status = 401;

		if (is_user_logged_in()) {
			$status = 403;
		}

		return $status;
	}
}

// Function to register our new routes from the controller.
function ph_register_user_routes()
{
	$controller = new PH_REST_Users_Controller();
	$controller->register_routes();
}

add_action('rest_api_init', 'ph_register_user_routes');
