Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
115 changes: 86 additions & 29 deletions src/wp-includes/abilities.php
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you verify that the provided strings match with the ones already used in the profile.php so that no string duplication occurs?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, you're right, four of the five new titles already match existing core strings, but 'Website URL' was a duplicate I introduced. Updated to use 'Website' to match wp-admin/user-edit.php.

Original file line number Diff line number Diff line change
Expand Up @@ -130,57 +130,114 @@ function wp_register_core_abilities(): void {
)
);

$user_info_properties = array(
'id' => array(
'type' => 'integer',
'title' => __( 'User ID' ),
'description' => __( 'The user ID.' ),
),
'display_name' => array(
'type' => 'string',
'title' => __( 'Display Name' ),
'description' => __( 'The display name of the user.' ),
),
'user_nicename' => array(
'type' => 'string',
'title' => __( 'User Nicename' ),
'description' => __( 'The URL-friendly name for the user.' ),
),
'user_login' => array(
'type' => 'string',
'title' => __( 'Username' ),
'description' => __( 'The login username for the user.' ),
),
'roles' => array(
'type' => 'array',
'title' => __( 'Roles' ),
'description' => __( 'The roles assigned to the user.' ),
'items' => array(
'type' => 'string',
),
),
'locale' => array(
'type' => 'string',
'title' => __( 'Locale' ),
'description' => __( 'The locale string for the user, such as en_US.' ),
),
'first_name' => array(
'type' => 'string',
'title' => __( 'First Name' ),
'description' => __( 'The first name of the user.' ),
),
'last_name' => array(
'type' => 'string',
'title' => __( 'Last Name' ),
'description' => __( 'The last name of the user.' ),
),
'nickname' => array(
'type' => 'string',
'title' => __( 'Nickname' ),
'description' => __( 'The nickname of the user.' ),
),
'description' => array(
'type' => 'string',
'title' => __( 'Biographical Info' ),
'description' => __( 'The biographical description of the user.' ),
),
'user_url' => array(
'type' => 'string',
'title' => __( 'Website URL' ),
'description' => __( 'The URL of the user\'s website.' ),
),
);
$user_info_fields = array_keys( $user_info_properties );

