Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/wp-includes/abilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

declare( strict_types = 1 );

require_once __DIR__ . '/abilities/class-wp-users-abilities.php';

/**
* Registers the core ability categories.
*
Expand Down Expand Up @@ -259,4 +261,6 @@ function wp_register_core_abilities(): void {
),
)
);

WP_Users_Abilities::register();
}
297 changes: 297 additions & 0 deletions src/wp-includes/abilities/class-wp-users-abilities.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
<?php
/**
* Registers core user abilities.
*
* This is a utility class to encapsulate the registration of user-related abilities.
* It is not intended to be instantiated or consumed directly by any other code or plugin.
*
* @package WordPress
* @subpackage Abilities_API
* @since 6.9.0
*
* @internal This class is not part of the public API.
* @access private
*/

declare( strict_types=1 );

/**
* Registers core user abilities.
*
* @since 6.9.0
* @access private
*/
class WP_Users_Abilities {

/**
* Registers all user-related abilities.
*
* @since 6.9.0
*
* @return void
*/
public static function register(): void {
self::register_get_user();
}

/**
* Registers the core/get-user ability.
*
* @since 6.9.0
*
* @return void
*/
private static function register_get_user(): void {
wp_register_ability(
'core/get-user',
array(
'label' => __( 'Get User' ),
'description' => __( 'Returns comprehensive profile details for a user by id, username, or email.' ),
'category' => 'user',
'input_schema' => self::get_user_input_schema(),
'output_schema' => self::get_user_output_schema(),
'execute_callback' => array( __CLASS__, 'execute_get_user' ),
'permission_callback' => array( __CLASS__, 'check_get_user_permission' ),
'meta' => array(
'annotations' => array(
'readonly' => true,
'destructive' => false,
'idempotent' => true,
),
'show_in_rest' => true,
),
)
);
}

/**
* Gets the input schema for the get-user ability.
*
* @since 6.9.0
*
* @return array<string, mixed> The input schema.
*/
private static function get_user_input_schema(): array {
return array(
'type' => 'object',
'oneOf' => array(
array( 'required' => array( 'id' ) ),
array( 'required' => array( 'username' ) ),
array( 'required' => array( 'email' ) ),
),
'properties' => array(
'id' => array(
'type' => 'integer',
'description' => __( 'User ID.' ),
),
'username' => array(
'type' => 'string',
'description' => __( 'User login name.' ),
),
'email' => array(
'type' => 'string',
'description' => __( 'User email address.' ),
),
'include_capabilities' => array(
'type' => 'boolean',
'description' => __( 'Whether to include the user capabilities in the response.' ),
'default' => false,
),
),
'additionalProperties' => false,
);
}

/**
* Gets the output schema for the get-user ability.
*
* @since 6.9.0
*
* @return array<string, mixed> The output schema.
*/
private static function get_user_output_schema(): array {
return array(
'type' => 'object',
'required' => array(
'id',
'username',
'email',
'display_name',
'first_name',
'last_name',
'nickname',
'description',
'url',
'link',
'slug',
'registered_date',
'roles',
'locale',
'avatar_urls',
),
'properties' => array(
'id' => array(
'type' => 'integer',
'description' => __( 'Unique identifier for the user.' ),
),
'username' => array(
'type' => 'string',
'description' => __( 'Login name for the user.' ),
),
'email' => array(
'type' => 'string',
'description' => __( 'The email address for the user.' ),
),
'display_name' => array(
'type' => 'string',
'description' => __( 'Display name for the user.' ),
),
'first_name' => array(
'type' => 'string',
'description' => __( 'First name for the user.' ),
),
'last_name' => array(
'type' => 'string',
'description' => __( 'Last name for the user.' ),
),
'nickname' => array(
'type' => 'string',
'description' => __( 'The nickname for the user.' ),
),
'description' => array(
'type' => 'string',
'description' => __( 'Description of the user.' ),
),
'url' => array(
'type' => 'string',
'description' => __( 'URL of the user.' ),
),
'link' => array(
'type' => 'string',
'description' => __( 'Author URL of the user.' ),
),
'slug' => array(
'type' => 'string',
'description' => __( 'An alphanumeric identifier for the user.' ),
),
'registered_date' => array(
'type' => 'string',
'description' => __( 'Registration date for the user in ISO 8601 format.' ),
),
'roles' => array(
'type' => 'array',
'description' => __( 'Roles assigned to the user.' ),
'items' => array(
'type' => 'string',
),
),
'locale' => array(
'type' => 'string',
'description' => __( 'Locale for the user.' ),
),
'avatar_urls' => array(
'type' => 'object',
'description' => __( 'Avatar URLs for the user at various sizes.' ),
),
'capabilities' => array(
'type' => 'object',
'description' => __( 'All capabilities assigned to the user. Only included if include_capabilities is true.' ),
),
),
'additionalProperties' => false,
);
}

/**
* Finds a user by id, username, or email from input parameters.
*
* @since 6.9.0
*
* @param array<string, mixed> $input The input parameters.
* @return WP_User|false The user object if found, false otherwise.
*/
private static function find_user( array $input ) {
if ( ! empty( $input['id'] ) ) {
return get_user_by( 'ID', (int) $input['id'] );
}

if ( ! empty( $input['username'] ) ) {
return get_user_by( 'login', sanitize_user( $input['username'] ) );
}

if ( ! empty( $input['email'] ) ) {
return get_user_by( 'email', sanitize_email( $input['email'] ) );
}

return false;
}

/**
* Permission callback for the get-user ability.
*
* @since 6.9.0
*
* @param array<string, mixed> $input The input parameters.
* @return bool Whether the user has permission.
*/
public static function check_get_user_permission( array $input = array() ): bool {
// Must be logged in.
if ( ! is_user_logged_in() ) {
return false;
}

$user = self::find_user( $input );
$target_user_id = $user ? $user->ID : 0;

// Users can view their own profile.
if ( get_current_user_id() === $target_user_id ) {
return true;
}

// Otherwise require list_users capability.
return current_user_can( 'list_users' );
}

/**
* Executes the get-user ability.
*
* @since 6.9.0
*
* @param array<string, mixed> $input The input parameters.
* @return array<string, mixed>|WP_Error The user data or error.
*/
public static function execute_get_user( array $input = array() ) {
$input = is_array( $input ) ? $input : array();
$include_capabilities = ! empty( $input['include_capabilities'] ) && ( true === $input['include_capabilities'] || 'true' === $input['include_capabilities'] );

$user = self::find_user( $input );

if ( ! $user || ! $user->exists() ) {
return new WP_Error( 'user_not_found', __( 'User not found.' ), array( 'status' => 404 ) );
}

$result = array(
'id' => $user->ID,
'username' => $user->user_login,
'email' => $user->user_email,
'display_name' => $user->display_name,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'nickname' => $user->nickname,
'description' => $user->description,
'url' => $user->user_url,
'link' => get_author_posts_url( $user->ID, $user->user_nicename ),
'slug' => $user->user_nicename,
'registered_date' => gmdate( 'c', strtotime( $user->user_registered ) ),
'roles' => array_values( $user->roles ),
'locale' => get_user_locale( $user ),
'avatar_urls' => rest_get_avatar_urls( $user ),
);

if ( $include_capabilities ) {
$result['capabilities'] = (object) $user->allcaps;
}

return $result;
}
}
Loading