Make WordPress Core

Changeset 62419


Ignore:
Timestamp:
05/26/2026 08:58:11 AM (3 months ago)
Author:
gziolo
Message:

Abilities API: Expand core/get-user-info with profile fields

Extends the core/get-user-info ability with additional profile fields (first_name, last_name, nickname, description, user_url) and a new optional fields input parameter that lets callers limit the response to a specific subset.

The output schema now documents each property with a title and description, mirroring the user profile form labels where they apply and aligning the descriptions with the WP REST API user schema voice.

Also ensures roles is encoded as a JSON array regardless of the underlying PHP array keys.

Props sukhendu2002, apermo, gziolo.
Fixes #65234.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/abilities.php

    r62396 r62419  
    135135        );
    136136
     137        $user_info_properties = array(
     138                'id'            => array(
     139                        'type'        => 'integer',
     140                        'title'       => __( 'User ID' ),
     141                        'description' => __( 'Unique numeric identifier for the user.' ),
     142                ),
     143                'display_name'  => array(
     144                        'type'        => 'string',
     145                        'title'       => __( 'Display Name' ),
     146                        'description' => __( 'Public-facing name selected by the user.' ),
     147                ),
     148                'user_nicename' => array(
     149                        'type'        => 'string',
     150                        'title'       => __( 'User Nicename' ),
     151                        'description' => __( 'URL-friendly slug for the user. Defaults to the username.' ),
     152                ),
     153                'user_login'    => array(
     154                        'type'        => 'string',
     155                        'title'       => __( 'Username' ),
     156                        'description' => __( 'Login identifier for the user. Cannot be changed once set.' ),
     157                ),
     158                'roles'         => array(
     159                        'type'        => 'array',
     160                        'title'       => __( 'Roles' ),
     161                        'description' => __( 'Roles assigned to the user, such as administrator, editor, author, contributor, or subscriber.' ),
     162                        'items'       => array(
     163                                'type' => 'string',
     164                        ),
     165                ),
     166                'locale'        => array(
     167                        'type'        => 'string',
     168                        'title'       => __( 'Language' ),
     169                        'description' => __( 'Locale code for the user, such as en_US.' ),
     170                ),
     171                'first_name'    => array(
     172                        'type'        => 'string',
     173                        'title'       => __( 'First Name' ),
     174                        'description' => __( 'Given name.' ),
     175                ),
     176                'last_name'     => array(
     177                        'type'        => 'string',
     178                        'title'       => __( 'Last Name' ),
     179                        'description' => __( 'Family name.' ),
     180                ),
     181                'nickname'      => array(
     182                        'type'        => 'string',
     183                        'title'       => __( 'Nickname' ),
     184                        'description' => __( 'Informal name. Defaults to the username.' ),
     185                ),
     186                'description'   => array(
     187                        'type'        => 'string',
     188                        'title'       => __( 'Biographical Info' ),
     189                        'description' => __( 'User-authored biography, often shown on author pages.' ),
     190                ),
     191                'user_url'      => array(
     192                        'type'        => 'string',
     193                        'title'       => __( 'Website' ),
     194                        'description' => __( 'Personal website URL.' ),
     195                ),
     196        );
     197        $user_info_fields     = array_keys( $user_info_properties );
     198
    137199        wp_register_ability(
    138200                'core/get-user-info',
    139201                array(
    140202                        'label'               => __( 'Get User Information' ),
    141                         'description'         => __( 'Returns basic profile details for the current authenticated user to support personalization, auditing, and access-aware behavior.' ),
     203                        '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.' ),
    142204                        'category'            => $category_user,
    143                         'output_schema'       => array(
    144                                 'type'                 => 'object',
    145                                 'required'             => array( 'id', 'display_name', 'user_nicename', 'user_login', 'roles', 'locale' ),
     205                        'input_schema'        => array(
     206                                'type'                 => 'object',
    146207                                'properties'           => array(
    147                                         'id'            => array(
    148                                                 'type'        => 'integer',
    149                                                 'description' => __( 'The user ID.' ),
    150                                         ),
    151                                         'display_name'  => array(
    152                                                 'type'        => 'string',
    153                                                 'description' => __( 'The display name of the user.' ),
    154                                         ),
    155                                         'user_nicename' => array(
    156                                                 'type'        => 'string',
    157                                                 'description' => __( 'The URL-friendly name for the user.' ),
    158                                         ),
    159                                         'user_login'    => array(
    160                                                 'type'        => 'string',
    161                                                 'description' => __( 'The login username for the user.' ),
    162                                         ),
    163                                         'roles'         => array(
     208                                        'fields' => array(
    164209                                                'type'        => 'array',
    165                                                 'description' => __( 'The roles assigned to the user.' ),
    166210                                                'items'       => array(
    167211                                                        'type' => 'string',
     212                                                        'enum' => $user_info_fields,
    168213                                                ),
    169                                         ),
    170                                         'locale'        => array(
    171                                                 'type'        => 'string',
    172                                                 'description' => __( 'The locale string for the user, such as en_US.' ),
    173                                         ),
    174                                 ),
    175                                 'additionalProperties' => false,
    176                         ),
    177                         'execute_callback'    => static function (): array {
    178                                 $current_user = wp_get_current_user();
    179 
    180                                 return array(
     214                                                'description' => __( 'Optional: Limit response to specific fields. If omitted, all fields are returned.' ),
     215                                        ),
     216                                ),
     217                                'additionalProperties' => false,
     218                                'default'              => array(),
     219                        ),
     220                        'output_schema'       => array(
     221                                'type'                 => 'object',
     222                                'properties'           => $user_info_properties,
     223                                'additionalProperties' => false,
     224                        ),
     225                        'execute_callback'    => static function ( $input = array() ) use ( $user_info_fields ): array {
     226                                $input            = is_array( $input ) ? $input : array();
     227                                $requested_fields = ! empty( $input['fields'] ) ? $input['fields'] : $user_info_fields;
     228                                $current_user     = wp_get_current_user();
     229
     230                                $all = array(
    181231                                        'id'            => $current_user->ID,
    182232                                        'display_name'  => $current_user->display_name,
    183233                                        'user_nicename' => $current_user->user_nicename,
    184234                                        'user_login'    => $current_user->user_login,
    185                                         'roles'         => $current_user->roles,
     235                                        // Ensure roles are encoded as a JSON array, regardless of their array keys.
     236                                        'roles'         => array_values( $current_user->roles ),
    186237                                        'locale'        => get_user_locale( $current_user ),
     238                                        'first_name'    => $current_user->first_name,
     239                                        'last_name'     => $current_user->last_name,
     240                                        'nickname'      => $current_user->nickname,
     241                                        'description'   => $current_user->description,
     242                                        'user_url'      => $current_user->user_url,
    187243                                );
     244
     245                                return array_intersect_key( $all, array_flip( $requested_fields ) );
    188246                        },
    189247                        'permission_callback' => static function (): bool {
  • trunk/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php

    r62396 r62419  
    163163                $user_id = self::factory()->user->create(
    164164                        array(
    165                                 'role'   => 'subscriber',
    166                                 'locale' => 'fr_FR',
     165                                'role'        => 'subscriber',
     166                                'locale'      => 'fr_FR',
     167                                'first_name'  => 'Jane',
     168                                'last_name'   => 'Doe',
     169                                'nickname'    => 'janed',
     170                                'description' => 'Site contributor.',
     171                                'user_url'    => 'https://example.com',
    167172                        )
    168173                );
     
    179184                $this->assertSame( 'subscriber', $result['roles'][0] );
    180185                $this->assertSame( get_userdata( $user_id )->display_name, $result['display_name'] );
     186
     187                // New profile fields should be present by default.
     188                $this->assertSame( 'Jane', $result['first_name'] );
     189                $this->assertSame( 'Doe', $result['last_name'] );
     190                $this->assertSame( 'janed', $result['nickname'] );
     191                $this->assertSame( 'Site contributor.', $result['description'] );
     192                $this->assertSame( 'https://example.com', $result['user_url'] );
     193        }
     194
     195        /**
     196         * Tests that the `core/get-user-info` ability is registered with the expected schema.
     197         * @ticket 65234
     198         */
     199        public function test_core_get_user_info_ability_is_registered(): void {
     200                $ability = wp_get_ability( 'core/get-user-info' );
     201
     202                $this->assertInstanceOf( WP_Ability::class, $ability );
     203
     204                $input_schema  = $ability->get_input_schema();
     205                $output_schema = $ability->get_output_schema();
     206
     207                // Input schema should expose an optional `fields` array with an enum of valid field names.
     208                $this->assertSame( 'object', $input_schema['type'] );
     209                $this->assertArrayHasKey( 'default', $input_schema );
     210                $this->assertSame( array(), $input_schema['default'] );
     211                $this->assertArrayHasKey( 'fields', $input_schema['properties'] );
     212                $this->assertSame( 'array', $input_schema['properties']['fields']['type'] );
     213
     214                $enum = $input_schema['properties']['fields']['items']['enum'];
     215                foreach ( array( 'id', 'display_name', 'first_name', 'last_name', 'nickname', 'description', 'user_url' ) as $field ) {
     216                        $this->assertContains( $field, $enum );
     217                }
     218
     219                // Output schema should document the original and new profile fields with title + description.
     220                foreach ( array( 'id', 'display_name', 'first_name', 'last_name', 'nickname', 'description', 'user_url' ) as $field ) {
     221                        $this->assertArrayHasKey( $field, $output_schema['properties'] );
     222                        $this->assertArrayHasKey( 'title', $output_schema['properties'][ $field ] );
     223                        $this->assertArrayHasKey( 'description', $output_schema['properties'][ $field ] );
     224                }
     225        }
     226
     227        /**
     228         * Tests that the `core/get-user-info` ability filters its output by the `fields` input parameter.
     229         * @ticket 65234
     230         */
     231        public function test_core_get_user_info_filters_fields(): void {
     232                $user_id = self::factory()->user->create(
     233                        array(
     234                                'role'       => 'subscriber',
     235                                'first_name' => 'Jane',
     236                                'last_name'  => 'Doe',
     237                        )
     238                );
     239                wp_set_current_user( $user_id );
     240
     241                $ability = wp_get_ability( 'core/get-user-info' );
     242
     243                $result = $ability->execute(
     244                        array(
     245                                'fields' => array( 'display_name', 'first_name', 'last_name' ),
     246                        )
     247                );
     248
     249                $this->assertIsArray( $result );
     250                $this->assertCount( 3, $result );
     251                $this->assertArrayHasKey( 'display_name', $result );
     252                $this->assertArrayHasKey( 'first_name', $result );
     253                $this->assertArrayHasKey( 'last_name', $result );
     254                $this->assertArrayNotHasKey( 'id', $result );
     255                $this->assertArrayNotHasKey( 'roles', $result );
     256                $this->assertSame( 'Jane', $result['first_name'] );
     257                $this->assertSame( 'Doe', $result['last_name'] );
     258        }
     259
     260        /**
     261         * Tests that the `core/get-user-info` ability rejects unknown field names via schema validation.
     262         * @ticket 65234
     263         */
     264        public function test_core_get_user_info_rejects_invalid_fields(): void {
     265                $user_id = self::factory()->user->create( array( 'role' => 'subscriber' ) );
     266                wp_set_current_user( $user_id );
     267
     268                $ability = wp_get_ability( 'core/get-user-info' );
     269
     270                $result = $ability->execute(
     271                        array(
     272                                'fields' => array( 'display_name', 'not_a_real_field' ),
     273                        )
     274                );
     275
     276                $this->assertWPError( $result );
     277                $this->assertSame( 'ability_invalid_input', $result->get_error_code() );
    181278        }
    182279
Note: See TracChangeset for help on using the changeset viewer.