diff --git a/src/wp-includes/abilities.php b/src/wp-includes/abilities.php index 0320df3b9f38a..3ea82e8867f06 100644 --- a/src/wp-includes/abilities.php +++ b/src/wp-includes/abilities.php @@ -9,6 +9,8 @@ declare( strict_types = 1 ); +require_once __DIR__ . '/abilities/class-wp-settings-abilities.php'; + /** * Registers the core ability categories. * @@ -259,4 +261,6 @@ function wp_register_core_abilities(): void { ), ) ); + + WP_Settings_Abilities::register(); } diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php new file mode 100644 index 0000000000000..59d56c00f5476 --- /dev/null +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -0,0 +1,299 @@ + $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $group = $args['group'] ?? 'general'; + + $rest_name = $option_name; + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { + $rest_name = $args['show_in_rest']['name']; + } + + $setting_schema = array( + 'type' => $args['type'] ?? 'string', + ); + + if ( ! empty( $args['label'] ) ) { + $setting_schema['title'] = $args['label']; + } + + if ( ! empty( $args['description'] ) ) { + $setting_schema['description'] = $args['description']; + } elseif ( ! empty( $args['label'] ) ) { + $setting_schema['description'] = $args['label']; + } + + if ( isset( $args['default'] ) ) { + $setting_schema['default'] = $args['default']; + } + + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['schema'] ) ) { + $setting_schema = array_merge( $setting_schema, $args['show_in_rest']['schema'] ); + } + + if ( ! isset( $group_properties[ $group ] ) ) { + $group_properties[ $group ] = array( + 'type' => 'object', + 'description' => sprintf( + /* translators: %s: Settings group name. */ + __( '%s settings.' ), + ucfirst( $group ) + ), + 'properties' => array(), + ); + } + + $group_properties[ $group ]['properties'][ $rest_name ] = $setting_schema; + } + + ksort( $group_properties ); + + return array( + 'type' => 'object', + 'description' => __( 'Settings grouped by registration group. Each group contains settings with their current values.' ), + 'properties' => $group_properties, + ); + } + + /** + * Registers the core/get-settings ability. + * + * @since 6.9.0 + * + * @return void + */ + private static function register_get_settings(): void { + wp_register_ability( + 'core/get-settings', + array( + 'label' => __( 'Get Settings' ), + 'description' => __( 'Returns registered WordPress settings grouped by their registration group. Returns key-value pairs per setting.' ), + 'category' => 'site', + 'input_schema' => array( + 'type' => 'object', + 'properties' => array( + 'group' => array( + 'type' => 'string', + 'description' => __( 'Filter settings by group name. If omitted, returns all groups.' ), + 'enum' => self::$available_groups, + ), + ), + 'additionalProperties' => false, + 'default' => array(), + ), + 'output_schema' => self::$output_schema, + 'execute_callback' => array( __CLASS__, 'execute_get_settings' ), + 'permission_callback' => array( __CLASS__, 'check_manage_options' ), + 'meta' => array( + 'annotations' => array( + 'readOnlyHint' => true, + 'destructiveHint' => false, + 'idempotentHint' => true, + ), + 'show_in_rest' => true, + ), + ) + ); + } + + /** + * Permission callback for settings abilities. + * + * @since 6.9.0 + * + * @return bool True if the current user can manage options, false otherwise. + */ + public static function check_manage_options(): bool { + return current_user_can( 'manage_options' ); + } + + /** + * Execute callback for core/get-settings ability. + * + * Retrieves all registered settings that are exposed to the REST API, + * grouped by their registration group. + * + * @since 6.9.0 + * + * @param array $input { + * Optional. Input parameters. + * + * @type string $group Optional. Filter settings by group name. + * } + * @return array Settings grouped by registration group. + */ + public static function execute_get_settings( $input = array() ): array { + $input = is_array( $input ) ? $input : array(); + $filter_group = ! empty( $input['group'] ) ? $input['group'] : null; + + $registered_settings = get_registered_settings(); + $settings_by_group = array(); + + foreach ( $registered_settings as $option_name => $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $group = $args['group'] ?? 'general'; + + if ( $filter_group && $group !== $filter_group ) { + continue; + } + + $rest_name = $option_name; + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { + $rest_name = $args['show_in_rest']['name']; + } + + $default = $args['default'] ?? null; + if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema']['default'] ) ) { + $default = $args['show_in_rest']['schema']['default']; + } + + $value = get_option( $option_name, $default ); + $value = self::cast_value( $value, $args['type'] ?? 'string' ); + + if ( ! isset( $settings_by_group[ $group ] ) ) { + $settings_by_group[ $group ] = array(); + } + + $settings_by_group[ $group ][ $rest_name ] = $value; + } + + ksort( $settings_by_group ); + + return $settings_by_group; + } + + /** + * Casts a value to the appropriate type based on the setting's registered type. + * + * @since 6.9.0 + * + * @param mixed $value The value to cast. + * @param string $type The registered type (string, boolean, integer, number, array, object). + * @return mixed The cast value. + */ + private static function cast_value( $value, string $type ) { + switch ( $type ) { + case 'boolean': + return (bool) $value; + case 'integer': + return (int) $value; + case 'number': + return (float) $value; + case 'array': + case 'object': + return is_array( $value ) ? $value : array(); + case 'string': + default: + return (string) $value; + } + } +}