BP_REST_Members_Endpoint

BuddyPress Members endpoints.


Description Description


Source Source

File: bp-members/classes/class-bp-rest-members-endpoint.php

class BP_REST_Members_Endpoint extends WP_REST_Users_Controller {

	/**
	 * Constructor.
	 *
	 * @since 5.0.0
	 */
	public function __construct() {
		$this->namespace = bp_rest_namespace() . '/' . bp_rest_version();
		$this->rest_base = 'members';
	}

	/**
	 * Retrieve users.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response
	 */
	public function get_items( $request ) {
		$args = array(
			'type'           => $request['type'],
			'user_id'        => $request['user_id'],
			'user_ids'       => $request['user_ids'],
			'xprofile_query' => $request['xprofile'],
			'include'        => $request['include'],
			'exclude'        => $request['exclude'],
			'member_type'    => $request['member_type'],
			'search_terms'   => $request['search'],
			'per_page'       => $request['per_page'],
			'page'           => $request['page'],
		);

		if ( empty( $request['user_ids'] ) ) {
			$args['user_ids'] = false;
		}

		if ( empty( $request['exclude'] ) ) {
			$args['exclude'] = false;
		}

		if ( empty( $request['include'] ) ) {
			$args['include'] = false;
		}

		if ( empty( $request['xprofile'] ) ) {
			$args['xprofile_query'] = false;
		}

		if ( empty( $request['member_type'] ) ) {
			$args['member_type'] = '';
		}

		/**
		 * Filter the query arguments for the request.
		 *
		 * @since 5.0.0
		 *
		 * @param array           $args    Key value array of query var to query value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		$args = apply_filters( 'bp_rest_members_get_items_query_args', $args, $request );

		// Actually, query it.
		$member_query = new BP_User_Query( $args );
		$members      = array_values( $member_query->results );

		$retval = array();
		foreach ( $members as $member ) {
			$retval[] = $this->prepare_response_for_collection(
				$this->prepare_item_for_response( $member, $request )
			);
		}

		$response = rest_ensure_response( $retval );
		$response = bp_rest_response_add_total_headers( $response, $member_query->total_users, $args['per_page'] );

		/**
		 * Fires after a list of members is fetched via the REST API.
		 *
		 * @since 5.0.0
		 *
		 * @param array            $members  Fetched members.
		 * @param WP_REST_Response $response The response data.
		 * @param WP_REST_Request  $request  The request sent to the API.
		 */
		do_action( 'bp_rest_members_get_items', $members, $response, $request );

		return $response;
	}

