diff --git a/api/class-wc-rest-dev-setting-options-controller.php b/api/class-wc-rest-dev-setting-options-controller.php index 94a6dfc..be5d7a9 100644 --- a/api/class-wc-rest-dev-setting-options-controller.php +++ b/api/class-wc-rest-dev-setting-options-controller.php @@ -77,4 +77,140 @@ class WC_REST_Dev_Setting_Options_Controller extends WC_REST_Setting_Options_Con */ protected $namespace = 'wc/v3'; + /** + * Get setting data. + * + * @param string $group_id Group ID. + * @param string $setting_id Setting ID. + * @return stdClass|WP_Error + */ + public function get_setting( $group_id, $setting_id ) { + $setting = parent::get_setting( $group_id, $setting_id ); + if ( is_wp_error( $setting ) ) { + return $setting; + } + $setting['group_id'] = $group_id; + return $setting; + } + + /** + * Callback for allowed keys for each setting response. + * + * @param string $key Key to check + * @return boolean + */ + public function allowed_setting_keys( $key ) { + return in_array( $key, array( + 'id', + 'group_id', + 'label', + 'description', + 'default', + 'tip', + 'placeholder', + 'type', + 'options', + 'value', + 'option_key', + ) ); + } + + /** + * Get the settings schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'setting', + 'type' => 'object', + 'properties' => array( + 'id' => array( + 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_title', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'group_id' => array( + 'description' => __( 'An identifier for the group this setting belongs to.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_title', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'label' => array( + 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_text_field', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'description' => array( + 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_text_field', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'value' => array( + 'description' => __( 'Setting value.', 'woocommerce' ), + 'type' => 'mixed', + 'context' => array( 'view', 'edit' ), + ), + 'default' => array( + 'description' => __( 'Default value for the setting.', 'woocommerce' ), + 'type' => 'mixed', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'tip' => array( + 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_text_field', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'placeholder' => array( + 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_text_field', + ), + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'type' => array( + 'description' => __( 'Type of setting.', 'woocommerce' ), + 'type' => 'string', + 'arg_options' => array( + 'sanitize_callback' => 'sanitize_text_field', + ), + 'context' => array( 'view', 'edit' ), + 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), + 'readonly' => true, + ), + 'options' => array( + 'description' => __( 'Array of options (key value pairs) for inputs such as select, multiselect, and radio buttons.', 'woocommerce' ), + 'type' => 'object', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } + } diff --git a/api/class-wc-rest-dev-settings-controller.php b/api/class-wc-rest-dev-settings-controller.php index 7bcd58a..1478e09 100644 --- a/api/class-wc-rest-dev-settings-controller.php +++ b/api/class-wc-rest-dev-settings-controller.php @@ -4,10 +4,9 @@ * * Handles requests to the /settings endpoints. * - * @author WooThemes + * @author Automattic * @category API * @package WooCommerce/API - * @since 3.0.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -26,4 +25,46 @@ class WC_REST_Dev_Settings_Controller extends WC_REST_Settings_Controller { */ protected $namespace = 'wc/v3'; + /** + * Register routes. + * + */ + public function register_routes() { + parent::register_routes(); + register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'batch_items' ), + 'permission_callback' => array( $this, 'update_items_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_batch_schema' ), + ) ); + } + + /** + * Makes sure the current user has access to WRITE the settings APIs. + * + * @param WP_REST_Request $request Full data about the request. + * @return WP_Error|boolean + */ + public function update_items_permissions_check( $request ) { + if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) { + return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); + } + + return true; + } + + /** + * Update a setting. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public function update_item( $request ) { + $options_controller = new WC_REST_Dev_Setting_Options_Controller; + $response = $options_controller->update_item( $request ); + return $response; + } + } diff --git a/tests/unit-tests/functions.php b/tests/unit-tests/functions.php index b44d8c2..7b593e0 100644 --- a/tests/unit-tests/functions.php +++ b/tests/unit-tests/functions.php @@ -64,7 +64,7 @@ public function test_wc_rest_validate_reports_request_arg() { * @since 2.6.0 */ public function test_wc_rest_urlencode_rfc3986() { - $this->assertEquals( 'https%253A%252F%252Fwoocommerce.com%252F', wc_rest_urlencode_rfc3986( 'https://woocommerce.com/' ) ); + $this->assertEquals( 'https%3A%2F%2Fwoocommerce.com%2F', wc_rest_urlencode_rfc3986( 'https://woocommerce.com/' ) ); } /** diff --git a/tests/unit-tests/settings.php b/tests/unit-tests/settings.php index 57020c7..baf3b61 100644 --- a/tests/unit-tests/settings.php +++ b/tests/unit-tests/settings.php @@ -28,7 +28,9 @@ public function setUp() { public function test_register_routes() { $routes = $this->server->get_routes(); $this->assertArrayHasKey( '/wc/v3/settings', $routes ); + $this->assertArrayHasKey( '/wc/v3/settings/batch', $routes ); $this->assertArrayHasKey( '/wc/v3/settings/(?P[\w-]+)', $routes ); + $this->assertArrayHasKey( '/wc/v3/settings/(?P[\w-]+)/batch', $routes ); $this->assertArrayHasKey( '/wc/v3/settings/(?P[\w-]+)/(?P[\w-]+)', $routes ); } @@ -133,8 +135,9 @@ public function test_get_setting_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 9, count( $properties ) ); + $this->assertEquals( 10, count( $properties ) ); $this->assertArrayHasKey( 'id', $properties ); + $this->assertArrayHasKey( 'group_id', $properties ); $this->assertArrayHasKey( 'label', $properties ); $this->assertArrayHasKey( 'description', $properties ); $this->assertArrayHasKey( 'value', $properties ); @@ -285,6 +288,28 @@ public function test_update_settings() { $this->assertEquals( 'both', $data['update'][0]['value'] ); $this->assertEquals( 'both', get_option( 'woocommerce_shop_page_display' ) ); + // test bulk settings batch endpoint + $request = new WP_REST_Request( 'POST', '/wc/v3/settings/batch' ); + $request->set_body_params( array( + 'update' => array( + array( + 'group_id' => 'test', + 'id' => 'woocommerce_shop_page_display', + 'value' => 'subcategories', + ), + array( + 'group_id' => 'products', + 'id' => 'woocommerce_dimension_unit', + 'value' => 'yd', + ), + ), + ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'subcategories', $data['update'][0]['value'] ); + $this->assertEquals( 'yd', $data['update'][1]['value'] ); + $this->assertEquals( 'yd', get_option( 'woocommerce_dimension_unit' ) ); + // test updating one, but making sure the other value stays the same $request = new WP_REST_Request( 'POST', '/wc/v3/settings/test/batch' ); $request->set_body_params( array( @@ -539,11 +564,12 @@ public function test_email_settings() { $this->assertEquals( array( 'id' => 'subject', 'label' => 'Subject', - 'description' => 'This controls the email subject line. Leave blank to use the default subject: [{site_title}] New customer order ({order_number}) - {order_date}.', + 'description' => 'Available placeholders: {site_title}, {order_date}, {order_number}', 'type' => 'text', 'default' => '', - 'tip' => 'This controls the email subject line. Leave blank to use the default subject: [{site_title}] New customer order ({order_number}) - {order_date}.', + 'tip' => 'Available placeholders: {site_title}, {order_date}, {order_number}', 'value' => '', + 'group_id' => 'email_new_order', ), $setting ); // test update @@ -557,11 +583,12 @@ public function test_email_settings() { $this->assertEquals( array( 'id' => 'subject', 'label' => 'Subject', - 'description' => 'This controls the email subject line. Leave blank to use the default subject: [{site_title}] New customer order ({order_number}) - {order_date}.', + 'description' => 'Available placeholders: {site_title}, {order_date}, {order_number}', 'type' => 'text', 'default' => '', - 'tip' => 'This controls the email subject line. Leave blank to use the default subject: [{site_title}] New customer order ({order_number}) - {order_date}.', + 'tip' => 'Available placeholders: {site_title}, {order_date}, {order_number}', 'value' => 'This is my subject', + 'group_id' => 'email_new_order', ), $setting ); // test updating another subject and making sure it works with a "similar" id