wp_register_ability(
'core/get-user-info',
array(
'label' => __( 'Get User Information' ),
'description' => __( 'Returns basic profile details for the current authenticated user to support personalization, auditing, and access-aware behavior.' ),
'description' => __( 'Returns profile details for the current authenticated user to support personalization, auditing, and access-aware behavior. By default returns all fields, or optionally a filtered subset.' ),
'category' => $category_user,
'output_schema' => array(
'input_schema' => array(
'type' => 'object',
'required' => array( 'id', 'display_name', 'user_nicename', 'user_login', 'roles', 'locale' ),
'properties' => array(
'id' => array(
'type' => 'integer',
'description' => __( 'The user ID.' ),
),
'display_name' => array(
'type' => 'string',
'description' => __( 'The display name of the user.' ),
),
'user_nicename' => array(
'type' => 'string',
'description' => __( 'The URL-friendly name for the user.' ),
),
'user_login' => array(
'type' => 'string',
'description' => __( 'The login username for the user.' ),
),
'roles' => array(
'fields' => array(
'type' => 'array',
'description' => __( 'The roles assigned to the user.' ),
'items' => array(
'type' => 'string',
'enum' => $user_info_fields,
),
),
'locale' => array(
'type' => 'string',
'description' => __( 'The locale string for the user, such as en_US.' ),
'description' => __( 'Optional: Limit response to specific fields. If omitted, all fields are returned.' ),
),
),
'additionalProperties' => false,
'default' => array(),
),
'execute_callback' => static function (): array {
$current_user = wp_get_current_user();
'output_schema' => array(
'type' => 'object',
'properties' => $user_info_properties,
'additionalProperties' => false,
),
'execute_callback' => static function ( $input = array() ) use ( $user_info_fields ): array {
$input = is_array( $input ) ? $input : array();
$requested_fields = ! empty( $input['fields'] ) ? $input['fields'] : $user_info_fields;
$current_user = wp_get_current_user();

return array(
$all = array(
'id' => $current_user->ID,
'display_name' => $current_user->display_name,
'user_nicename' => $current_user->user_nicename,
'user_login' => $current_user->user_login,
'roles' => $current_user->roles,
'roles' => array_values( $current_user->roles ),
'locale' => get_user_locale( $current_user ),
'first_name' => $current_user->first_name,
'last_name' => $current_user->last_name,
'nickname' => $current_user->nickname,
'description' => $current_user->description,
'user_url' => $current_user->user_url,
);

return array_intersect_key( $all, array_flip( $requested_fields ) );
},
'permission_callback' => static function (): bool {
return is_user_logged_in();
Expand Down
101 changes: 99 additions & 2 deletions tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,13 @@ public function test_core_get_current_user_info_requires_authentication(): void
public function test_core_get_current_user_info_returns_user_data(): void {
$user_id = self::factory()->user->create(
array(
'role' => 'subscriber',
'locale' => 'fr_FR',
'role' => 'subscriber',
'locale' => 'fr_FR',
'first_name' => 'Jane',
'last_name' => 'Doe',
'nickname' => 'janed',
'description' => 'Site contributor.',
'user_url' => 'https://example.com',
)
);

Expand All @@ -152,6 +157,98 @@ public function test_core_get_current_user_info_returns_user_data(): void {
$this->assertSame( 'fr_FR', $result['locale'] );
$this->assertSame( 'subscriber', $result['roles'][0] );
$this->assertSame( get_userdata( $user_id )->display_name, $result['display_name'] );

// New profile fields should be present by default.
$this->assertSame( 'Jane', $result['first_name'] );
$this->assertSame( 'Doe', $result['last_name'] );
$this->assertSame( 'janed', $result['nickname'] );
$this->assertSame( 'Site contributor.', $result['description'] );
$this->assertSame( 'https://example.com', $result['user_url'] );
}

/**
* Tests that the `core/get-user-info` ability is registered with the expected schema.
* @ticket 65234
*/
public function test_core_get_user_info_ability_is_registered(): void {
$ability = wp_get_ability( 'core/get-user-info' );

$this->assertInstanceOf( WP_Ability::class, $ability );

$input_schema = $ability->get_input_schema();
$output_schema = $ability->get_output_schema();

// Input schema should expose an optional `fields` array with an enum of valid field names.
$this->assertSame( 'object', $input_schema['type'] );
$this->assertArrayHasKey( 'default', $input_schema );
$this->assertSame( array(), $input_schema['default'] );
$this->assertArrayHasKey( 'fields', $input_schema['properties'] );
$this->assertSame( 'array', $input_schema['properties']['fields']['type'] );

$enum = $input_schema['properties']['fields']['items']['enum'];
foreach ( array( 'id', 'display_name', 'first_name', 'last_name', 'nickname', 'description', 'user_url' ) as $field ) {
$this->assertContains( $field, $enum );
}

// Output schema should document the original and new profile fields with title + description.
foreach ( array( 'id', 'display_name', 'first_name', 'last_name', 'nickname', 'description', 'user_url' ) as $field ) {
$this->assertArrayHasKey( $field, $output_schema['properties'] );
$this->assertArrayHasKey( 'title', $output_schema['properties'][ $field ] );
$this->assertArrayHasKey( 'description', $output_schema['properties'][ $field ] );
}
}

/**
* Tests that the `core/get-user-info` ability filters its output by the `fields` input parameter.
* @ticket 65234
*/
public function test_core_get_user_info_filters_fields(): void {
$user_id = self::factory()->user->create(
array(
'role' => 'subscriber',
'first_name' => 'Jane',
'last_name' => 'Doe',
)
);
wp_set_current_user( $user_id );

$ability = wp_get_ability( 'core/get-user-info' );

$result = $ability->execute(
array(
'fields' => array( 'display_name', 'first_name', 'last_name' ),
)
);

$this->assertIsArray( $result );
$this->assertCount( 3, $result );
$this->assertArrayHasKey( 'display_name', $result );
$this->assertArrayHasKey( 'first_name', $result );
$this->assertArrayHasKey( 'last_name', $result );
$this->assertArrayNotHasKey( 'id', $result );
$this->assertArrayNotHasKey( 'roles', $result );
$this->assertSame( 'Jane', $result['first_name'] );
$this->assertSame( 'Doe', $result['last_name'] );
}

/**
* Tests that the `core/get-user-info` ability rejects unknown field names via schema validation.
* @ticket 65234
*/
public function test_core_get_user_info_rejects_invalid_fields(): void {
$user_id = self::factory()->user->create( array( 'role' => 'subscriber' ) );
wp_set_current_user( $user_id );

$ability = wp_get_ability( 'core/get-user-info' );

$result = $ability->execute(
array(
'fields' => array( 'display_name', 'not_a_real_field' ),
)
);

$this->assertWPError( $result );
$this->assertSame( 'ability_invalid_input', $result->get_error_code() );
}

/**
Expand Down
Loading