	/**
	 * Checks if a given request has access to get all users.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return bool
	 */
	public function get_items_permissions_check( $request ) {

		/**
		 * Filter the members `get_items` permissions check.
		 *
		 * @since 5.0.0
		 *
		 * @param bool            $retval  Returned value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		return apply_filters( 'bp_rest_members_get_items_permissions_check', true, $request );
	}

	/**
	 * Checks if a given request has access to read a user.
	 *
	 * @since 5.0.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error
	 */
	public function get_item_permissions_check( $request ) {
		$retval = true;

		if ( ! is_user_logged_in() ) {
			$retval = new WP_Error(
				'bp_rest_authorization_required',
				__( 'Sorry, you are not allowed to view members.', 'buddypress' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		$user = bp_rest_get_user( $request['id'] );

		if ( true === $retval && ! $user instanceof WP_User ) {
			$retval = new WP_Error(
				'bp_rest_member_invalid_id',
				__( 'Invalid member ID.', 'buddypress' ),
				array(
					'status' => 404,
				)
			);
		}

		if ( true === $retval && get_current_user_id() === $user->ID ) {
			$retval = true;
		} elseif ( true === $retval && 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
			$retval = new WP_Error(
				'bp_rest_authorization_required',
				__( 'Sorry, you are not allowed to view members.', 'buddypress' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		/**
		 * Filter the members `get_item` permissions check.
		 *
		 * @since 5.0.0
		 *
		 * @param bool|WP_Error   $retval  Returned value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		return apply_filters( 'bp_rest_members_get_item_permissions_check', $retval, $request );
	}

	/**
	 * Checks if a given request has access create members.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error
	 */
	public function create_item_permissions_check( $request ) {
		$retval = true;

		if ( ! ( is_user_logged_in() && current_user_can( 'bp_moderate' ) ) ) {
			$retval = new WP_Error(
				'bp_rest_authorization_required',
				__( 'Sorry, you are not allowed to view members.', 'buddypress' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		/**
		 * Filter or override the members `create_item` permissions check.
		 *
		 * @since 5.0.0
		 *
		 * @param bool|WP_Error   $retval  Returned value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		return apply_filters( 'bp_rest_members_create_item_permissions_check', $retval, $request );
	}

	/**
	 * Check if a given request has access to update a member.
	 *
	 * @since 5.0.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error
	 */
	public function update_item_permissions_check( $request ) {
		$retval = true;
		$user   = bp_rest_get_user( $request['id'] );

		if ( ! $user instanceof WP_User ) {
			$retval = new WP_Error(
				'bp_rest_member_invalid_id',
				__( 'Invalid member ID.', 'buddypress' ),
				array(
					'status' => 404,
				)
			);
		}

		$action = 'delete';
		if ( 'DELETE' !== $request->get_method() ) {
			$action = 'update';
		}

		if ( true === $retval && ! $this->can_manage_member( $user, $action ) ) {
			$retval = new WP_Error(
				'bp_rest_authorization_required',
				__( 'Sorry, you are not allowed to view members.', 'buddypress' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		/**
		 * Filter the members `update_item` permissions check.
		 *
		 * @since 5.0.0
		 *
		 * @param bool|WP_Error   $retval  Returned value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		return apply_filters( 'bp_rest_members_update_item_permissions_check', $retval, $request );
	}

	/**
	 * Check if a given request has access to delete a member.
	 *
	 * @since 5.0.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error
	 */
	public function delete_item_permissions_check( $request ) {
		$retval = $this->update_item_permissions_check( $request );

		/**
		 * Filter the members `delete_item` permissions check.
		 *
		 * @since 5.0.0
		 *
		 * @param bool|WP_Error   $retval  Returned value.
		 * @param WP_REST_Request $request The request sent to the API.
		 */
		return apply_filters( 'bp_rest_members_delete_item_permissions_check', $retval, $request );
	}

	/**
	 * Deleting the current user is not implemented into this endpoint.
	 *
	 * This action is specific to the User Settings endpoint.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_Error                WP_Error object to inform it's not implemented.
	 */
	public function delete_current_item_permissions_check( $request ) {
		return new WP_Error(
			'bp_rest_invalid_method',
			/* translators: %s: transport method name */
			sprintf( __( '\'%s\' Transport Method not implemented.', 'buddypress' ), $request->get_method() ),
			array(
				'status' => 405,
			)
		);
	}

	/**
	 * Deleting the current user is not implemented into this endpoint.
	 *
	 * This action is specific to the User Settings endpoint.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_Error                WP_Error to inform it's not implemented.
	 */
	public function delete_current_item( $request ) {
		return new WP_Error(
			'bp_rest_invalid_method',
			/* translators: %s: transport method name */
			sprintf( __( '\'%s\' Transport method not implemented.', 'buddypress' ), $request->get_method() ),
			array(
				'status' => 405,
			)
		);
	}

	/**
	 * Prepares a single user output for response.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_User         $user    User object.
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response
	 */
	public function prepare_item_for_response( $user, $request ) {
		$context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
		$data     = $this->user_data( $user, $context );
		$data     = $this->add_additional_fields_to_object( $data, $request );
		$data     = $this->filter_response_by_context( $data, $context );
		$response = rest_ensure_response( $data );

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

		/**
		 * Filters user data returned from the API.
		 *
		 * @since 5.0.0
		 *
		 * @param WP_REST_Response $response The response object.
		 * @param WP_REST_Request  $request  The request object.
		 * @param WP_User          $user     WP_User object.
		 */
		return apply_filters( 'bp_rest_members_prepare_value', $response, $request, $user );
	}

	/**
	 * Method to facilitate fetching of user data.
	 *
	 * This was abstracted to be used in other BuddyPress endpoints.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_User $user    User object.
	 * @param string  $context The context of the request. Defaults to 'view'.
	 * @return array
	 */
	public function user_data( $user, $context = 'view' ) {
		$data = array(
			'id'                 => $user->ID,
			'name'               => $user->display_name,
			'user_login'         => $user->user_login,
			'link'               => bp_core_get_user_domain( $user->ID, $user->user_nicename, $user->user_login ),
			'member_types'       => bp_get_member_type( $user->ID, false ),
			'roles'              => array(),
			'capabilities'       => array(),
			'extra_capabilities' => array(),
			'registered_date'    => '',
			'xprofile'           => $this->xprofile_data( $user->ID ),
		);

		if ( 'edit' === $context ) {
			$data['registered_date']    = bp_rest_prepare_date_response( $user->data->user_registered );
			$data['roles']              = (array) array_values( $user->roles );
			$data['capabilities']       = (array) array_keys( $user->allcaps );
			$data['extra_capabilities'] = (array) array_keys( $user->caps );
		}

		// The name used for that user in @-mentions.
		if ( bp_is_active( 'activity' ) ) {
			$data['mention_name'] = bp_activity_get_user_mentionname( $user->ID );
		}

		// Avatars.
		$data['avatar_urls'] = array(
			'full'  => bp_core_fetch_avatar(
				array(
					'item_id' => $user->ID,
					'html'    => false,
					'type'    => 'full',
				)
			),
			'thumb' => bp_core_fetch_avatar(
				array(
					'item_id' => $user->ID,
					'html'    => false,
				)
			),
		);

		// Fallback.
		if ( false === $data['member_types'] ) {
			$data['member_types'] = array();
		}

		return $data;
	}

	/**
	 * Prepares a single user for creation or update.
	 *
	 * @todo Improve sanitization and schema verification.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return stdClass
	 */
	protected function prepare_item_for_database( $request ) {
		$prepared_user = parent::prepare_item_for_database( $request );

		// The parent class uses username instead of user_login.
		if ( ! isset( $prepared_user->user_login ) && isset( $request['user_login'] ) ) {
			$prepared_user->user_login = $request['user_login'];
		}

		/**
		 * Filters an user object before it is inserted or updated via the REST API.
		 *
		 * @since 5.0.0
		 *
		 * @param stdClass        $prepared_user An object prepared for inserting or updating the database.
		 * @param WP_REST_Request $request       Request object.
		 */
		return apply_filters( 'bp_rest_members_pre_insert_value', $prepared_user, $request );
	}

	/**
	 * Get XProfile info from the user.
	 *
	 * @since 5.0.0
	 *
	 * @param  int $user_id User ID.
	 * @return array
	 */
	protected function xprofile_data( $user_id ) {
		$data = array();

		// Get XProfile groups, only if the component is active.
		if ( bp_is_active( 'xprofile' ) ) {
			$fields_endpoint = new BP_REST_XProfile_Fields_Endpoint();

			$groups = bp_xprofile_get_groups(
				array(
					'user_id'          => $user_id,
					'fetch_fields'     => true,
					'fetch_field_data' => true,
				)
			);

			foreach ( $groups as $group ) {
				$data['groups'][ $group->id ] = array(
					'name' => $group->name,
				);

				foreach ( $group->fields as $item ) {
					$data['groups'][ $group->id ]['fields'][ $item->id ] = array(
						'name'  => $item->name,
						'value' => array(
							'raw'          => $item->data->value,
							'unserialized' => $fields_endpoint->get_profile_field_unserialized_value( $item->data->value ),
							'rendered'     => $fields_endpoint->get_profile_field_rendered_value( $item->data->value, $item ),
						),
					);
				}
			}
		} else {
			$data = array( __( 'No extended profile data available as the component is inactive', 'buddypress' ) );
		}

		return $data;
	}

	/**
	 * Can user manage (delete/update) a member?
	 *
	 * @since 5.0.0
	 *
	 * @param  WP_User $user User object.
	 * @param  string  $action The action to perform (update or delete).
	 * @return bool
	 */
	protected function can_manage_member( $user, $action = 'delete' ) {
		$capability = 'delete_user';
		if ( 'update' === $action ) {
			$capability = 'edit_user';
		}

		return ( current_user_can( 'bp_moderate' ) || current_user_can( $capability, $user->ID ) );
	}

	/**
	 * Updates the values of additional fields added to a data object.
	 *
	 * This function makes sure updating the field value thanks to the `id` property of
	 * the created/updated object type is consistent accross BuddyPress components.
	 *
	 * @since 5.0.0
	 *
	 * @param WP_User         $object  The WordPress user object.
	 * @param WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error True on success, WP_Error object if a field cannot be updated.
	 */
	protected function update_additional_fields_for_object( $object, $request ) {
		if ( ! isset( $object->data ) ) {
			return new WP_Error( 'invalid_user', __( 'The data for the user was not found.', 'buddypress' ) );
		}

		$member     = $object->data;
		$member->id = $member->ID;

		return WP_REST_Controller::update_additional_fields_for_object( $member, $request );
	}

	/**
	 * Make sure to retrieve the needed arguments for the endpoint CREATABLE method.
	 *
	 * @since 5.0.0
	 *
	 * @param string $method Optional. HTTP method of the request.
	 * @return array Endpoint arguments.
	 */
	public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) {
		$args = WP_REST_Controller::get_endpoint_args_for_item_schema( $method );
		$key  = 'get_item';

		if ( WP_REST_Server::CREATABLE === $method ) {
			$key = 'create_item';

			// We don't need the mention name to create a user.
			unset( $args['mention_name'] );

			// But we absolutely need the email.
			$args['email'] = array(
				'description' => __( 'The email address for the member.', 'buddypress' ),
				'type'        => 'string',
				'format'      => 'email',
				'context'     => array( 'edit' ),
				'required'    => true,
			);
		} elseif ( WP_REST_Server::EDITABLE === $method ) {
			$key = 'update_item';

			/**
			 * 1. The mention name or user login are not updatable.
			 * 2. The password belongs to the Settings endpoint parameter.
			 */
			unset( $args['mention_name'], $args['user_login'], $args['password'] );
		} elseif ( WP_REST_Server::DELETABLE === $method ) {
			$key = 'delete_item';
		}

		/**
		 * Filters the method query arguments.
		 *
		 * @since 5.0.0
		 *
		 * @param array  $args   Query arguments.
		 * @param string $method HTTP method of the request.
		 */
		return apply_filters( "bp_rest_members_{$key}_query_arguments", $args, $method );
	}

	/**
	 * Get the members schema, conforming to JSON Schema.
	 *
	 * @since 5.0.0
	 *
	 * @return array
	 */
	public function get_item_schema() {
		$schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'bp_members',
			'type'       => 'object',
			'properties' => array(
				'id'                 => array(
					'description' => __( 'A unique numeric ID for the Member.', 'buddypress' ),
					'type'        => 'integer',
					'context'     => array( 'embed', 'view', 'edit' ),
					'readonly'    => true,
				),
				'name'               => array(
					'description' => __( 'Display name for the member.', 'buddypress' ),
					'type'        => 'string',
					'context'     => array( 'embed', 'view', 'edit' ),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'mention_name'       => array(
					'description' => __( 'The name used for that user in @-mentions.', 'buddypress' ),
					'type'        => 'string',
					'context'     => array( 'embed', 'view', 'edit' ),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'link'               => array(
					'description' => __( 'Profile URL of the member.', 'buddypress' ),
					'type'        => 'string',
					'format'      => 'uri',
					'context'     => array( 'embed', 'view', 'edit' ),
					'readonly'    => true,
				),
				'user_login'         => array(
					'description' => __( 'An alphanumeric identifier for the Member.', 'buddypress' ),
					'type'        => 'string',
					'context'     => array( 'embed', 'view', 'edit' ),
					'required'    => true,
					'arg_options' => array(
						'sanitize_callback' => array( $this, 'check_username' ),
					),
				),
				'member_types'       => array(
					'description' => __( 'Member types associated with the member.', 'buddypress' ),
					'type'        => 'object',
					'context'     => array( 'embed', 'view', 'edit' ),
					'readonly'    => true,
				),
				'registered_date'    => array(
					'description' => __( 'Registration date for the member.', 'buddypress' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'edit' ),
					'readonly'    => true,
				),
				'password'           => array(
					'description' => __( 'Password for the member (never included).', 'buddypress' ),
					'type'        => 'string',
					'context'     => array(), // Password is never displayed.
					'required'    => true,
					'arg_options' => array(
						'sanitize_callback' => array( $this, 'check_user_password' ),
					),
				),
				'roles'              => array(
					'description' => __( 'Roles assigned to the member.', 'buddypress' ),
					'type'        => 'array',
					'context'     => array( 'edit' ),
					'items'       => array(
						'type' => 'string',
					),
				),
				'capabilities'       => array(
					'description' => __( 'All capabilities assigned to the user.', 'buddypress' ),
					'type'        => 'object',
					'context'     => array( 'edit' ),
					'readonly'    => true,
				),
				'extra_capabilities' => array(
					'description' => __( 'Any extra capabilities assigned to the user.', 'buddypress' ),
					'type'        => 'object',
					'context'     => array( 'edit' ),
					'readonly'    => true,
				),
				'xprofile'           => array(
					'description' => __( 'Member XProfile groups and its fields.', 'buddypress' ),
					'type'        => 'array',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
			),
		);

		// Avatars.
		if ( true === buddypress()->avatar->show_avatars ) {
			$avatar_properties = array();

			$avatar_properties['full'] = array(
				/* translators: Full image size for the member Avatar */
				'description' => sprintf( __( 'Avatar URL with full image size (%1$d x %2$d pixels).', 'buddypress' ), number_format_i18n( bp_core_avatar_full_width() ), number_format_i18n( bp_core_avatar_full_height() ) ),
				'type'        => 'string',
				'format'      => 'uri',
				'context'     => array( 'embed', 'view', 'edit' ),
			);

			$avatar_properties['thumb'] = array(
				/* translators: Thumb imaze size for the member Avatar */
				'description' => sprintf( __( 'Avatar URL with thumb image size (%1$d x %2$d pixels).', 'buddypress' ), number_format_i18n( bp_core_avatar_thumb_width() ), number_format_i18n( bp_core_avatar_thumb_height() ) ),
				'type'        => 'string',
				'format'      => 'uri',
				'context'     => array( 'embed', 'view', 'edit' ),
			);

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

		/**
		 * Filters the members schema.
		 *
		 * @param array $schema The endpoint schema.
		 */
		return apply_filters( 'bp_rest_members_schema', $this->add_additional_fields_schema( $schema ) );
	}

	/**
	 * Get the query params for collections.
	 *
	 * @since 5.0.0
	 *
	 * @return array
	 */
	public function get_collection_params() {
		$params = array_intersect_key(
			parent::get_collection_params(),
			array(
				'context'  => true,
				'page'     => true,
				'per_page' => true,
				'search'   => true,
			)
		);

		$params['type'] = array(
			'description'       => __( 'Shorthand for certain orderby/order combinations.', 'buddypress' ),
			'default'           => 'newest',
			'type'              => 'string',
			'enum'              => array( 'active', 'newest', 'alphabetical', 'random', 'online', 'popular' ),
			'sanitize_callback' => 'sanitize_key',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['user_id'] = array(
			'description'       => __( 'Limit results to friends of a user.', 'buddypress' ),
			'default'           => 0,
			'type'              => 'integer',
			'sanitize_callback' => 'absint',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['user_ids'] = array(
			'description'       => __( 'Pass IDs of users to limit result set.', 'buddypress' ),
			'default'           => array(),
			'type'              => 'array',
			'items'             => array( 'type' => 'integer' ),
			'sanitize_callback' => 'wp_parse_id_list',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['include'] = array(
			'description'       => __( 'Ensure result set includes specific IDs.', 'buddypress' ),
			'default'           => array(),
			'type'              => 'array',
			'items'             => array( 'type' => 'integer' ),
			'sanitize_callback' => 'wp_parse_id_list',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['exclude'] = array(
			'description'       => __( 'Ensure result set excludes specific IDs.', 'buddypress' ),
			'default'           => array(),
			'type'              => 'array',
			'items'             => array( 'type' => 'integer' ),
			'sanitize_callback' => 'wp_parse_id_list',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['member_type'] = array(
			'description'       => __( 'Limit results set to certain type(s).', 'buddypress' ),
			'default'           => array(),
			'type'              => 'array',
			'items'             => array( 'type' => 'string' ),
			'sanitize_callback' => 'bp_rest_sanitize_string_list',
			'validate_callback' => 'rest_validate_request_arg',
		);

		$params['xprofile'] = array(
			'description'       => __( 'Limit results set to a certain XProfile field.', 'buddypress' ),
			'default'           => '',
			'type'              => 'string',
			'sanitize_callback' => 'sanitize_key',
			'validate_callback' => 'rest_validate_request_arg',
		);

		/**
		 * Filters the collection query params.
		 *
		 * @param array $params Query params.
		 */
		return apply_filters( 'bp_rest_members_collection_params', $params );
	}
}

Top ↑

Changelog Changelog

Changelog
Version Description
5.0.0 Introduced.

Top ↑

Methods Methods


Top ↑

User Contributed Notes User Contributed Notes

You must log in before being able to contribute a note or feedback.