From d88ab33dda3dd89b17e6e7b6107eafcb8cc8f85b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 14 Jun 2019 11:44:09 +0100 Subject: [PATCH] Split batch and settings into traits --- .../Version4/AbstractController.php | 492 +++--------------- .../Version4/AbstractObjectsController.php | 78 +-- src/Controllers/Version4/BatchTrait.php | 263 ++++++++++ src/Controllers/Version4/Coupons.php | 92 +--- src/Controllers/Version4/Orders.php | 82 --- src/Controllers/Version4/PaymentGateways.php | 1 + src/Controllers/Version4/Products.php | 86 --- src/Controllers/Version4/SettingsOptions.php | 1 + src/Controllers/Version4/SettingsTrait.php | 152 ++++++ .../Version4/ShippingZoneMethods.php | 1 + 10 files changed, 545 insertions(+), 703 deletions(-) create mode 100644 src/Controllers/Version4/BatchTrait.php create mode 100644 src/Controllers/Version4/SettingsTrait.php diff --git a/src/Controllers/Version4/AbstractController.php b/src/Controllers/Version4/AbstractController.php index fe827724f19..a9b6b06b7ea 100644 --- a/src/Controllers/Version4/AbstractController.php +++ b/src/Controllers/Version4/AbstractController.php @@ -2,9 +2,6 @@ /** * REST Controller * - * This class extend `WP_REST_Controller` in order to include /batch endpoint - * for almost all endpoints in WooCommerce REST API. - * * It's required to follow "Controller Classes" guide before extending this class: * * @@ -42,425 +39,92 @@ abstract class AbstractController extends WP_REST_Controller { */ protected $rest_base = ''; + /** + * Register route for items requests. + */ + protected function register_items_route() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'create_item' ), + 'permission_callback' => array( $this, 'create_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ), + true + ); + } + + /** + * Register route for item create/get/delete/update requests. + */ + protected function register_item_route() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)', + array( + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), + 'type' => 'integer', + ), + ), + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( + array( + 'default' => 'view', + ) + ), + ), + ), + array( + 'methods' => \WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'permission_callback' => array( $this, 'update_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), + ), + array( + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), + 'permission_callback' => array( $this, 'delete_item_permissions_check' ), + 'args' => array( + 'force' => array( + 'default' => false, + 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), + 'type' => 'boolean', + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ), + true + ); + } + /** * Add the schema from additional fields to an schema array. * - * The type of object is inferred from the passed schema. - * * @param array $schema Schema array. - * * @return array */ protected function add_additional_fields_schema( $schema ) { - if ( empty( $schema['title'] ) ) { - return $schema; - } - - /** - * Can't use $this->get_object_type otherwise we cause an inf loop. - */ - $object_type = $schema['title']; - - $additional_fields = $this->get_additional_fields( $object_type ); - - foreach ( $additional_fields as $field_name => $field_options ) { - if ( ! $field_options['schema'] ) { - continue; - } - - $schema['properties'][ $field_name ] = $field_options['schema']; - } - + $schema = parent::add_additional_fields_schema( $schema ); + $object_type = $schema['title']; $schema['properties'] = apply_filters( 'woocommerce_rest_' . $object_type . '_schema', $schema['properties'] ); - return $schema; } - - /** - * Get normalized rest base. - * - * @return string - */ - protected function get_normalized_rest_base() { - return preg_replace( '/\(.*\)\//i', '', $this->rest_base ); - } - - /** - * Check batch limit. - * - * @param array $items Request items. - * @return bool|\WP_Error - */ - protected function check_batch_limit( $items ) { - $limit = apply_filters( 'woocommerce_rest_batch_items_limit', 100, $this->get_normalized_rest_base() ); - $total = 0; - - if ( ! empty( $items['create'] ) ) { - $total += count( $items['create'] ); - } - - if ( ! empty( $items['update'] ) ) { - $total += count( $items['update'] ); - } - - if ( ! empty( $items['delete'] ) ) { - $total += count( $items['delete'] ); - } - - if ( $total > $limit ) { - /* translators: %s: items limit */ - return new \WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) ); - } - - return true; - } - - /** - * Bulk create, update and delete items. - * - * @param \WP_REST_Request $request Full details about the request. - * @return array Of \WP_Error or \WP_REST_Response. - */ - public function batch_items( $request ) { - /** - * REST Server - * - * @var WP_REST_Server $wp_rest_server - */ - global $wp_rest_server; - - // Get the request params. - $items = array_filter( $request->get_params() ); - $response = array(); - - // Check batch limit. - $limit = $this->check_batch_limit( $items ); - if ( is_wp_error( $limit ) ) { - return $limit; - } - - if ( ! empty( $items['create'] ) ) { - foreach ( $items['create'] as $item ) { - $_item = new \WP_REST_Request( 'POST' ); - - // Default parameters. - $defaults = array(); - $schema = $this->get_public_item_schema(); - foreach ( $schema['properties'] as $arg => $options ) { - if ( isset( $options['default'] ) ) { - $defaults[ $arg ] = $options['default']; - } - } - $_item->set_default_params( $defaults ); - - // Set request parameters. - $_item->set_body_params( $item ); - $_response = $this->create_item( $_item ); - - if ( is_wp_error( $_response ) ) { - $response['create'][] = array( - 'id' => 0, - 'error' => array( - 'code' => $_response->get_error_code(), - 'message' => $_response->get_error_message(), - 'data' => $_response->get_error_data(), - ), - ); - } else { - $response['create'][] = $wp_rest_server->response_to_data( $_response, '' ); - } - } - } - - if ( ! empty( $items['update'] ) ) { - foreach ( $items['update'] as $item ) { - $_item = new \WP_REST_Request( 'PUT' ); - $_item->set_body_params( $item ); - $_response = $this->update_item( $_item ); - - if ( is_wp_error( $_response ) ) { - $response['update'][] = array( - 'id' => $item['id'], - 'error' => array( - 'code' => $_response->get_error_code(), - 'message' => $_response->get_error_message(), - 'data' => $_response->get_error_data(), - ), - ); - } else { - $response['update'][] = $wp_rest_server->response_to_data( $_response, '' ); - } - } - } - - if ( ! empty( $items['delete'] ) ) { - foreach ( $items['delete'] as $id ) { - $id = (int) $id; - - if ( 0 === $id ) { - continue; - } - - $_item = new \WP_REST_Request( 'DELETE' ); - $_item->set_query_params( - array( - 'id' => $id, - 'force' => true, - ) - ); - $_response = $this->delete_item( $_item ); - - if ( is_wp_error( $_response ) ) { - $response['delete'][] = array( - 'id' => $id, - 'error' => array( - 'code' => $_response->get_error_code(), - 'message' => $_response->get_error_message(), - 'data' => $_response->get_error_data(), - ), - ); - } else { - $response['delete'][] = $wp_rest_server->response_to_data( $_response, '' ); - } - } - } - - return $response; - } - - /** - * Validate a text value for a text based setting. - * - * @since 3.0.0 - * @param string $value Value. - * @param array $setting Setting. - * @return string - */ - public function validate_setting_text_field( $value, $setting ) { - $value = is_null( $value ) ? '' : $value; - return wp_kses_post( trim( stripslashes( $value ) ) ); - } - - /** - * Validate select based settings. - * - * @since 3.0.0 - * @param string $value Value. - * @param array $setting Setting. - * @return string|\WP_Error - */ - public function validate_setting_select_field( $value, $setting ) { - if ( array_key_exists( $value, $setting['options'] ) ) { - return $value; - } else { - return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - } - - /** - * Validate multiselect based settings. - * - * @since 3.0.0 - * @param array $values Values. - * @param array $setting Setting. - * @return array|\WP_Error - */ - public function validate_setting_multiselect_field( $values, $setting ) { - if ( empty( $values ) ) { - return array(); - } - - if ( ! is_array( $values ) ) { - return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $final_values = array(); - foreach ( $values as $value ) { - if ( array_key_exists( $value, $setting['options'] ) ) { - $final_values[] = $value; - } - } - - return $final_values; - } - - /** - * Validate image_width based settings. - * - * @since 3.0.0 - * @param array $values Values. - * @param array $setting Setting. - * @return string|\WP_Error - */ - public function validate_setting_image_width_field( $values, $setting ) { - if ( ! is_array( $values ) ) { - return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $current = $setting['value']; - if ( isset( $values['width'] ) ) { - $current['width'] = intval( $values['width'] ); - } - if ( isset( $values['height'] ) ) { - $current['height'] = intval( $values['height'] ); - } - if ( isset( $values['crop'] ) ) { - $current['crop'] = (bool) $values['crop']; - } - return $current; - } - - /** - * Validate radio based settings. - * - * @since 3.0.0 - * @param string $value Value. - * @param array $setting Setting. - * @return string|\WP_Error - */ - public function validate_setting_radio_field( $value, $setting ) { - return $this->validate_setting_select_field( $value, $setting ); - } - - /** - * Validate checkbox based settings. - * - * @since 3.0.0 - * @param string $value Value. - * @param array $setting Setting. - * @return string|\WP_Error - */ - public function validate_setting_checkbox_field( $value, $setting ) { - if ( in_array( $value, array( 'yes', 'no' ), true ) ) { - return $value; - } elseif ( empty( $value ) ) { - $value = isset( $setting['default'] ) ? $setting['default'] : 'no'; - return $value; - } else { - return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - } - - /** - * Validate textarea based settings. - * - * @since 3.0.0 - * @param string $value Value. - * @param array $setting Setting. - * @return string - */ - public function validate_setting_textarea_field( $value, $setting ) { - $value = is_null( $value ) ? '' : $value; - return wp_kses( - trim( stripslashes( $value ) ), - array_merge( - array( - 'iframe' => array( - 'src' => true, - 'style' => true, - 'id' => true, - 'class' => true, - ), - ), - wp_kses_allowed_html( 'post' ) - ) - ); - } - - /** - * Add meta query. - * - * @since 3.0.0 - * @param array $args Query args. - * @param array $meta_query Meta query. - * @return array - */ - protected function add_meta_query( $args, $meta_query ) { - if ( empty( $args['meta_query'] ) ) { - $args['meta_query'] = []; // phpcs:ignore - } - - $args['meta_query'][] = $meta_query; - - return $args['meta_query']; - } - - /** - * Get the batch schema, conforming to JSON Schema. - * - * @return array - */ - public function get_public_batch_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'batch', - 'type' => 'object', - 'properties' => array( - 'create' => array( - 'description' => __( 'List of created resources.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - ), - ), - 'update' => array( - 'description' => __( 'List of updated resources.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - ), - ), - 'delete' => array( - 'description' => __( 'List of delete resources.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'integer', - ), - ), - ), - ); - - return $schema; - } - - /** - * Gets an array of fields to be included on the response. - * Included fields are based on item schema and `_fields=` request argument. - * Introduced to support WordPress 4.9.6 changes. - * - * @since 3.5.0 - * @param \WP_REST_Request $request Full details about the request. - * @return array Fields to be included in the response. - */ - public function get_fields_for_response( $request ) { - $schema = $this->get_item_schema(); - $fields = isset( $schema['properties'] ) ? array_keys( $schema['properties'] ) : array(); - - $additional_fields = $this->get_additional_fields(); - foreach ( $additional_fields as $field_name => $field_options ) { - // For back-compat, include any field with an empty schema - // because it won't be present in $this->get_item_schema(). - if ( is_null( $field_options['schema'] ) ) { - $fields[] = $field_name; - } - } - - if ( ! isset( $request['_fields'] ) ) { - return $fields; - } - $requested_fields = array_filter( is_array( $request['_fields'] ) ? $request['_fields'] : (array) preg_split( '/[\s,]+/', $request['_fields'] ) ); - if ( 0 === count( $requested_fields ) ) { - return $fields; - } - // Trim off outside whitespace from the comma delimited list. - $requested_fields = array_map( 'trim', $requested_fields ); - // Always persist 'id', because it can be needed for add_additional_fields_to_object(). - if ( in_array( 'id', $fields, true ) ) { - $requested_fields[] = 'id'; - } - return array_intersect( $fields, $requested_fields ); - } } diff --git a/src/Controllers/Version4/AbstractObjectsController.php b/src/Controllers/Version4/AbstractObjectsController.php index c0b2a202196..efca889a057 100644 --- a/src/Controllers/Version4/AbstractObjectsController.php +++ b/src/Controllers/Version4/AbstractObjectsController.php @@ -13,6 +13,7 @@ defined( 'ABSPATH' ) || exit; * CRUD Object Controller. */ abstract class AbstractObjectsController extends AbstractController { + use BatchTrait; /** * If object is hierarchical. @@ -36,6 +37,35 @@ abstract class AbstractObjectsController extends AbstractController { */ abstract protected function get_object( $id ); + /** + * Prepares the object for the REST response. + * + * @since 3.0.0 + * @param \WC_Data $object Object data. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response Response object on success, or \WP_Error object on failure. + */ + abstract protected function prepare_object_for_response( $object, $request ); + + /** + * Prepares one object for create or update operation. + * + * @since 3.0.0 + * @param \WP_REST_Request $request Request object. + * @param bool $creating If is creating a new object. + * @return \WC_Data The prepared item, or \WP_Error object on failure. + */ + abstract protected function prepare_object_for_database( $request, $creating = false ); + + /** + * Register the routes for products. + */ + public function register_routes() { + $this->register_items_route(); + $this->register_item_route(); + $this->register_batch_route(); + } + /** * Check if a given request has access to read items. * @@ -127,36 +157,6 @@ abstract class AbstractObjectsController extends AbstractController { return true; } - /** - * Get object permalink. - * - * @param object $object Object. - * @return string - */ - protected function get_permalink( $object ) { - return ''; - } - - /** - * Prepares the object for the REST response. - * - * @since 3.0.0 - * @param \WC_Data $object Object data. - * @param \WP_REST_Request $request Request object. - * @return \WP_REST_Response Response object on success, or \WP_Error object on failure. - */ - abstract protected function prepare_object_for_response( $object, $request ); - - /** - * Prepares one object for create or update operation. - * - * @since 3.0.0 - * @param \WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return \WC_Data The prepared item, or \WP_Error object on failure. - */ - abstract protected function prepare_object_for_database( $request, $creating = false ); - /** * Get a single item. * @@ -814,4 +814,22 @@ abstract class AbstractObjectsController extends AbstractController { return $valid_vars; } + + /** + * Add meta query. + * + * @since 3.0.0 + * @param array $args Query args. + * @param array $meta_query Meta query. + * @return array + */ + protected function add_meta_query( $args, $meta_query ) { + if ( empty( $args['meta_query'] ) ) { + $args['meta_query'] = []; // phpcs:ignore + } + + $args['meta_query'][] = $meta_query; + + return $args['meta_query']; + } } diff --git a/src/Controllers/Version4/BatchTrait.php b/src/Controllers/Version4/BatchTrait.php new file mode 100644 index 00000000000..44125054470 --- /dev/null +++ b/src/Controllers/Version4/BatchTrait.php @@ -0,0 +1,263 @@ +get_params() ); + $limit = $this->check_batch_limit( $items ); + if ( is_wp_error( $limit ) ) { + return $limit; + } + + $response = []; + $batches = [ 'create', 'update', 'delete' ]; + foreach ( $batches as $batch ) { + if ( ! isset( $items[ $batch ] ) ) { + continue; + } + $items[ $batch ] = wp_parse_id_list( $items[ $batch ] ); + $response[ $batch ] = $this->{"batch_$batch"}( $items[ $batch ] ); + } + + return $response; + } + + /** + * Check if a given request has access batch create, update and delete items. + * + * @param \WP_REST_Request $request Full details about the request. + * @return boolean|\WP_Error + */ + abstract public function batch_items_permissions_check( $request ); + + /** + * Register route for batch requests. + */ + protected function register_batch_route() { + 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, 'batch_items_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), + ), + 'schema' => array( $this, 'get_public_batch_schema' ), + ), + true + ); + } + + /** + * Get the batch schema, conforming to JSON Schema. + * + * @return array + */ + protected function get_public_batch_schema() { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'batch', + 'type' => 'object', + 'properties' => array( + 'create' => array( + 'description' => __( 'List of created resources.', 'woocommerce' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + 'items' => array( + 'type' => 'object', + ), + ), + 'update' => array( + 'description' => __( 'List of updated resources.', 'woocommerce' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + 'items' => array( + 'type' => 'object', + ), + ), + 'delete' => array( + 'description' => __( 'List of delete resources.', 'woocommerce' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + 'items' => array( + 'type' => 'integer', + ), + ), + ), + ); + + return $schema; + } + + /** + * Get normalized rest base. + * + * @return string + */ + protected function get_normalized_rest_base() { + return preg_replace( '/\(.*\)\//i', '', $this->rest_base ); + } + + /** + * Check batch limit. + * + * @param array $items Request items. + * @return bool|\WP_Error + */ + protected function check_batch_limit( $items ) { + $limit = apply_filters( 'woocommerce_rest_batch_items_limit', 100, $this->get_normalized_rest_base() ); + $total = 0; + + $batches = [ 'create', 'update', 'delete' ]; + foreach ( $batches as $batch ) { + if ( ! isset( $items[ $batch ] ) ) { + continue; + } + $items[ $batch ] = wp_parse_id_list( $items[ $batch ] ); + $total = $total + count( $items[ $batch ] ); + } + + if ( $total > $limit ) { + /* translators: %s: items limit */ + return new \WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) ); + } + + return true; + } + + /** + * Get default params from schema. + * + * @return array + */ + protected function get_default_params() { + $defaults = []; + $schema = $this->get_public_item_schema(); + foreach ( $schema['properties'] as $arg => $options ) { + if ( isset( $options['default'] ) ) { + $defaults[ $arg ] = $options['default']; + } + } + return $defaults; + } + + /** + * Batch create items. + * + * @param array $items Array of item ids. + * @return array Response data. + */ + protected function batch_create( $items ) { + $response = []; + + foreach ( $items as $id ) { + $item = new \WP_REST_Request( 'POST' ); + $item->set_default_params( $this->get_default_params() ); + $item->set_body_params( $item ); + $item_response = $this->create_item( $item ); + $response[] = $this->format_response( $id, $item_response ); + } + + return $response; + } + + /** + * Batch update items. + * + * @param array $items Array of item ids. + * @return array Response data. + */ + protected function batch_update( $items ) { + $response = []; + + foreach ( $items as $id ) { + $item = new \WP_REST_Request( 'PUT' ); + $item->set_body_params( $item ); + $item_response = $this->update_item( $item ); + $response[] = $this->format_response( $id, $item_response ); + } + + return $response; + } + + /** + * Batch delete items. + * + * @param array $items Array of item ids. + * @return array Response data. + */ + protected function batch_delete( $items ) { + $response = []; + + foreach ( $items as $id ) { + $item = new \WP_REST_Request( 'DELETE' ); + $item->set_query_params( + [ + 'id' => $id, + 'force' => true, + ] + ); + $item_response = $this->delete_item( $item ); + $response[] = $this->format_response( $id, $item_response ); + } + + return $response; + } + + /** + * Format response data. + * + * @param int $id ID of item being updated. + * @param \WP_REST_Response|\WP_Error $response Response object. + * @return array + */ + protected function format_response( $id, $response ) { + /** + * REST Server + * + * @var \WP_REST_Server $wp_rest_server + */ + global $wp_rest_server; + + if ( is_wp_error( $response ) ) { + return $this->format_error_response( $response ); + } else { + return array( + 'id' => $id, + 'error' => $wp_rest_server->response_to_data( $response, '' ), + ); + } + } + + /** + * Format WP Error to response data. + * + * @param \WP_Error $error Error object. + * @return array + */ + protected function format_error_response( $error ) { + return array( + 'code' => $error->get_error_code(), + 'message' => $error->get_error_message(), + 'data' => $error->get_error_data(), + ); + } +} diff --git a/src/Controllers/Version4/Coupons.php b/src/Controllers/Version4/Coupons.php index a32d11bf31b..7bc765561a2 100644 --- a/src/Controllers/Version4/Coupons.php +++ b/src/Controllers/Version4/Coupons.php @@ -30,97 +30,6 @@ class Coupons extends AbstractObjectsController { */ protected $post_type = 'shop_coupon'; - /** - * Register the routes for coupons. - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - array( - 'methods' => \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => array_merge( - $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), - array( - 'code' => array( - 'description' => __( 'Coupon code.', 'woocommerce' ), - 'required' => true, - 'type' => 'string', - ), - ) - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)', - array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ), - ), - array( - 'methods' => \WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => \WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'default' => false, - 'type' => 'boolean', - 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - 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, 'batch_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_batch_schema' ), - ), - true - ); - } - /** * Get object. * @@ -334,6 +243,7 @@ class Coupons extends AbstractObjectsController { 'description' => __( 'Coupon code.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), + 'required' => true, ), 'amount' => array( 'description' => __( 'The amount of discount. Should always be numeric, even if setting a percentage.', 'woocommerce' ), diff --git a/src/Controllers/Version4/Orders.php b/src/Controllers/Version4/Orders.php index b145056f801..762741ffff0 100644 --- a/src/Controllers/Version4/Orders.php +++ b/src/Controllers/Version4/Orders.php @@ -44,88 +44,6 @@ class Orders extends AbstractObjectsController { */ protected $request = array(); - /** - * Register the routes for orders. - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - array( - 'methods' => \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)', - array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ), - ), - array( - 'methods' => \WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => \WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'default' => false, - 'type' => 'boolean', - 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - 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, 'batch_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_batch_schema' ), - ), - true - ); - } - /** * Get object. Return false if object is not of required type. * diff --git a/src/Controllers/Version4/PaymentGateways.php b/src/Controllers/Version4/PaymentGateways.php index ee073b1ea1a..773b499e3cb 100644 --- a/src/Controllers/Version4/PaymentGateways.php +++ b/src/Controllers/Version4/PaymentGateways.php @@ -15,6 +15,7 @@ defined( 'ABSPATH' ) || exit; * Payment gateways controller class. */ class PaymentGateways extends AbstractController { + use SettingsTrait; /** * Route base. diff --git a/src/Controllers/Version4/Products.php b/src/Controllers/Version4/Products.php index 26045c52312..96301be217b 100644 --- a/src/Controllers/Version4/Products.php +++ b/src/Controllers/Version4/Products.php @@ -44,92 +44,6 @@ class Products extends AbstractObjectsController { add_action( "woocommerce_rest_insert_{$this->post_type}_object", array( $this, 'clear_transients' ) ); } - /** - * Register the routes for products. - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - array( - 'methods' => \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)', - array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( - array( - 'default' => 'view', - ) - ), - ), - ), - array( - 'methods' => \WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => \WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'default' => false, - 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), - 'type' => 'boolean', - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - - 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, 'batch_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_batch_schema' ), - ), - true - ); - } - /** * Get object. * diff --git a/src/Controllers/Version4/SettingsOptions.php b/src/Controllers/Version4/SettingsOptions.php index 092ce3eda30..af2e525efe5 100644 --- a/src/Controllers/Version4/SettingsOptions.php +++ b/src/Controllers/Version4/SettingsOptions.php @@ -15,6 +15,7 @@ defined( 'ABSPATH' ) || exit; * REST API Setting Options controller class. */ class SettingsOptions extends AbstractController { + use SettingsTrait; /** * Route base. diff --git a/src/Controllers/Version4/SettingsTrait.php b/src/Controllers/Version4/SettingsTrait.php new file mode 100644 index 00000000000..a16763e5a43 --- /dev/null +++ b/src/Controllers/Version4/SettingsTrait.php @@ -0,0 +1,152 @@ + 400 ) ); + } + } + + /** + * Validate multiselect based settings. + * + * @since 3.0.0 + * @param array $values Values. + * @param array $setting Setting. + * @return array|\WP_Error + */ + public function validate_setting_multiselect_field( $values, $setting ) { + if ( empty( $values ) ) { + return array(); + } + + if ( ! is_array( $values ) ) { + return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); + } + + $final_values = array(); + foreach ( $values as $value ) { + if ( array_key_exists( $value, $setting['options'] ) ) { + $final_values[] = $value; + } + } + + return $final_values; + } + + /** + * Validate image_width based settings. + * + * @since 3.0.0 + * @param array $values Values. + * @param array $setting Setting. + * @return string|\WP_Error + */ + public function validate_setting_image_width_field( $values, $setting ) { + if ( ! is_array( $values ) ) { + return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); + } + + $current = $setting['value']; + if ( isset( $values['width'] ) ) { + $current['width'] = intval( $values['width'] ); + } + if ( isset( $values['height'] ) ) { + $current['height'] = intval( $values['height'] ); + } + if ( isset( $values['crop'] ) ) { + $current['crop'] = (bool) $values['crop']; + } + return $current; + } + + /** + * Validate radio based settings. + * + * @since 3.0.0 + * @param string $value Value. + * @param array $setting Setting. + * @return string|\WP_Error + */ + public function validate_setting_radio_field( $value, $setting ) { + return $this->validate_setting_select_field( $value, $setting ); + } + + /** + * Validate checkbox based settings. + * + * @since 3.0.0 + * @param string $value Value. + * @param array $setting Setting. + * @return string|\WP_Error + */ + public function validate_setting_checkbox_field( $value, $setting ) { + if ( in_array( $value, array( 'yes', 'no' ), true ) ) { + return $value; + } elseif ( empty( $value ) ) { + $value = isset( $setting['default'] ) ? $setting['default'] : 'no'; + return $value; + } else { + return new \WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); + } + } + + /** + * Validate textarea based settings. + * + * @since 3.0.0 + * @param string $value Value. + * @param array $setting Setting. + * @return string + */ + public function validate_setting_textarea_field( $value, $setting ) { + $value = is_null( $value ) ? '' : $value; + return wp_kses( + trim( stripslashes( $value ) ), + array_merge( + array( + 'iframe' => array( + 'src' => true, + 'style' => true, + 'id' => true, + 'class' => true, + ), + ), + wp_kses_allowed_html( 'post' ) + ) + ); + } +} diff --git a/src/Controllers/Version4/ShippingZoneMethods.php b/src/Controllers/Version4/ShippingZoneMethods.php index 717205ff2a8..92e063891f7 100644 --- a/src/Controllers/Version4/ShippingZoneMethods.php +++ b/src/Controllers/Version4/ShippingZoneMethods.php @@ -15,6 +15,7 @@ defined( 'ABSPATH' ) || exit; * REST API Shipping Zone Methods class. */ class ShippingZoneMethods extends AbstractShippingZonesController { + use SettingsTrait; /** * Register the routes for Shipping Zone Methods.