diff --git a/src/wp-includes/abilities.php b/src/wp-includes/abilities.php index 0d167337215aa..bb5baccc3d7ef 100644 --- a/src/wp-includes/abilities.php +++ b/src/wp-includes/abilities.php @@ -46,35 +46,43 @@ function wp_register_core_abilities(): void { $site_info_properties = array( 'name' => array( 'type' => 'string', + 'title' => __( 'Site Title' ), 'description' => __( 'The site title.' ), ), 'description' => array( 'type' => 'string', + 'title' => __( 'Tagline' ), 'description' => __( 'The site tagline.' ), ), 'url' => array( 'type' => 'string', - 'description' => __( 'The site home URL.' ), + 'title' => __( 'Site Address (URL)' ), + 'description' => __( 'The public URL where visitors access the site. May differ from the WordPress installation URL.' ), ), 'wpurl' => array( 'type' => 'string', - 'description' => __( 'The WordPress installation URL.' ), + 'title' => __( 'WordPress Address (URL)' ), + 'description' => __( 'The URL where WordPress core files are served. May differ from the public site URL.' ), ), 'admin_email' => array( 'type' => 'string', + 'title' => __( 'Administration Email Address' ), 'description' => __( 'The site administrator email address.' ), ), 'charset' => array( 'type' => 'string', + 'title' => __( 'Site Charset' ), 'description' => __( 'The site character encoding.' ), ), 'language' => array( 'type' => 'string', - 'description' => __( 'The site language locale code.' ), + 'title' => __( 'Site Language' ), + 'description' => __( 'The site locale in dash form (e.g. en-US).' ), ), 'version' => array( 'type' => 'string', - 'description' => __( 'The WordPress version.' ), + 'title' => __( 'WordPress Version' ), + 'description' => __( 'The WordPress core version running on this site.' ), ), ); $site_info_fields = array_keys( $site_info_properties ); @@ -138,7 +146,7 @@ function wp_register_core_abilities(): void { 'id' => array( 'type' => 'integer', 'title' => __( 'User ID' ), - 'description' => __( 'Unique numeric identifier for the user.' ), + 'description' => __( 'Unique identifier for the user.' ), ), 'display_name' => array( 'type' => 'string', @@ -186,7 +194,7 @@ function wp_register_core_abilities(): void { 'description' => array( 'type' => 'string', 'title' => __( 'Biographical Info' ), - 'description' => __( 'User-authored biography, often shown on author pages.' ), + 'description' => __( 'User-authored biography. May be empty.' ), ), 'user_url' => array( 'type' => 'string', @@ -253,58 +261,81 @@ function wp_register_core_abilities(): void { 'destructive' => false, 'idempotent' => true, ), - 'show_in_rest' => false, + 'show_in_rest' => true, ), ) ); + $environment_info_properties = array( + 'environment' => array( + 'type' => 'string', + 'title' => __( 'Environment Type' ), + 'description' => __( 'The site\'s runtime environment classification.' ), + 'enum' => array( 'production', 'staging', 'development', 'local' ), + ), + 'php_version' => array( + 'type' => 'string', + 'title' => __( 'PHP Version' ), + 'description' => __( 'The PHP runtime version executing WordPress.' ), + ), + 'db_server_info' => array( + 'type' => 'string', + 'title' => __( 'Database Server Info' ), + 'description' => __( 'The database server vendor and version string reported by the driver.' ), + ), + 'wp_version' => array( + 'type' => 'string', + 'title' => __( 'WordPress Version' ), + 'description' => __( 'The WordPress core version running on this site.' ), + ), + ); + $environment_info_fields = array_keys( $environment_info_properties ); + wp_register_ability( 'core/get-environment-info', array( 'label' => __( 'Get Environment Info' ), - 'description' => __( 'Returns core details about the site\'s runtime context for diagnostics and compatibility (environment, PHP runtime, database server info, WordPress version).' ), + 'description' => __( 'Returns core details about the site\'s runtime context for diagnostics and compatibility (environment, PHP runtime, database server info, WordPress version). By default returns all fields, or optionally a filtered subset.' ), 'category' => $category_site, - 'output_schema' => array( + 'input_schema' => array( 'type' => 'object', - 'required' => array( 'environment', 'php_version', 'db_server_info', 'wp_version' ), 'properties' => array( - 'environment' => array( - 'type' => 'string', - 'description' => __( 'The site\'s runtime environment classification (can be one of these: production, staging, development, local).' ), - 'enum' => array( 'production', 'staging', 'development', 'local' ), - ), - 'php_version' => array( - 'type' => 'string', - 'description' => __( 'The PHP runtime version executing WordPress.' ), - ), - 'db_server_info' => array( - 'type' => 'string', - 'description' => __( 'The database server vendor and version string reported by the driver.' ), - ), - 'wp_version' => array( - 'type' => 'string', - 'description' => __( 'The WordPress core version running on this site.' ), + 'fields' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string', + 'enum' => $environment_info_fields, + ), + 'description' => __( 'Optional: Limit response to specific fields. If omitted, all fields are returned.' ), ), ), 'additionalProperties' => false, + 'default' => array(), + ), + 'output_schema' => array( + 'type' => 'object', + 'properties' => $environment_info_properties, + 'additionalProperties' => false, ), - 'execute_callback' => static function (): array { + 'execute_callback' => static function ( $input = array() ) use ( $environment_info_fields ): array { global $wpdb; - $env = wp_get_environment_type(); - $php_version = phpversion(); - $db_server_info = ''; + $input = is_array( $input ) ? $input : array(); + $requested_fields = ! empty( $input['fields'] ) ? $input['fields'] : $environment_info_fields; + + $db_server_info = ''; if ( method_exists( $wpdb, 'db_server_info' ) ) { $db_server_info = $wpdb->db_server_info() ?? ''; } - $wp_version = get_bloginfo( 'version' ); - return array( - 'environment' => $env, - 'php_version' => $php_version, + $all = array( + 'environment' => wp_get_environment_type(), + 'php_version' => phpversion(), 'db_server_info' => $db_server_info, - 'wp_version' => $wp_version, + 'wp_version' => get_bloginfo( 'version' ), ); + + return array_intersect_key( $all, array_flip( $requested_fields ) ); }, 'permission_callback' => static function (): bool { return current_user_can( 'manage_options' ); diff --git a/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php b/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php index a1cd1a4420ae2..c85174406ea95 100644 --- a/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php +++ b/tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php @@ -70,15 +70,18 @@ public function test_core_get_site_info_ability_is_registered(): void { $this->assertArrayHasKey( 'default', $input_schema ); $this->assertSame( array(), $input_schema['default'] ); - // Input schema should have optional fields array. $this->assertArrayHasKey( 'fields', $input_schema['properties'] ); $this->assertSame( 'array', $input_schema['properties']['fields']['type'] ); - $this->assertContains( 'name', $input_schema['properties']['fields']['items']['enum'] ); - // Output schema should have all fields documented. - $this->assertArrayHasKey( 'name', $output_schema['properties'] ); - $this->assertArrayHasKey( 'url', $output_schema['properties'] ); - $this->assertArrayHasKey( 'version', $output_schema['properties'] ); + $expected_fields = array( 'name', 'description', 'url', 'wpurl', 'admin_email', 'charset', 'language', 'version' ); + + $this->assertSame( $expected_fields, $input_schema['properties']['fields']['items']['enum'] ); + $this->assertSame( $expected_fields, array_keys( $output_schema['properties'] ) ); + + foreach ( $expected_fields as $field ) { + $this->assertArrayHasKey( 'title', $output_schema['properties'][ $field ] ); + $this->assertArrayHasKey( 'description', $output_schema['properties'][ $field ] ); + } } /** @@ -200,25 +203,23 @@ 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 ); + $this->assertTrue( $ability->get_meta_item( 'show_in_rest', false ) ); $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 ); - } + $expected_fields = array( 'id', 'display_name', 'user_nicename', 'user_login', 'roles', 'locale', 'first_name', 'last_name', 'nickname', 'description', 'user_url' ); + + $this->assertSame( $expected_fields, $input_schema['properties']['fields']['items']['enum'] ); + $this->assertSame( $expected_fields, array_keys( $output_schema['properties'] ) ); - // 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'] ); + foreach ( $expected_fields as $field ) { $this->assertArrayHasKey( 'title', $output_schema['properties'][ $field ] ); $this->assertArrayHasKey( 'description', $output_schema['properties'][ $field ] ); } @@ -298,6 +299,83 @@ public function test_core_get_environment_info_executes(): void { $this->assertSame( $environment, $ability_data['environment'] ); } + /** + * Tests that the `core/get-environment-info` ability is registered with the expected schema. + * + * @ticket 65355 + */ + public function test_core_get_environment_info_ability_is_registered(): void { + $ability = wp_get_ability( 'core/get-environment-info' ); + + $this->assertInstanceOf( WP_Ability::class, $ability ); + $this->assertTrue( $ability->get_meta_item( 'show_in_rest', false ) ); + + $input_schema = $ability->get_input_schema(); + $output_schema = $ability->get_output_schema(); + + $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'] ); + + $expected_fields = array( 'environment', 'php_version', 'db_server_info', 'wp_version' ); + + $this->assertSame( $expected_fields, $input_schema['properties']['fields']['items']['enum'] ); + $this->assertSame( $expected_fields, array_keys( $output_schema['properties'] ) ); + + foreach ( $expected_fields as $field ) { + $this->assertArrayHasKey( 'title', $output_schema['properties'][ $field ] ); + $this->assertArrayHasKey( 'description', $output_schema['properties'][ $field ] ); + } + } + + /** + * Tests that the `core/get-environment-info` ability filters its output by the `fields` input parameter. + * + * @ticket 65355 + */ + public function test_core_get_environment_info_filters_fields(): void { + $admin_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $admin_id ); + + $ability = wp_get_ability( 'core/get-environment-info' ); + + $result = $ability->execute( + array( + 'fields' => array( 'environment', 'wp_version' ), + ) + ); + + $this->assertIsArray( $result ); + $this->assertCount( 2, $result ); + $this->assertArrayHasKey( 'environment', $result ); + $this->assertArrayHasKey( 'wp_version', $result ); + $this->assertArrayNotHasKey( 'php_version', $result ); + $this->assertArrayNotHasKey( 'db_server_info', $result ); + } + + /** + * Tests that the `core/get-environment-info` ability rejects unknown field names via schema validation. + * + * @ticket 65355 + */ + public function test_core_get_environment_info_rejects_invalid_fields(): void { + $admin_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $admin_id ); + + $ability = wp_get_ability( 'core/get-environment-info' ); + + $result = $ability->execute( + array( + 'fields' => array( 'environment', 'not_a_real_field' ), + ) + ); + + $this->assertWPError( $result ); + $this->assertSame( 'ability_invalid_input', $result->get_error_code() ); + } + /** * Tests that all core ability schemas only use valid JSON Schema keywords. *