summaryrefslogtreecommitdiffstats
path: root/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php
diff options
context:
space:
mode:
Diffstat (limited to 'wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php')
-rw-r--r--wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php342
1 files changed, 342 insertions, 0 deletions
diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php
new file mode 100644
index 0000000..365afd3
--- /dev/null
+++ b/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php
@@ -0,0 +1,342 @@
+<?php
+/**
+ * REST API: WP_REST_Settings_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 4.7.0
+ */
+
+/**
+ * Core class used to manage a site's settings via the REST API.
+ *
+ * @since 4.7.0
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Settings_Controller extends WP_REST_Controller {
+
+ /**
+ * Constructor.
+ *
+ * @since 4.7.0
+ */
+ public function __construct() {
+ $this->namespace = 'wp/v2';
+ $this->rest_base = 'settings';
+ }
+
+ /**
+ * Registers the routes for the site's settings.
+ *
+ * @since 4.7.0
+ *
+ * @see register_rest_route()
+ */
+ public function register_routes() {
+
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_item' ),
+ 'args' => array(),
+ 'permission_callback' => array( $this, 'get_item_permissions_check' ),
+ ),
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'update_item' ),
+ 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
+ 'permission_callback' => array( $this, 'get_item_permissions_check' ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * Checks if a given request has access to read and manage settings.
+ *
+ * @since 4.7.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return bool True if the request has read access for the item, otherwise false.
+ */
+ public function get_item_permissions_check( $request ) {
+ return current_user_can( 'manage_options' );
+ }
+
+ /**
+ * Retrieves the settings.
+ *
+ * @since 4.7.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return array|WP_Error Array on success, or WP_Error object on failure.
+ */
+ public function get_item( $request ) {
+ $options = $this->get_registered_options();
+ $response = array();
+
+ foreach ( $options as $name => $args ) {
+ /**
+ * Filters the value of a setting recognized by the REST API.
+ *
+ * Allow hijacking the setting value and overriding the built-in behavior by returning a
+ * non-null value. The returned value will be presented as the setting value instead.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $result Value to use for the requested setting. Can be a scalar
+ * matching the registered schema for the setting, or null to
+ * follow the default get_option() behavior.
+ * @param string $name Setting name (as shown in REST API responses).
+ * @param array $args Arguments passed to register_setting() for this setting.
+ */
+ $response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args );
+
+ if ( is_null( $response[ $name ] ) ) {
+ // Default to a null value as "null" in the response means "not set".
+ $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] );
+ }
+
+ /*
+ * Because get_option() is lossy, we have to
+ * cast values to the type they are registered with.
+ */
+ $response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] );
+ }
+
+ return $response;
+ }
+
+ /**
+ * Prepares a value for output based off a schema array.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $value Value to prepare.
+ * @param array $schema Schema to match.
+ * @return mixed The prepared value.
+ */
+ protected function prepare_value( $value, $schema ) {
+ /*
+ * If the value is not valid by the schema, set the value to null.
+ * Null values are specifically non-destructive, so this will not cause
+ * overwriting the current invalid value to null.
+ */
+ if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) {
+ return null;
+ }
+
+ return rest_sanitize_value_from_schema( $value, $schema );
+ }
+
+ /**
+ * Updates settings for the settings object.
+ *
+ * @since 4.7.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return array|WP_Error Array on success, or error object on failure.
+ */
+ public function update_item( $request ) {
+ $options = $this->get_registered_options();
+
+ $params = $request->get_params();
+
+ foreach ( $options as $name => $args ) {
+ if ( ! array_key_exists( $name, $params ) ) {
+ continue;
+ }
+
+ /**
+ * Filters whether to preempt a setting value update via the REST API.
+ *
+ * Allows hijacking the setting update logic and overriding the built-in behavior by
+ * returning true.
+ *
+ * @since 4.7.0
+ *
+ * @param bool $result Whether to override the default behavior for updating the
+ * value of a setting.
+ * @param string $name Setting name (as shown in REST API responses).
+ * @param mixed $value Updated setting value.
+ * @param array $args Arguments passed to register_setting() for this setting.
+ */
+ $updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args );
+
+ if ( $updated ) {
+ continue;
+ }
+
+ /*
+ * A null value for an option would have the same effect as
+ * deleting the option from the database, and relying on the
+ * default value.
+ */
+ if ( is_null( $request[ $name ] ) ) {
+ /*
+ * A null value is returned in the response for any option
+ * that has a non-scalar value.
+ *
+ * To protect clients from accidentally including the null
+ * values from a response object in a request, we do not allow
+ * options with values that don't pass validation to be updated to null.
+ * Without this added protection a client could mistakenly
+ * delete all options that have invalid values from the
+ * database.
+ */
+ if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) {
+ return new WP_Error(
+ 'rest_invalid_stored_value',
+ /* translators: %s: Property name. */
+ sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
+ array( 'status' => 500 )
+ );
+ }
+
+ delete_option( $args['option_name'] );
+ } else {
+ update_option( $args['option_name'], $request[ $name ] );
+ }
+ }
+
+ return $this->get_item( $request );
+ }
+
+ /**
+ * Retrieves all of the registered options for the Settings API.
+ *
+ * @since 4.7.0
+ *
+ * @return array Array of registered options.
+ */
+ protected function get_registered_options() {
+ $rest_options = array();
+
+ foreach ( get_registered_settings() as $name => $args ) {
+ if ( empty( $args['show_in_rest'] ) ) {
+ continue;
+ }
+
+ $rest_args = array();
+
+ if ( is_array( $args['show_in_rest'] ) ) {
+ $rest_args = $args['show_in_rest'];
+ }
+
+ $defaults = array(
+ 'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name,
+ 'schema' => array(),
+ );
+
+ $rest_args = array_merge( $defaults, $rest_args );
+
+ $default_schema = array(
+ 'type' => empty( $args['type'] ) ? null : $args['type'],
+ 'description' => empty( $args['description'] ) ? '' : $args['description'],
+ 'default' => isset( $args['default'] ) ? $args['default'] : null,
+ );
+
+ $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] );
+ $rest_args['option_name'] = $name;
+
+ // Skip over settings that don't have a defined type in the schema.
+ if ( empty( $rest_args['schema']['type'] ) ) {
+ continue;
+ }
+
+ /*
+ * Allow the supported types for settings, as we don't want invalid types
+ * to be updated with arbitrary values that we can't do decent sanitizing for.
+ */
+ if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'integer', 'string', 'boolean', 'array', 'object' ), true ) ) {
+ continue;
+ }
+
+ $rest_args['schema'] = rest_default_additional_properties_to_false( $rest_args['schema'] );
+
+ $rest_options[ $rest_args['name'] ] = $rest_args;
+ }
+
+ return $rest_options;
+ }
+
+ /**
+ * Retrieves the site setting schema, conforming to JSON Schema.
+ *
+ * @since 4.7.0
+ *
+ * @return array Item schema data.
+ */
+ public function get_item_schema() {
+ if ( $this->schema ) {
+ return $this->add_additional_fields_schema( $this->schema );
+ }
+
+ $options = $this->get_registered_options();
+
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'settings',
+ 'type' => 'object',
+ 'properties' => array(),
+ );
+
+ foreach ( $options as $option_name => $option ) {
+ $schema['properties'][ $option_name ] = $option['schema'];
+ $schema['properties'][ $option_name ]['arg_options'] = array(
+ 'sanitize_callback' => array( $this, 'sanitize_callback' ),
+ );
+ }
+
+ $this->schema = $schema;
+
+ return $this->add_additional_fields_schema( $this->schema );
+ }
+
+ /**
+ * Custom sanitize callback used for all options to allow the use of 'null'.
+ *
+ * By default, the schema of settings will throw an error if a value is set to
+ * `null` as it's not a valid value for something like "type => string". We
+ * provide a wrapper sanitizer to allow the use of `null`.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $value The value for the setting.
+ * @param WP_REST_Request $request The request object.
+ * @param string $param The parameter name.
+ * @return mixed|WP_Error
+ */
+ public function sanitize_callback( $value, $request, $param ) {
+ if ( is_null( $value ) ) {
+ return $value;
+ }
+
+ return rest_parse_request_arg( $value, $request, $param );
+ }
+
+ /**
+ * Recursively add additionalProperties = false to all objects in a schema
+ * if no additionalProperties setting is specified.
+ *
+ * This is needed to restrict properties of objects in settings values to only
+ * registered items, as the REST API will allow additional properties by
+ * default.
+ *
+ * @since 4.9.0
+ * @deprecated 6.1.0 Use {@see rest_default_additional_properties_to_false()} instead.
+ *
+ * @param array $schema The schema array.
+ * @return array
+ */
+ protected function set_additional_properties_to_false( $schema ) {
+ _deprecated_function( __METHOD__, '6.1.0', 'rest_default_additional_properties_to_false()' );
+
+ return rest_default_additional_properties_to_false( $schema );
+ }
+}