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
7 changes: 6 additions & 1 deletion src/wp-includes/abilities/class-wp-settings-abilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private static function get_available_slugs(): array {
*
* Creates a JSON Schema that documents each setting group and its settings
* with their types, titles, descriptions, defaults, and any additional
* schema properties from show_in_rest.
* schema properties from show_in_abilities.
*
* @since 7.0.0
*
Expand All @@ -163,6 +163,11 @@ private static function build_output_schema(): array {
$setting_schema['description'] = $args['label'];
}

// Merge custom schema from show_in_abilities if provided as an array.
if ( is_array( $args['show_in_abilities'] ) && ! empty( $args['show_in_abilities']['schema'] ) ) {
$setting_schema = array_merge( $setting_schema, $args['show_in_abilities']['schema'] );
}

if ( ! isset( $group_properties[ $group ] ) ) {
$group_properties[ $group ] = array(
'type' => 'object',
Expand Down
41 changes: 23 additions & 18 deletions src/wp-includes/option.php
Original file line number Diff line number Diff line change
Expand Up @@ -2768,35 +2768,37 @@ function register_initial_settings() {
);

if ( ! is_multisite() ) {
$uri_schema = array( 'format' => 'uri' );
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think we just inline this . Conceptually, the schemas are different (or we'd be abstracting to a top-level schema prop, instead of nesting), so this a convenience helper. and since it's a 1-line array that makes this block longer, it doesnt feel like its that "convenient" imo

register_setting(
'general',
'siteurl',
array(
'show_in_rest' => array(
'name' => 'url',
'schema' => array(
'format' => 'uri',
),
'schema' => $uri_schema,
),
'show_in_abilities' => array(
'schema' => $uri_schema,
),
'show_in_abilities' => true,
'type' => 'string',
'description' => __( 'Site URL.' ),
)
);
}

if ( ! is_multisite() ) {
$email_schema = array( 'format' => 'email' );
register_setting(
'general',
'admin_email',
array(
'show_in_rest' => array(
'name' => 'email',
'schema' => array(
'format' => 'email',
),
'schema' => $email_schema,
),
'show_in_abilities' => array(
'schema' => $email_schema,
),
'show_in_abilities' => true,
'type' => 'string',
'description' => __( 'This address is used for admin purposes, like new user notification.' ),
)
Expand Down Expand Up @@ -2945,16 +2947,18 @@ function register_initial_settings() {
)
);

$open_closed_enum_schema = array( 'enum' => array( 'open', 'closed' ) );
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(If https://github.com/WordPress/wordpress-develop/pull/10954/files#r2816934895, I would reuse the enum sand make it 'schema' => [ 'enum' => $open_closed_enum ] )


register_setting(
'discussion',
'default_ping_status',
array(
'show_in_rest' => array(
'schema' => array(
'enum' => array( 'open', 'closed' ),
),
'schema' => $open_closed_enum_schema,
),
'show_in_abilities' => array(
'schema' => $open_closed_enum_schema,
),
'show_in_abilities' => true,
'type' => 'string',
'description' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new articles.' ),
)
Expand All @@ -2965,11 +2969,11 @@ function register_initial_settings() {
'default_comment_status',
array(
'show_in_rest' => array(
'schema' => array(
'enum' => array( 'open', 'closed' ),
),
'schema' => $open_closed_enum_schema,
),
'show_in_abilities' => array(
'schema' => $open_closed_enum_schema,
),
'show_in_abilities' => true,
'type' => 'string',
'label' => __( 'Allow comments on new posts' ),
'description' => __( 'Allow people to submit comments on new posts.' ),
Expand Down Expand Up @@ -3006,8 +3010,9 @@ function register_initial_settings() {
* @type bool|array $show_in_rest Whether data associated with this setting should be included in the REST API.
* When registering complex settings, this argument may optionally be an
* array with a 'schema' key.
* @type bool $show_in_abilities Whether this setting should be exposed through the Abilities API.
* Default false.
* @type bool|array $show_in_abilities Whether this setting should be exposed through the Abilities API.
* When registering complex settings, this argument may optionally be an
* array with a 'schema' key. Default false.
* @type mixed $default Default value when calling `get_option()`.
* }
*/
Expand Down
85 changes: 85 additions & 0 deletions tests/phpunit/tests/rest-api/wpRestAbilitiesSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,89 @@ public function test_core_get_settings_returns_correct_values(): void {

$this->assertSame( 'Test Site Name', $data['general']['blogname'] );
}

/**
* Tests that settings with enum schema in show_in_abilities include it in output schema.
*
* @ticket 64605
*/
public function test_core_get_settings_output_schema_includes_enum(): void {
$ability = wp_get_ability( 'core/get-settings' );
$output_schema = $ability->get_output_schema();

// Check default_ping_status has enum.
$this->assertArrayHasKey( 'discussion', $output_schema['properties'] );
$this->assertArrayHasKey( 'default_ping_status', $output_schema['properties']['discussion']['properties'] );
$this->assertArrayHasKey( 'enum', $output_schema['properties']['discussion']['properties']['default_ping_status'] );
$this->assertSame( array( 'open', 'closed' ), $output_schema['properties']['discussion']['properties']['default_ping_status']['enum'] );

// Check default_comment_status has enum.
$this->assertArrayHasKey( 'default_comment_status', $output_schema['properties']['discussion']['properties'] );
$this->assertArrayHasKey( 'enum', $output_schema['properties']['discussion']['properties']['default_comment_status'] );
$this->assertSame( array( 'open', 'closed' ), $output_schema['properties']['discussion']['properties']['default_comment_status']['enum'] );
}

/**
* Tests that boolean show_in_abilities (true) still works correctly.
*
* @ticket 64605
*/
public function test_core_get_settings_boolean_show_in_abilities_still_works(): void {
$ability = wp_get_ability( 'core/get-settings' );
$output_schema = $ability->get_output_schema();

// blogname uses show_in_abilities => true (boolean).
$this->assertArrayHasKey( 'general', $output_schema['properties'] );
$this->assertArrayHasKey( 'blogname', $output_schema['properties']['general']['properties'] );
$this->assertSame( 'string', $output_schema['properties']['general']['properties']['blogname']['type'] );
}

/**
* Tests that custom show_in_abilities schema preserves base schema properties while adding custom ones.
*
* @ticket 64605
*/
public function test_core_get_settings_output_schema_preserves_base_schema(): void {
$ability = wp_get_ability( 'core/get-settings' );
$output_schema = $ability->get_output_schema();

// default_comment_status has show_in_abilities with schema but also has label and description.
$this->assertArrayHasKey( 'discussion', $output_schema['properties'] );
$this->assertArrayHasKey( 'default_comment_status', $output_schema['properties']['discussion']['properties'] );

$setting_schema = $output_schema['properties']['discussion']['properties']['default_comment_status'];

// Verify base schema properties are preserved.
$this->assertSame( 'string', $setting_schema['type'] );
$this->assertArrayHasKey( 'title', $setting_schema );
$this->assertArrayHasKey( 'description', $setting_schema );

// Verify custom schema property (enum) is merged.
$this->assertArrayHasKey( 'enum', $setting_schema );
$this->assertSame( array( 'open', 'closed' ), $setting_schema['enum'] );
}

/**
* Tests that ability returns error when setting value violates schema enum.
*
* @ticket 64605
*/
public function test_core_get_settings_returns_error_for_invalid_enum_value(): void {
// Set an invalid value for default_ping_status (violates enum: ['open', 'closed']).
update_option( 'default_ping_status', 'invalid_value' );

$request = new WP_REST_Request( 'GET', '/wp-abilities/v1/abilities/core/get-settings/run' );
$request->set_query_params(
array(
'input' => array(
'slugs' => array( 'default_ping_status' ),
),
)
);
$response = $this->server->dispatch( $request );

$this->assertSame( 500, $response->get_status() );
$data = $response->get_data();
$this->assertSame( 'ability_invalid_output', $data['code'] );
}
}
Loading