From f02e4e3088d732b070e3fa18e6ab6f8676aabdff Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 17 May 2018 11:44:46 +0100 Subject: [PATCH] Initial REST API v3 files --- .../api/class-wc-rest-coupons-controller.php | 521 +--- ...-wc-rest-customer-downloads-controller.php | 144 +- .../class-wc-rest-customers-controller.php | 343 +-- ...lass-wc-rest-network-orders-controller.php | 139 +- .../class-wc-rest-order-notes-controller.php | 161 +- ...class-wc-rest-order-refunds-controller.php | 562 +---- .../api/class-wc-rest-orders-controller.php | 1666 +------------ ...ss-wc-rest-payment-gateways-controller.php | 445 +--- ...est-product-attribute-terms-controller.php | 6 +- ...-wc-rest-product-attributes-controller.php | 6 +- ...-wc-rest-product-categories-controller.php | 191 +- ...ass-wc-rest-product-reviews-controller.php | 178 +- ...st-product-shipping-classes-controller.php | 6 +- .../class-wc-rest-product-tags-controller.php | 6 +- ...-wc-rest-product-variations-controller.php | 982 +------- .../api/class-wc-rest-products-controller.php | 2124 +---------------- .../class-wc-rest-report-sales-controller.php | 27 - ...-wc-rest-report-top-sellers-controller.php | 27 - .../api/class-wc-rest-reports-controller.php | 13 +- ...ass-wc-rest-setting-options-controller.php | 562 +---- .../api/class-wc-rest-settings-controller.php | 213 +- ...ss-wc-rest-shipping-methods-controller.php | 210 +- ...est-shipping-zone-locations-controller.php | 173 +- ...-rest-shipping-zone-methods-controller.php | 524 +--- ...lass-wc-rest-shipping-zones-controller.php | 287 +-- ...class-wc-rest-system-status-controller.php | 1046 +------- ...wc-rest-system-status-tools-controller.php | 536 +---- .../class-wc-rest-tax-classes-controller.php | 6 +- .../api/class-wc-rest-taxes-controller.php | 8 +- ...-wc-rest-webhook-deliveries-controller.php | 153 -- .../api/class-wc-rest-webhooks-controller.php | 164 +- 31 files changed, 104 insertions(+), 11325 deletions(-) delete mode 100644 includes/api/class-wc-rest-report-sales-controller.php delete mode 100644 includes/api/class-wc-rest-report-top-sellers-controller.php delete mode 100644 includes/api/class-wc-rest-webhook-deliveries-controller.php diff --git a/includes/api/class-wc-rest-coupons-controller.php b/includes/api/class-wc-rest-coupons-controller.php index 1b8c8aad6776..6fc216b11e92 100644 --- a/includes/api/class-wc-rest-coupons-controller.php +++ b/includes/api/class-wc-rest-coupons-controller.php @@ -14,529 +14,14 @@ * REST API Coupons controller class. * * @package WooCommerce/API - * @extends WC_REST_CRUD_Controller + * @extends WC_REST_Coupons_V2_Controller */ -class WC_REST_Coupons_Controller extends WC_REST_Legacy_Coupons_Controller { +class WC_REST_Coupons_Controller extends WC_REST_Coupons_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'coupons'; - - /** - * Post type. - * - * @var string - */ - 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' ), - ) - ); - - 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' ), - ) - ); - - 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' ), - ) - ); - } - - /** - * Get object. - * - * @since 3.0.0 - * @param int $id Object ID. - * @return WC_Data - */ - protected function get_object( $id ) { - return new WC_Coupon( $id ); - } - - /** - * Get formatted item data. - * - * @since 3.0.0 - * @param WC_Data $object WC_Data instance. - * @return array - */ - protected function get_formatted_item_data( $object ) { - $data = $object->get_data(); - - $format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' ); - $format_date = array( 'date_created', 'date_modified', 'date_expires' ); - $format_null = array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items' ); - - // Format decimal values. - foreach ( $format_decimal as $key ) { - $data[ $key ] = wc_format_decimal( $data[ $key ], 2 ); - } - - // Format date values. - foreach ( $format_date as $key ) { - $datetime = $data[ $key ]; - $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); - $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); - } - - // Format null values. - foreach ( $format_null as $key ) { - $data[ $key ] = $data[ $key ] ? $data[ $key ] : null; - } - - return array( - 'id' => $object->get_id(), - 'code' => $data['code'], - 'amount' => $data['amount'], - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'discount_type' => $data['discount_type'], - 'description' => $data['description'], - 'date_expires' => $data['date_expires'], - 'date_expires_gmt' => $data['date_expires_gmt'], - 'usage_count' => $data['usage_count'], - 'individual_use' => $data['individual_use'], - 'product_ids' => $data['product_ids'], - 'excluded_product_ids' => $data['excluded_product_ids'], - 'usage_limit' => $data['usage_limit'], - 'usage_limit_per_user' => $data['usage_limit_per_user'], - 'limit_usage_to_x_items' => $data['limit_usage_to_x_items'], - 'free_shipping' => $data['free_shipping'], - 'product_categories' => $data['product_categories'], - 'excluded_product_categories' => $data['excluded_product_categories'], - 'exclude_sale_items' => $data['exclude_sale_items'], - 'minimum_amount' => $data['minimum_amount'], - 'maximum_amount' => $data['maximum_amount'], - 'email_restrictions' => $data['email_restrictions'], - 'used_by' => $data['used_by'], - 'meta_data' => $data['meta_data'], - ); - } - - /** - * Prepare a single coupon output for response. - * - * @since 3.0.0 - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_object_for_response( $object, $request ) { - $data = $this->get_formatted_item_data( $object ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $object, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, - * refers to object type being prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - $args = parent::prepare_objects_query( $request ); - - if ( ! empty( $request['code'] ) ) { - $id = wc_get_coupon_id_by_code( $request['code'] ); - $args['post__in'] = array( $id ); - } - - // Get only ids. - $args['fields'] = 'ids'; - - return $args; - } - - /** - * Only return writable props from schema. - * - * @param array $schema Schema. - * @return bool - */ - protected function filter_writable_props( $schema ) { - return empty( $schema['readonly'] ); - } - - /** - * Prepare a single coupon for create or update. - * - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return WP_Error|WC_Data - */ - protected function prepare_object_for_database( $request, $creating = false ) { - $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; - $coupon = new WC_Coupon( $id ); - $schema = $this->get_item_schema(); - $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); - - // Validate required POST fields. - if ( $creating && empty( $request['code'] ) ) { - return new WP_Error( 'woocommerce_rest_empty_coupon_code', sprintf( __( 'The coupon code cannot be empty.', 'woocommerce' ), 'code' ), array( 'status' => 400 ) ); - } - - // Handle all writable props. - foreach ( $data_keys as $key ) { - $value = $request[ $key ]; - - if ( ! is_null( $value ) ) { - switch ( $key ) { - case 'code': - $coupon_code = wc_format_coupon_code( $value ); - $id = $coupon->get_id() ? $coupon->get_id() : 0; - $id_from_code = wc_get_coupon_id_by_code( $coupon_code, $id ); - - if ( $id_from_code ) { - return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $coupon->set_code( $coupon_code ); - break; - case 'meta_data': - if ( is_array( $value ) ) { - foreach ( $value as $meta ) { - $coupon->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - break; - case 'description': - $coupon->set_description( wp_filter_post_kses( $value ) ); - break; - default: - if ( is_callable( array( $coupon, "set_{$key}" ) ) ) { - $coupon->{"set_{$key}"}( $value ); - } - break; - } - } - } - - /** - * Filters an object before it is inserted via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, - * refers to the object type slug. - * - * @param WC_Data $coupon Object object. - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - */ - return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $coupon, $request, $creating ); - } - - /** - * Get the Coupon's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the object.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'code' => array( - 'description' => __( 'Coupon code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'amount' => array( - 'description' => __( 'The amount of discount. Should always be numeric, even if setting a percentage.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the coupon was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the coupon was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the coupon was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the coupon was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'discount_type' => array( - 'description' => __( 'Determines the type of discount that will be applied.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'fixed_cart', - 'enum' => array_keys( wc_get_coupon_types() ), - 'context' => array( 'view', 'edit' ), - ), - 'description' => array( - 'description' => __( 'Coupon description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_expires' => array( - 'description' => __( "The date the coupon expires, in the site's timezone.", 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_expires_gmt' => array( - 'description' => __( 'The date the coupon expires, as GMT.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'usage_count' => array( - 'description' => __( 'Number of times the coupon has been used already.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'individual_use' => array( - 'description' => __( 'If true, the coupon can only be used individually. Other applied coupons will be removed from the cart.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'product_ids' => array( - 'description' => __( 'List of product IDs the coupon can be used on.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'excluded_product_ids' => array( - 'description' => __( 'List of product IDs the coupon cannot be used on.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'usage_limit' => array( - 'description' => __( 'How many times the coupon can be used in total.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'usage_limit_per_user' => array( - 'description' => __( 'How many times the coupon can be used per customer.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'limit_usage_to_x_items' => array( - 'description' => __( 'Max number of items in the cart the coupon can be applied to.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'free_shipping' => array( - 'description' => __( 'If true and if the free shipping method requires a coupon, this coupon will enable free shipping.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'product_categories' => array( - 'description' => __( 'List of category IDs the coupon applies to.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'excluded_product_categories' => array( - 'description' => __( 'List of category IDs the coupon does not apply to.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'exclude_sale_items' => array( - 'description' => __( 'If true, this coupon will not be applied to items that have sale prices.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'minimum_amount' => array( - 'description' => __( 'Minimum order amount that needs to be in the cart before coupon applies.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'maximum_amount' => array( - 'description' => __( 'Maximum order amount allowed when using the coupon.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'email_restrictions' => array( - 'description' => __( 'List of email addresses that can use this coupon.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'string', - ), - 'context' => array( 'view', 'edit' ), - ), - 'used_by' => array( - 'description' => __( 'List of user IDs (or guest email addresses) that have used the coupon.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ); - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get the query params for collections of attachments. - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - $params['code'] = array( - 'description' => __( 'Limit result set to resources with a specific code.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - - return $params; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-customer-downloads-controller.php b/includes/api/class-wc-rest-customer-downloads-controller.php index 3fd52f3827f5..3fd89d4a4fb9 100644 --- a/includes/api/class-wc-rest-customer-downloads-controller.php +++ b/includes/api/class-wc-rest-customer-downloads-controller.php @@ -14,152 +14,14 @@ * REST API Customers controller class. * * @package WooCommerce/API - * @extends WC_REST_Customer_Downloads_V1_Controller + * @extends WC_REST_Customer_Downloads_V2_Controller */ -class WC_REST_Customer_Downloads_Controller extends WC_REST_Customer_Downloads_V1_Controller { +class WC_REST_Customer_Downloads_Controller extends WC_REST_Customer_Downloads_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Prepare a single download output for response. - * - * @param stdClass $download Download object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $download, $request ) { - $data = array( - 'download_id' => $download->download_id, - 'download_url' => $download->download_url, - 'product_id' => $download->product_id, - 'product_name' => $download->product_name, - 'download_name' => $download->download_name, - 'order_id' => $download->order_id, - 'order_key' => $download->order_key, - 'downloads_remaining' => '' === $download->downloads_remaining ? 'unlimited' : $download->downloads_remaining, - 'access_expires' => $download->access_expires ? wc_rest_prepare_date_response( $download->access_expires ) : 'never', - 'access_expires_gmt' => $download->access_expires ? wc_rest_prepare_date_response( get_gmt_from_date( $download->access_expires ) ) : 'never', - 'file' => $download->file, - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $download, $request ) ); - - /** - * Filter customer download data returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param stdClass $download Download object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_customer_download', $response, $download, $request ); - } - - /** - * Get the Customer Download's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'customer_download', - 'type' => 'object', - 'properties' => array( - 'download_id' => array( - 'description' => __( 'Download ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'download_url' => array( - 'description' => __( 'Download file URL.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'product_id' => array( - 'description' => __( 'Downloadable product ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'product_name' => array( - 'description' => __( 'Product name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'download_name' => array( - 'description' => __( 'Downloadable file name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'order_id' => array( - 'description' => __( 'Order ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'order_key' => array( - 'description' => __( 'Order key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'downloads_remaining' => array( - 'description' => __( 'Number of downloads remaining.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'access_expires' => array( - 'description' => __( "The date when download access expires, in the site's timezone.", 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'access_expires_gmt' => array( - 'description' => __( 'The date when download access expires, as GMT.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'file' => array( - 'description' => __( 'File details.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'name' => array( - 'description' => __( 'File name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'file' => array( - 'description' => __( 'File URL.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-customers-controller.php b/includes/api/class-wc-rest-customers-controller.php index 33e4f098a4ee..2bdacd95ba09 100644 --- a/includes/api/class-wc-rest-customers-controller.php +++ b/includes/api/class-wc-rest-customers-controller.php @@ -14,351 +14,14 @@ * REST API Customers controller class. * * @package WooCommerce/API - * @extends WC_REST_Customers_V1_Controller + * @extends WC_REST_Customers_V2_Controller */ -class WC_REST_Customers_Controller extends WC_REST_Customers_V1_Controller { +class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Get formatted item data. - * - * @since 3.0.0 - * @param WC_Data $object WC_Data instance. - * @return array - */ - protected function get_formatted_item_data( $object ) { - $data = $object->get_data(); - $format_date = array( 'date_created', 'date_modified' ); - - // Format date values. - foreach ( $format_date as $key ) { - $datetime = $data[ $key ]; - $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); - $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); - } - - return array( - 'id' => $object->get_id(), - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'email' => $data['email'], - 'first_name' => $data['first_name'], - 'last_name' => $data['last_name'], - 'role' => $data['role'], - 'username' => $data['username'], - 'billing' => $data['billing'], - 'shipping' => $data['shipping'], - 'is_paying_customer' => $data['is_paying_customer'], - 'orders_count' => $object->get_order_count(), - 'total_spent' => $object->get_total_spent(), - 'avatar_url' => $object->get_avatar_url(), - 'meta_data' => $data['meta_data'], - ); - } - - /** - * Prepare a single customer output for response. - * - * @param WP_User $user_data User object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $user_data, $request ) { - $customer = new WC_Customer( $user_data->ID ); - $data = $this->get_formatted_item_data( $customer ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $user_data ) ); - - /** - * Filter customer data returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WP_User $user_data User object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_customer', $response, $user_data, $request ); - } - - /** - * Update customer meta fields. - * - * @param WC_Customer $customer Customer data. - * @param WP_REST_Request $request Request data. - */ - protected function update_customer_meta_fields( $customer, $request ) { - parent::update_customer_meta_fields( $customer, $request ); - - // Meta data. - if ( isset( $request['meta_data'] ) ) { - if ( is_array( $request['meta_data'] ) ) { - foreach ( $request['meta_data'] as $meta ) { - $customer->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - } - } - - /** - * Get the Customer's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'customer', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the order was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the customer was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the customer was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'email' => array( - 'description' => __( 'The email address for the customer.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'email', - 'context' => array( 'view', 'edit' ), - ), - 'first_name' => array( - 'description' => __( 'Customer first name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'last_name' => array( - 'description' => __( 'Customer last name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'role' => array( - 'description' => __( 'Customer role.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'username' => array( - 'description' => __( 'Customer login name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_user', - ), - ), - 'password' => array( - 'description' => __( 'Customer password.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'edit' ), - ), - 'billing' => array( - 'description' => __( 'List of billing address data.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'first_name' => array( - 'description' => __( 'First name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'last_name' => array( - 'description' => __( 'Last name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'company' => array( - 'description' => __( 'Company name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_1' => array( - 'description' => __( 'Address line 1', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_2' => array( - 'description' => __( 'Address line 2', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'city' => array( - 'description' => __( 'City name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'state' => array( - 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'postcode' => array( - 'description' => __( 'Postal code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'country' => array( - 'description' => __( 'ISO code of the country.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'email' => array( - 'description' => __( 'Email address.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'email', - 'context' => array( 'view', 'edit' ), - ), - 'phone' => array( - 'description' => __( 'Phone number.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'shipping' => array( - 'description' => __( 'List of shipping address data.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'first_name' => array( - 'description' => __( 'First name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'last_name' => array( - 'description' => __( 'Last name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'company' => array( - 'description' => __( 'Company name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_1' => array( - 'description' => __( 'Address line 1', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_2' => array( - 'description' => __( 'Address line 2', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'city' => array( - 'description' => __( 'City name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'state' => array( - 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'postcode' => array( - 'description' => __( 'Postal code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'country' => array( - 'description' => __( 'ISO code of the country.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'is_paying_customer' => array( - 'description' => __( 'Is the customer a paying customer?', 'woocommerce' ), - 'type' => 'bool', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'orders_count' => array( - 'description' => __( 'Quantity of orders made by the customer.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total_spent' => array( - 'description' => __( 'Total amount spent.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'avatar_url' => array( - 'description' => __( 'Avatar URL.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-network-orders-controller.php b/includes/api/class-wc-rest-network-orders-controller.php index 00c7ed2366ac..070f95b05559 100644 --- a/includes/api/class-wc-rest-network-orders-controller.php +++ b/includes/api/class-wc-rest-network-orders-controller.php @@ -4,8 +4,8 @@ * * Handles requests to the /orders/network endpoint * - * @package WooCommerce/API - * @since 3.4.0 + * @package WooCommerce/API + * @since 3.4.0 */ defined( 'ABSPATH' ) || exit; @@ -14,139 +14,14 @@ * REST API Network Orders controller class. * * @package WooCommerce/API - * @extends WC_REST_Orders_Controller + * @extends WC_REST_Network_Orders_V2_Controller */ -class WC_REST_Network_Orders_Controller extends WC_REST_Orders_Controller { +class WC_REST_Network_Orders_Controller extends WC_REST_Network_Orders_V2_Controller { /** - * Register the routes for network orders. - */ - public function register_routes() { - if ( is_multisite() ) { - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/network', array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'network_orders' ), - 'permission_callback' => array( $this, 'network_orders_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - } - - /** - * Retrieves the item's schema for display / public consumption purposes. - * - * @access public + * Endpoint namespace. * - * @return array Public item schema data. + * @var string */ - public function get_public_item_schema() { - $schema = parent::get_public_item_schema(); - - $schema['properties']['blog'] = array( - 'description' => __( 'Blog id of the record on the multisite.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ); - $schema['properties']['edit_url'] = array( - 'description' => __( 'URL to edit the order', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ); - $schema['properties']['customer'][] = array( - 'description' => __( 'Name of the customer for the order', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ); - $schema['properties']['status_name'][] = array( - 'description' => __( 'Order Status', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ); - $schema['properties']['formatted_total'][] = array( - 'description' => __( 'Order total formatted for locale', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ); - - return $schema; - } - - /** - * Does a permissions check for the proper requested blog - * - * @param WP_REST_Request $request Full details about the request. - * - * @return bool $permission - */ - public function network_orders_permissions_check( $request ) { - $blog_id = $request->get_param( 'blog_id' ); - $blog_id = ! empty( $blog_id ) ? $blog_id : get_current_blog_id(); - - switch_to_blog( $blog_id ); - - $permission = $this->get_items_permissions_check( $request ); - - restore_current_blog(); - - return $permission; - } - - /** - * Get a collection of orders from the requested blog id - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_REST_Response - */ - public function network_orders( $request ) { - $blog_id = $request->get_param( 'blog_id' ); - $blog_id = ! empty( $blog_id ) ? $blog_id : get_current_blog_id(); - - switch_to_blog( $blog_id ); - - add_filter( 'woocommerce_rest_orders_prepare_object_query', array( $this, 'network_orders_filter_args' ) ); - $items = $this->get_items( $request ); - remove_filter( 'woocommerce_rest_orders_prepare_object_query', array( $this, 'network_orders_filter_args' ) ); - - foreach ( $items->data as &$current_order ) { - $order = wc_get_order( $current_order['id'] ); - - $current_order['blog'] = get_blog_details( get_current_blog_id() ); - $current_order['edit_url'] = get_admin_url( $blog_id, 'post.php?post=' . absint( $order->get_id() ) . '&action=edit' ); - /* translators: 1: first name 2: last name */ - $current_order['customer'] = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $order->get_billing_first_name(), $order->get_billing_last_name() ) ); - $current_order['status_name'] = wc_get_order_status_name( $order->get_status() ); - $current_order['formatted_total'] = $order->get_formatted_order_total(); - } - - restore_current_blog(); - - return $items; - } - - /** - * Filters the post statuses to on hold and processing for the network order query. - * - * @param array $args Query args. - * - * @return array - */ - public function network_orders_filter_args( $args ) { - $args['post_status'] = array( - 'wc-on-hold', - 'wc-processing', - ); - - return $args; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-order-notes-controller.php b/includes/api/class-wc-rest-order-notes-controller.php index 870c4e17f196..035b64fce3f7 100644 --- a/includes/api/class-wc-rest-order-notes-controller.php +++ b/includes/api/class-wc-rest-order-notes-controller.php @@ -14,169 +14,14 @@ * REST API Order Notes controller class. * * @package WooCommerce/API - * @extends WC_REST_ControllerWC_REST_Order_Notes_V1_Controller + * @extends WC_REST_Order_Notes_V2_Controller */ -class WC_REST_Order_Notes_Controller extends WC_REST_Order_Notes_V1_Controller { +class WC_REST_Order_Notes_Controller extends WC_REST_Order_Notes_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Get order notes from an order. - * - * @param WP_REST_Request $request Request data. - * - * @return array|WP_Error - */ - public function get_items( $request ) { - $order = wc_get_order( (int) $request['order_id'] ); - - if ( ! $order || $this->post_type !== $order->get_type() ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid order ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $args = array( - 'post_id' => $order->get_id(), - 'approve' => 'approve', - 'type' => 'order_note', - ); - - // Allow filter by order note type. - if ( 'customer' === $request['type'] ) { - $args['meta_query'] = array( // WPCS: slow query ok. - array( - 'key' => 'is_customer_note', - 'value' => 1, - 'compare' => '=', - ), - ); - } elseif ( 'internal' === $request['type'] ) { - $args['meta_query'] = array( // WPCS: slow query ok. - array( - 'key' => 'is_customer_note', - 'compare' => 'NOT EXISTS', - ), - ); - } - - remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 ); - - $notes = get_comments( $args ); - - add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 ); - - $data = array(); - foreach ( $notes as $note ) { - $order_note = $this->prepare_item_for_response( $note, $request ); - $order_note = $this->prepare_response_for_collection( $order_note ); - $data[] = $order_note; - } - - return rest_ensure_response( $data ); - } - - /** - * Prepare a single order note output for response. - * - * @param WP_Comment $note Order note object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $note, $request ) { - $data = array( - 'id' => (int) $note->comment_ID, - 'date_created' => wc_rest_prepare_date_response( $note->comment_date ), - 'date_created_gmt' => wc_rest_prepare_date_response( $note->comment_date_gmt ), - 'note' => $note->comment_content, - 'customer_note' => (bool) get_comment_meta( $note->comment_ID, 'is_customer_note', true ), - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $note ) ); - - /** - * Filter order note object returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WP_Comment $note Order note object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_order_note', $response, $note, $request ); - } - - /** - * Get the Order Notes schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'order_note', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the order note was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the order note was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'note' => array( - 'description' => __( 'Order note content.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'customer_note' => array( - 'description' => __( 'If true, the note will be shown to customers and they will be notified. If false, the note will be for admin reference only.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get the query params for collections. - * - * @return array - */ - public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['type'] = array( - 'default' => 'any', - 'description' => __( 'Limit result to customers or internal notes.', 'woocommerce' ), - 'type' => 'string', - 'enum' => array( 'any', 'customer', 'internal' ), - 'sanitize_callback' => 'sanitize_key', - 'validate_callback' => 'rest_validate_request_arg', - ); - - return $params; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-order-refunds-controller.php b/includes/api/class-wc-rest-order-refunds-controller.php index 1df6b9360870..29546886019f 100644 --- a/includes/api/class-wc-rest-order-refunds-controller.php +++ b/includes/api/class-wc-rest-order-refunds-controller.php @@ -14,570 +14,14 @@ * REST API Order Refunds controller class. * * @package WooCommerce/API - * @extends WC_REST_Orders_Controller + * @extends WC_REST_Order_Refunds_V2_Controller */ -class WC_REST_Order_Refunds_Controller extends WC_REST_Orders_Controller { +class WC_REST_Order_Refunds_Controller extends WC_REST_Order_Refunds_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'orders/(?P[\d]+)/refunds'; - - /** - * Post type. - * - * @var string - */ - protected $post_type = 'shop_order_refund'; - - /** - * Stores the request. - * - * @var array - */ - protected $request = array(); - - /** - * Order refunds actions. - */ - public function __construct() { - add_filter( "woocommerce_rest_{$this->post_type}_object_trashable", '__return_false' ); - } - - /** - * Register the routes for order refunds. - */ - public function register_routes() { - register_rest_route( - $this->namespace, '/' . $this->rest_base, array( - 'args' => array( - 'order_id' => array( - 'description' => __( 'The order ID.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - 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' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( - 'args' => array( - 'order_id' => array( - 'description' => __( 'The order ID.', 'woocommerce' ), - 'type' => 'integer', - ), - '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::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'default' => true, - 'type' => 'boolean', - 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ), - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Get object. - * - * @since 3.0.0 - * @param int $id Object ID. - * @return WC_Data - */ - protected function get_object( $id ) { - return wc_get_order( $id ); - } - - /** - * Get formatted item data. - * - * @since 3.0.0 - * @param WC_Data $object WC_Data instance. - * @return array - */ - protected function get_formatted_item_data( $object ) { - $data = $object->get_data(); - $format_decimal = array( 'amount' ); - $format_date = array( 'date_created' ); - $format_line_items = array( 'line_items' ); - - // Format decimal values. - foreach ( $format_decimal as $key ) { - $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); - } - - // Format date values. - foreach ( $format_date as $key ) { - $datetime = $data[ $key ]; - $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); - $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); - } - - // Format line items. - foreach ( $format_line_items as $key ) { - $data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) ); - } - - return array( - 'id' => $object->get_id(), - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'amount' => $data['amount'], - 'reason' => $data['reason'], - 'refunded_by' => $data['refunded_by'], - 'refunded_payment' => $data['refunded_payment'], - 'meta_data' => $data['meta_data'], - 'line_items' => $data['line_items'], - ); - } - - /** - * Prepare a single order output for response. - * - * @since 3.0.0 - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * - * @return WP_Error|WP_REST_Response - */ - public function prepare_object_for_response( $object, $request ) { - $this->request = $request; - $this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] ); - $order = wc_get_order( (int) $request['order_id'] ); - - if ( ! $order ) { - return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); - } - - if ( ! $object || $object->get_parent_id() !== $order->get_id() ) { - return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 ); - } - - $data = $this->get_formatted_item_data( $object ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $object, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, - * refers to object type being prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); - } - - /** - * Prepare links for the request. - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $object, $request ) { - $base = str_replace( '(?P[\d]+)', $object->get_parent_id(), $this->rest_base ); - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ), - ), - 'up' => array( - 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object->get_parent_id() ) ), - ), - ); - - return $links; - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - $args = parent::prepare_objects_query( $request ); - - $args['post_status'] = array_keys( wc_get_order_statuses() ); - $args['post_parent__in'] = array( absint( $request['order_id'] ) ); - - return $args; - } - - /** - * 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 WP_Error|WC_Data The prepared item, or WP_Error object on failure. - */ - protected function prepare_object_for_database( $request, $creating = false ) { - $order = wc_get_order( (int) $request['order_id'] ); - - if ( ! $order ) { - return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); - } - - if ( 0 > $request['amount'] ) { - return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 ); - } - - // Create the refund. - $refund = wc_create_refund( - array( - 'order_id' => $order->get_id(), - 'amount' => $request['amount'], - 'reason' => empty( $request['reason'] ) ? null : $request['reason'], - 'refund_payment' => is_bool( $request['api_refund'] ) ? $request['api_refund'] : true, - 'restock_items' => true, - ) - ); - - if ( is_wp_error( $refund ) ) { - return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', $refund->get_error_message(), 500 ); - } - - if ( ! $refund ) { - return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 ); - } - - if ( ! empty( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) { - foreach ( $request['meta_data'] as $meta ) { - $refund->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - $refund->save_meta_data(); - } - - /** - * Filters an object before it is inserted via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, - * refers to the object type slug. - * - * @param WC_Data $coupon Object object. - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - */ - return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $refund, $request, $creating ); - } - - /** - * Save an object data. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @param bool $creating If is creating a new object. - * @return WC_Data|WP_Error - */ - protected function save_object( $request, $creating = false ) { - try { - $object = $this->prepare_object_for_database( $request, $creating ); - - if ( is_wp_error( $object ) ) { - return $object; - } - - return $this->get_object( $object->get_id() ); - } catch ( WC_Data_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); - } catch ( WC_REST_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - } - - /** - * Get the Order's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the order refund was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the order refund was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'amount' => array( - 'description' => __( 'Refund amount.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'reason' => array( - 'description' => __( 'Reason for refund.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'refunded_by' => array( - 'description' => __( 'User ID of user who created the refund.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'refunded_payment' => array( - 'description' => __( 'If the payment was refunded via the API.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'line_items' => array( - 'description' => __( 'Line items data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Product name.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'product_id' => array( - 'description' => __( 'Product ID.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'variation_id' => array( - 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'quantity' => array( - 'description' => __( 'Quantity ordered.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'tax_class' => array( - 'description' => __( 'Tax class of product.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotal' => array( - 'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotal_tax' => array( - 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Line total (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total_tax' => array( - 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'taxes' => array( - 'description' => __( 'Line taxes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Tax rate ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Tax total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotal' => array( - 'description' => __( 'Tax subtotal.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'sku' => array( - 'description' => __( 'Product SKU.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'price' => array( - 'description' => __( 'Product price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'api_refund' => array( - 'description' => __( 'When true, the payment gateway API is used to generate the refund.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'edit' ), - 'default' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get the query params for collections. - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - unset( $params['status'], $params['customer'], $params['product'] ); - - return $params; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index bfafc26054cf..0075619ce40c 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -14,1674 +14,14 @@ * REST API Orders controller class. * * @package WooCommerce/API - * @extends WC_REST_CRUD_Controller + * @extends WC_REST_Orders_V2_Controller */ -class WC_REST_Orders_Controller extends WC_REST_Legacy_Orders_Controller { +class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'orders'; - - /** - * Post type. - * - * @var string - */ - protected $post_type = 'shop_order'; - - /** - * If object is hierarchical. - * - * @var bool - */ - protected $hierarchical = true; - - /** - * Stores the request. - * - * @var array - */ - 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' ), - ) - ); - - 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' ), - ) - ); - - 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' ), - ) - ); - } - - /** - * Get object. - * - * @since 3.0.0 - * @param int $id Object ID. - * @return WC_Data - */ - protected function get_object( $id ) { - return wc_get_order( $id ); - } - - /** - * Expands an order item to get its data. - * - * @param WC_Order_item $item Order item data. - * @return array - */ - protected function get_order_item_data( $item ) { - $data = $item->get_data(); - $format_decimal = array( 'subtotal', 'subtotal_tax', 'total', 'total_tax', 'tax_total', 'shipping_tax_total' ); - - // Format decimal values. - foreach ( $format_decimal as $key ) { - if ( isset( $data[ $key ] ) ) { - $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); - } - } - - // Add SKU and PRICE to products. - if ( is_callable( array( $item, 'get_product' ) ) ) { - $data['sku'] = $item->get_product() ? $item->get_product()->get_sku() : null; - $data['price'] = $item->get_quantity() ? $item->get_total() / $item->get_quantity() : 0; - } - - // Format taxes. - if ( ! empty( $data['taxes']['total'] ) ) { - $taxes = array(); - - foreach ( $data['taxes']['total'] as $tax_rate_id => $tax ) { - $taxes[] = array( - 'id' => $tax_rate_id, - 'total' => $tax, - 'subtotal' => isset( $data['taxes']['subtotal'][ $tax_rate_id ] ) ? $data['taxes']['subtotal'][ $tax_rate_id ] : '', - ); - } - $data['taxes'] = $taxes; - } elseif ( isset( $data['taxes'] ) ) { - $data['taxes'] = array(); - } - - // Remove names for coupons, taxes and shipping. - if ( isset( $data['code'] ) || isset( $data['rate_code'] ) || isset( $data['method_title'] ) ) { - unset( $data['name'] ); - } - - // Remove props we don't want to expose. - unset( $data['order_id'] ); - unset( $data['type'] ); - - return $data; - } - - /** - * Get formatted item data. - * - * @since 3.0.0 - * @param WC_Data $object WC_Data instance. - * @return array - */ - protected function get_formatted_item_data( $object ) { - $data = $object->get_data(); - $format_decimal = array( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'shipping_total', 'shipping_tax', 'cart_tax', 'total', 'total_tax' ); - $format_date = array( 'date_created', 'date_modified', 'date_completed', 'date_paid' ); - $format_line_items = array( 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines' ); - - // Format decimal values. - foreach ( $format_decimal as $key ) { - $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); - } - - // Format date values. - foreach ( $format_date as $key ) { - $datetime = $data[ $key ]; - $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); - $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); - } - - // Format the order status. - $data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status']; - - // Format line items. - foreach ( $format_line_items as $key ) { - $data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) ); - } - - // Refunds. - $data['refunds'] = array(); - foreach ( $object->get_refunds() as $refund ) { - $data['refunds'][] = array( - 'id' => $refund->get_id(), - 'reason' => $refund->get_reason() ? $refund->get_reason() : '', - 'total' => '-' . wc_format_decimal( $refund->get_amount(), $this->request['dp'] ), - ); - } - - return array( - 'id' => $object->get_id(), - 'parent_id' => $data['parent_id'], - 'number' => $data['number'], - 'order_key' => $data['order_key'], - 'created_via' => $data['created_via'], - 'version' => $data['version'], - 'status' => $data['status'], - 'currency' => $data['currency'], - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'discount_total' => $data['discount_total'], - 'discount_tax' => $data['discount_tax'], - 'shipping_total' => $data['shipping_total'], - 'shipping_tax' => $data['shipping_tax'], - 'cart_tax' => $data['cart_tax'], - 'total' => $data['total'], - 'total_tax' => $data['total_tax'], - 'prices_include_tax' => $data['prices_include_tax'], - 'customer_id' => $data['customer_id'], - 'customer_ip_address' => $data['customer_ip_address'], - 'customer_user_agent' => $data['customer_user_agent'], - 'customer_note' => $data['customer_note'], - 'billing' => $data['billing'], - 'shipping' => $data['shipping'], - 'payment_method' => $data['payment_method'], - 'payment_method_title' => $data['payment_method_title'], - 'transaction_id' => $data['transaction_id'], - 'date_paid' => $data['date_paid'], - 'date_paid_gmt' => $data['date_paid_gmt'], - 'date_completed' => $data['date_completed'], - 'date_completed_gmt' => $data['date_completed_gmt'], - 'cart_hash' => $data['cart_hash'], - 'meta_data' => $data['meta_data'], - 'line_items' => $data['line_items'], - 'tax_lines' => $data['tax_lines'], - 'shipping_lines' => $data['shipping_lines'], - 'fee_lines' => $data['fee_lines'], - 'coupon_lines' => $data['coupon_lines'], - 'refunds' => $data['refunds'], - ); - } - - /** - * Prepare a single order output for response. - * - * @since 3.0.0 - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_object_for_response( $object, $request ) { - $this->request = $request; - $this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] ); - $data = $this->get_formatted_item_data( $object ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $object, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, - * refers to object type being prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); - } - - /** - * Prepare links for the request. - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $object, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - if ( 0 !== (int) $object->get_customer_id() ) { - $links['customer'] = array( - 'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $object->get_customer_id() ) ), - ); - } - - if ( 0 !== (int) $object->get_parent_id() ) { - $links['up'] = array( - 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object->get_parent_id() ) ), - ); - } - - return $links; - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - global $wpdb; - - $args = parent::prepare_objects_query( $request ); - - // Set post_status. - if ( 'any' !== $request['status'] ) { - $args['post_status'] = 'wc-' . $request['status']; - } else { - $args['post_status'] = 'any'; - } - - if ( isset( $request['customer'] ) ) { - if ( ! empty( $args['meta_query'] ) ) { - $args['meta_query'] = array(); // WPCS: slow query ok. - } - - $args['meta_query'][] = array( - 'key' => '_customer_user', - 'value' => $request['customer'], - 'type' => 'NUMERIC', - ); - } - - // Search by product. - if ( ! empty( $request['product'] ) ) { - $order_ids = $wpdb->get_col( - $wpdb->prepare( - " - SELECT order_id - FROM {$wpdb->prefix}woocommerce_order_items - WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d ) - AND order_item_type = 'line_item' - ", $request['product'] - ) - ); - - // Force WP_Query return empty if don't found any order. - $order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 ); - - $args['post__in'] = $order_ids; - } - - // Search. - if ( ! empty( $args['s'] ) ) { - $order_ids = wc_order_search( $args['s'] ); - - if ( ! empty( $order_ids ) ) { - unset( $args['s'] ); - $args['post__in'] = array_merge( $order_ids, array( 0 ) ); - } - } - - /** - * Filter the query arguments for a request. - * - * Enables adding extra arguments or setting defaults for an order collection request. - * - * @param array $args Key value array of query var to query value. - * @param WP_REST_Request $request The request used. - */ - $args = apply_filters( 'woocommerce_rest_orders_prepare_object_query', $args, $request ); - - return $args; - } - - /** - * Only return writable props from schema. - * - * @param array $schema Schema. - * @return bool - */ - protected function filter_writable_props( $schema ) { - return empty( $schema['readonly'] ); - } - - /** - * Prepare a single order for create or update. - * - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return WP_Error|WC_Data - */ - protected function prepare_object_for_database( $request, $creating = false ) { - $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; - $order = new WC_Order( $id ); - $schema = $this->get_item_schema(); - $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); - - // Handle all writable props. - foreach ( $data_keys as $key ) { - $value = $request[ $key ]; - - if ( ! is_null( $value ) ) { - switch ( $key ) { - case 'status': - // Status change should be done later so transitions have new data. - break; - case 'billing': - case 'shipping': - $this->update_address( $order, $value, $key ); - break; - case 'line_items': - case 'shipping_lines': - case 'fee_lines': - case 'coupon_lines': - if ( is_array( $value ) ) { - foreach ( $value as $item ) { - if ( is_array( $item ) ) { - if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) { - $order->remove_item( $item['id'] ); - } else { - $this->set_item( $order, $key, $item ); - } - } - } - } - break; - case 'meta_data': - if ( is_array( $value ) ) { - foreach ( $value as $meta ) { - $order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - break; - default: - if ( is_callable( array( $order, "set_{$key}" ) ) ) { - $order->{"set_{$key}"}( $value ); - } - break; - } - } - } - - /** - * Filters an object before it is inserted via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, - * refers to the object type slug. - * - * @param WC_Data $order Object object. - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - */ - return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $order, $request, $creating ); - } - - /** - * Save an object data. - * - * @since 3.0.0 - * @throws WC_REST_Exception But all errors are validated before returning any data. - * @param WP_REST_Request $request Full details about the request. - * @param bool $creating If is creating a new object. - * @return WC_Data|WP_Error - */ - protected function save_object( $request, $creating = false ) { - try { - $object = $this->prepare_object_for_database( $request, $creating ); - - if ( is_wp_error( $object ) ) { - return $object; - } - - // Make sure gateways are loaded so hooks from gateways fire on save/create. - WC()->payment_gateways(); - - if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] ) { - // Make sure customer exists. - if ( false === get_user_by( 'id', $request['customer_id'] ) ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id', __( 'Customer ID is invalid.', 'woocommerce' ), 400 ); - } - - // Make sure customer is part of blog. - if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id_network', __( 'Customer ID does not belong to this site.', 'woocommerce' ), 400 ); - } - } - - if ( $creating ) { - $object->set_created_via( 'rest-api' ); - $object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) ); - $object->calculate_totals(); - } else { - // If items have changed, recalculate order totals. - if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) { - $object->calculate_totals(); - } - } - - // Set status. - if ( ! empty( $request['status'] ) ) { - $object->set_status( $request['status'] ); - } - - $object->save(); - - // Actions for after the order is saved. - if ( true === $request['set_paid'] ) { - if ( $creating || $object->needs_payment() ) { - $object->payment_complete( $request['transaction_id'] ); - } - } - - return $this->get_object( $object->get_id() ); - } catch ( WC_Data_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); - } catch ( WC_REST_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - } - - /** - * Update address. - * - * @param WC_Order $order Order data. - * @param array $posted Posted data. - * @param string $type Address type. - */ - protected function update_address( $order, $posted, $type = 'billing' ) { - foreach ( $posted as $key => $value ) { - if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) { - $order->{"set_{$type}_{$key}"}( $value ); - } - } - } - - /** - * Gets the product ID from the SKU or posted ID. - * - * @throws WC_REST_Exception When SKU or ID is not valid. - * @param array $posted Request data. - * @return int - */ - protected function get_product_id( $posted ) { - if ( ! empty( $posted['sku'] ) ) { - $product_id = (int) wc_get_product_id_by_sku( $posted['sku'] ); - } elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) { - $product_id = (int) $posted['product_id']; - } elseif ( ! empty( $posted['variation_id'] ) ) { - $product_id = (int) $posted['variation_id']; - } else { - throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 ); - } - return $product_id; - } - - /** - * Maybe set an item prop if the value was posted. - * - * @param WC_Order_Item $item Order item. - * @param string $prop Order property. - * @param array $posted Request data. - */ - protected function maybe_set_item_prop( $item, $prop, $posted ) { - if ( isset( $posted[ $prop ] ) ) { - $item->{"set_$prop"}( $posted[ $prop ] ); - } - } - - /** - * Maybe set item props if the values were posted. - * - * @param WC_Order_Item $item Order item data. - * @param string[] $props Properties. - * @param array $posted Request data. - */ - protected function maybe_set_item_props( $item, $props, $posted ) { - foreach ( $props as $prop ) { - $this->maybe_set_item_prop( $item, $prop, $posted ); - } - } - - /** - * Maybe set item meta if posted. - * - * @param WC_Order_Item $item Order item data. - * @param array $posted Request data. - */ - protected function maybe_set_item_meta_data( $item, $posted ) { - if ( ! empty( $posted['meta_data'] ) && is_array( $posted['meta_data'] ) ) { - foreach ( $posted['meta_data'] as $meta ) { - if ( isset( $meta['key'] ) ) { - $value = isset( $meta['value'] ) ? $meta['value'] : null; - $item->update_meta_data( $meta['key'], $value, isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - } - } - - /** - * Create or update a line item. - * - * @param array $posted Line item data. - * @param string $action 'create' to add line item or 'update' to update it. - * @param object $item Passed when updating an item. Null during creation. - * @return WC_Order_Item_Product - * @throws WC_REST_Exception Invalid data, server error. - */ - protected function prepare_line_items( $posted, $action = 'create', $item = null ) { - $item = is_null( $item ) ? new WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; - $product = wc_get_product( $this->get_product_id( $posted ) ); - - if ( $product !== $item->get_product() ) { - $item->set_product( $product ); - - if ( 'create' === $action ) { - $quantity = isset( $posted['quantity'] ) ? $posted['quantity'] : 1; - $total = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) ); - $item->set_total( $total ); - $item->set_subtotal( $total ); - } - } - - $this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted ); - $this->maybe_set_item_meta_data( $item, $posted ); - - return $item; - } - - /** - * Create or update an order shipping method. - * - * @param array $posted $shipping Item data. - * @param string $action 'create' to add shipping or 'update' to update it. - * @param object $item Passed when updating an item. Null during creation. - * @return WC_Order_Item_Shipping - * @throws WC_REST_Exception Invalid data, server error. - */ - protected function prepare_shipping_lines( $posted, $action = 'create', $item = null ) { - $item = is_null( $item ) ? new WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; - - if ( 'create' === $action ) { - if ( empty( $posted['method_id'] ) ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 ); - } - } - - $this->maybe_set_item_props( $item, array( 'method_id', 'method_title', 'total' ), $posted ); - $this->maybe_set_item_meta_data( $item, $posted ); - - return $item; - } - - /** - * Create or update an order fee. - * - * @param array $posted Item data. - * @param string $action 'create' to add fee or 'update' to update it. - * @param object $item Passed when updating an item. Null during creation. - * @return WC_Order_Item_Fee - * @throws WC_REST_Exception Invalid data, server error. - */ - protected function prepare_fee_lines( $posted, $action = 'create', $item = null ) { - $item = is_null( $item ) ? new WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; - - if ( 'create' === $action ) { - if ( empty( $posted['name'] ) ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 ); - } - } - - $this->maybe_set_item_props( $item, array( 'name', 'tax_class', 'tax_status', 'total' ), $posted ); - $this->maybe_set_item_meta_data( $item, $posted ); - - return $item; - } - - /** - * Create or update an order coupon. - * - * @param array $posted Item data. - * @param string $action 'create' to add coupon or 'update' to update it. - * @param object $item Passed when updating an item. Null during creation. - * @return WC_Order_Item_Coupon - * @throws WC_REST_Exception Invalid data, server error. - */ - protected function prepare_coupon_lines( $posted, $action = 'create', $item = null ) { - $item = is_null( $item ) ? new WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; - - if ( 'create' === $action ) { - if ( empty( $posted['code'] ) ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); - } - } - - $this->maybe_set_item_props( $item, array( 'code', 'discount' ), $posted ); - $this->maybe_set_item_meta_data( $item, $posted ); - - return $item; - } - - /** - * Wrapper method to create/update order items. - * When updating, the item ID provided is checked to ensure it is associated - * with the order. - * - * @param WC_Order $order order object. - * @param string $item_type The item type. - * @param array $posted item provided in the request body. - * @throws WC_REST_Exception If item ID is not associated with order. - */ - protected function set_item( $order, $item_type, $posted ) { - global $wpdb; - - if ( ! empty( $posted['id'] ) ) { - $action = 'update'; - } else { - $action = 'create'; - } - - $method = 'prepare_' . $item_type; - $item = null; - - // Verify provided line item ID is associated with order. - if ( 'update' === $action ) { - $item = $order->get_item( absint( $posted['id'] ), false ); - - if ( ! $item ) { - throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 ); - } - } - - // Prepare item data. - $item = $this->$method( $posted, $action, $item ); - - do_action( 'woocommerce_rest_set_order_item', $item, $posted ); - - // If creating the order, add the item to it. - if ( 'create' === $action ) { - $order->add_item( $item ); - } else { - $item->save(); - } - } - - /** - * Helper method to check if the resource ID associated with the provided item is null. - * Items can be deleted by setting the resource ID to null. - * - * @param array $item Item provided in the request body. - * @return bool True if the item resource ID is null, false otherwise. - */ - protected function item_is_null( $item ) { - $keys = array( 'product_id', 'method_id', 'method_title', 'name', 'code' ); - - foreach ( $keys as $key ) { - if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) { - return true; - } - } - - return false; - } - - /** - * Get order statuses without prefixes. - * - * @return array - */ - protected function get_order_statuses() { - $order_statuses = array(); - - foreach ( array_keys( wc_get_order_statuses() ) as $status ) { - $order_statuses[] = str_replace( 'wc-', '', $status ); - } - - return $order_statuses; - } - - /** - * Get the Order's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'parent_id' => array( - 'description' => __( 'Parent order ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'number' => array( - 'description' => __( 'Order number.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'order_key' => array( - 'description' => __( 'Order key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'created_via' => array( - 'description' => __( 'Shows where the order was created.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'version' => array( - 'description' => __( 'Version of WooCommerce which last updated the order.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'status' => array( - 'description' => __( 'Order status.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'pending', - 'enum' => $this->get_order_statuses(), - 'context' => array( 'view', 'edit' ), - ), - 'currency' => array( - 'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ), - 'type' => 'string', - 'default' => get_woocommerce_currency(), - 'enum' => array_keys( get_woocommerce_currencies() ), - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the order was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the order was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the order was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the order was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'discount_total' => array( - 'description' => __( 'Total discount amount for the order.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'discount_tax' => array( - 'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'shipping_total' => array( - 'description' => __( 'Total shipping amount for the order.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'shipping_tax' => array( - 'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'cart_tax' => array( - 'description' => __( 'Sum of line item taxes only.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Grand total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total_tax' => array( - 'description' => __( 'Sum of all taxes.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'prices_include_tax' => array( - 'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'customer_id' => array( - 'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 0, - 'context' => array( 'view', 'edit' ), - ), - 'customer_ip_address' => array( - 'description' => __( "Customer's IP address.", 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'customer_user_agent' => array( - 'description' => __( 'User agent of the customer.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'customer_note' => array( - 'description' => __( 'Note left by customer during checkout.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'billing' => array( - 'description' => __( 'Billing address.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'first_name' => array( - 'description' => __( 'First name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'last_name' => array( - 'description' => __( 'Last name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'company' => array( - 'description' => __( 'Company name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_1' => array( - 'description' => __( 'Address line 1', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_2' => array( - 'description' => __( 'Address line 2', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'city' => array( - 'description' => __( 'City name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'state' => array( - 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'postcode' => array( - 'description' => __( 'Postal code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'country' => array( - 'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'email' => array( - 'description' => __( 'Email address.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'email', - 'context' => array( 'view', 'edit' ), - ), - 'phone' => array( - 'description' => __( 'Phone number.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'shipping' => array( - 'description' => __( 'Shipping address.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'first_name' => array( - 'description' => __( 'First name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'last_name' => array( - 'description' => __( 'Last name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'company' => array( - 'description' => __( 'Company name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_1' => array( - 'description' => __( 'Address line 1', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'address_2' => array( - 'description' => __( 'Address line 2', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'city' => array( - 'description' => __( 'City name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'state' => array( - 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'postcode' => array( - 'description' => __( 'Postal code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'country' => array( - 'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'payment_method' => array( - 'description' => __( 'Payment method ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'payment_method_title' => array( - 'description' => __( 'Payment method title.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'transaction_id' => array( - 'description' => __( 'Unique transaction ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_paid' => array( - 'description' => __( "The date the order was paid, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_paid_gmt' => array( - 'description' => __( 'The date the order was paid, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_completed' => array( - 'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_completed_gmt' => array( - 'description' => __( 'The date the order was completed, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'cart_hash' => array( - 'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'line_items' => array( - 'description' => __( 'Line items data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Product name.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'product_id' => array( - 'description' => __( 'Product ID.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'variation_id' => array( - 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'quantity' => array( - 'description' => __( 'Quantity ordered.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'tax_class' => array( - 'description' => __( 'Tax class of product.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'subtotal' => array( - 'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'subtotal_tax' => array( - 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Line total (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'total_tax' => array( - 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'taxes' => array( - 'description' => __( 'Line taxes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Tax rate ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'total' => array( - 'description' => __( 'Tax total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'subtotal' => array( - 'description' => __( 'Tax subtotal.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'sku' => array( - 'description' => __( 'Product SKU.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'price' => array( - 'description' => __( 'Product price.', 'woocommerce' ), - 'type' => 'number', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'tax_lines' => array( - 'description' => __( 'Tax lines data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'rate_code' => array( - 'description' => __( 'Tax rate code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'rate_id' => array( - 'description' => __( 'Tax rate ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'label' => array( - 'description' => __( 'Tax rate label.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'compound' => array( - 'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'tax_total' => array( - 'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'shipping_tax_total' => array( - 'description' => __( 'Shipping tax total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ), - ), - 'shipping_lines' => array( - 'description' => __( 'Shipping lines data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'method_title' => array( - 'description' => __( 'Shipping method name.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'method_id' => array( - 'description' => __( 'Shipping method ID.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'total' => array( - 'description' => __( 'Line total (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'total_tax' => array( - 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'taxes' => array( - 'description' => __( 'Line taxes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Tax rate ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Tax total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ), - ), - 'fee_lines' => array( - 'description' => __( 'Fee lines data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Fee name.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'tax_class' => array( - 'description' => __( 'Tax class of fee.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'tax_status' => array( - 'description' => __( 'Tax status of fee.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'enum' => array( 'taxable', 'none' ), - ), - 'total' => array( - 'description' => __( 'Line total (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'total_tax' => array( - 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'taxes' => array( - 'description' => __( 'Line taxes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Tax rate ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Tax total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotal' => array( - 'description' => __( 'Tax subtotal.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ), - ), - 'coupon_lines' => array( - 'description' => __( 'Coupons line data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Item ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'code' => array( - 'description' => __( 'Coupon code.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - 'discount' => array( - 'description' => __( 'Discount total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'discount_tax' => array( - 'description' => __( 'Discount total tax.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ), - ), - 'refunds' => array( - 'description' => __( 'List of refunds.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Refund ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'reason' => array( - 'description' => __( 'Refund reason.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total' => array( - 'description' => __( 'Refund total.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'set_paid' => array( - 'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'edit' ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get the query params for collections. - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - $params['status'] = array( - 'default' => 'any', - 'description' => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ), - 'type' => 'string', - 'enum' => array_merge( array( 'any' ), $this->get_order_statuses() ), - 'sanitize_callback' => 'sanitize_key', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['customer'] = array( - 'description' => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['product'] = array( - 'description' => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['dp'] = array( - 'default' => wc_get_price_decimals(), - 'description' => __( 'Number of decimal points to use in each resource.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - - return $params; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-payment-gateways-controller.php b/includes/api/class-wc-rest-payment-gateways-controller.php index 89f6a692632d..17bec9c2901e 100644 --- a/includes/api/class-wc-rest-payment-gateways-controller.php +++ b/includes/api/class-wc-rest-payment-gateways-controller.php @@ -14,453 +14,14 @@ * Paymenga gateways controller class. * * @package WooCommerce/API - * @extends WC_REST_Controller + * @extends WC_REST_Payment_Gateways_V2_Controller */ -class WC_REST_Payment_Gateways_Controller extends WC_REST_Controller { +class WC_REST_Payment_Gateways_Controller extends WC_REST_Payment_Gateways_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'payment_gateways'; - - /** - * Register the route for /payment_gateways and /payment_gateways/ - */ - 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(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'string', - ), - ), - 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_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Check whether a given request has permission to view payment gateways. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'payment_gateways', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Check if a given request has access to read a payment gateway. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'payment_gateways', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Check whether a given request has permission to edit payment gateways. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'payment_gateways', 'edit' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Get payment gateways. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $payment_gateways = WC()->payment_gateways->payment_gateways(); - $response = array(); - foreach ( $payment_gateways as $payment_gateway_id => $payment_gateway ) { - $payment_gateway->id = $payment_gateway_id; - $gateway = $this->prepare_item_for_response( $payment_gateway, $request ); - $gateway = $this->prepare_response_for_collection( $gateway ); - $response[] = $gateway; - } - return rest_ensure_response( $response ); - } - - /** - * Get a single payment gateway. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $gateway = $this->get_gateway( $request ); - - if ( is_null( $gateway ) ) { - return new WP_Error( 'woocommerce_rest_payment_gateway_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $gateway = $this->prepare_item_for_response( $gateway, $request ); - return rest_ensure_response( $gateway ); - } - - /** - * Update A Single Payment Method. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function update_item( $request ) { - $gateway = $this->get_gateway( $request ); - - if ( is_null( $gateway ) ) { - return new WP_Error( 'woocommerce_rest_payment_gateway_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - // Get settings. - $gateway->init_form_fields(); - $settings = $gateway->settings; - - // Update settings. - if ( isset( $request['settings'] ) ) { - $errors_found = false; - foreach ( $gateway->form_fields as $key => $field ) { - if ( isset( $request['settings'][ $key ] ) ) { - if ( is_callable( array( $this, 'validate_setting_' . $field['type'] . '_field' ) ) ) { - $value = $this->{'validate_setting_' . $field['type'] . '_field'}( $request['settings'][ $key ], $field ); - } else { - $value = $this->validate_setting_text_field( $request['settings'][ $key ], $field ); - } - if ( is_wp_error( $value ) ) { - $errors_found = true; - break; - } - $settings[ $key ] = $value; - } - } - - if ( $errors_found ) { - return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - } - - // Update if this method is enabled or not. - if ( isset( $request['enabled'] ) ) { - $settings['enabled'] = wc_bool_to_string( $request['enabled'] ); - $gateway->enabled = $settings['enabled']; - } - - // Update title. - if ( isset( $request['title'] ) ) { - $settings['title'] = $request['title']; - $gateway->title = $settings['title']; - } - - // Update description. - if ( isset( $request['description'] ) ) { - $settings['description'] = $request['description']; - $gateway->description = $settings['description']; - } - - // Update options. - $gateway->settings = $settings; - update_option( $gateway->get_option_key(), apply_filters( 'woocommerce_gateway_' . $gateway->id . '_settings_values', $settings, $gateway ) ); - - // Update order. - if ( isset( $request['order'] ) ) { - $order = (array) get_option( 'woocommerce_gateway_order' ); - $order[ $gateway->id ] = $request['order']; - update_option( 'woocommerce_gateway_order', $order ); - $gateway->order = absint( $request['order'] ); - } - - $gateway = $this->prepare_item_for_response( $gateway, $request ); - return rest_ensure_response( $gateway ); - } - - /** - * Get a gateway based on the current request object. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|null - */ - public function get_gateway( $request ) { - $gateway = null; - $payment_gateways = WC()->payment_gateways->payment_gateways(); - foreach ( $payment_gateways as $payment_gateway_id => $payment_gateway ) { - if ( $request['id'] !== $payment_gateway_id ) { - continue; - } - $payment_gateway->id = $payment_gateway_id; - $gateway = $payment_gateway; - } - return $gateway; - } - - /** - * Prepare a payment gateway for response. - * - * @param WC_Payment_Gateway $gateway Payment gateway object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $gateway, $request ) { - $order = (array) get_option( 'woocommerce_gateway_order' ); - $item = array( - 'id' => $gateway->id, - 'title' => $gateway->title, - 'description' => $gateway->description, - 'order' => isset( $order[ $gateway->id ] ) ? $order[ $gateway->id ] : '', - 'enabled' => ( 'yes' === $gateway->enabled ), - 'method_title' => $gateway->get_method_title(), - 'method_description' => $gateway->get_method_description(), - 'settings' => $this->get_settings( $gateway ), - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $item, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $gateway, $request ) ); - - /** - * Filter payment gateway objects returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WC_Payment_Gateway $gateway Payment gateway object. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_payment_gateway', $response, $gateway, $request ); - } - - /** - * Return settings associated with this payment gateway. - * - * @param WC_Payment_Gateway $gateway Gateway data. - * - * @return array - */ - public function get_settings( $gateway ) { - $settings = array(); - $gateway->init_form_fields(); - foreach ( $gateway->form_fields as $id => $field ) { - // Make sure we at least have a title and type. - if ( empty( $field['title'] ) || empty( $field['type'] ) ) { - continue; - } - // Ignore 'title' settings/fields -- they are UI only. - if ( 'title' === $field['type'] ) { - continue; - } - // Ignore 'enabled' and 'description' which get included elsewhere. - if ( in_array( $id, array( 'enabled', 'description' ), true ) ) { - continue; - } - $data = array( - 'id' => $id, - 'label' => empty( $field['label'] ) ? $field['title'] : $field['label'], - 'description' => empty( $field['description'] ) ? '' : $field['description'], - 'type' => $field['type'], - 'value' => empty( $gateway->settings[ $id ] ) ? '' : $gateway->settings[ $id ], - 'default' => empty( $field['default'] ) ? '' : $field['default'], - 'tip' => empty( $field['description'] ) ? '' : $field['description'], - 'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'], - ); - if ( ! empty( $field['options'] ) ) { - $data['options'] = $field['options']; - } - $settings[ $id ] = $data; - } - return $settings; - } - - /** - * Prepare links for the request. - * - * @param WC_Payment_Gateway $gateway Payment gateway object. - * @param WP_REST_Request $request Request object. - * @return array - */ - protected function prepare_links( $gateway, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $gateway->id ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - return $links; - } - - /** - * Get the payment gateway schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'payment_gateway', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Payment gateway ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'title' => array( - 'description' => __( 'Payment gateway title on checkout.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'description' => array( - 'description' => __( 'Payment gateway description on checkout.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'order' => array( - 'description' => __( 'Payment gateway sort order.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'absint', - ), - ), - 'enabled' => array( - 'description' => __( 'Payment gateway enabled status.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - ), - 'method_title' => array( - 'description' => __( 'Payment gateway method title.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'method_description' => array( - 'description' => __( 'Payment gateway method description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'settings' => array( - 'description' => __( 'Payment gateway settings.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'id' => array( - 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'label' => array( - 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'type' => array( - 'description' => __( 'Type of setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), - 'readonly' => true, - ), - 'value' => array( - 'description' => __( 'Setting value.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'default' => array( - 'description' => __( 'Default value for the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'tip' => array( - 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'placeholder' => array( - 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get any query params needed. - * - * @return array - */ - public function get_collection_params() { - return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ); - } - + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-attribute-terms-controller.php b/includes/api/class-wc-rest-product-attribute-terms-controller.php index 6d844ec62ec7..3e652894d692 100644 --- a/includes/api/class-wc-rest-product-attribute-terms-controller.php +++ b/includes/api/class-wc-rest-product-attribute-terms-controller.php @@ -14,14 +14,14 @@ * REST API Product Attribute Terms controller class. * * @package WooCommerce/API - * @extends WC_REST_Product_Attribute_Terms_V1_Controller + * @extends WC_REST_Product_Attribute_Terms_V2_Controller */ -class WC_REST_Product_Attribute_Terms_Controller extends WC_REST_Product_Attribute_Terms_V1_Controller { +class WC_REST_Product_Attribute_Terms_Controller extends WC_REST_Product_Attribute_Terms_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-attributes-controller.php b/includes/api/class-wc-rest-product-attributes-controller.php index 2ab17365c22c..047e6e4e2576 100644 --- a/includes/api/class-wc-rest-product-attributes-controller.php +++ b/includes/api/class-wc-rest-product-attributes-controller.php @@ -14,14 +14,14 @@ * REST API Product Attributes controller class. * * @package WooCommerce/API - * @extends WC_REST_Product_Attributes_V1_Controller + * @extends WC_REST_Product_Attributes_V2_Controller */ -class WC_REST_Product_Attributes_Controller extends WC_REST_Product_Attributes_V1_Controller { +class WC_REST_Product_Attributes_Controller extends WC_REST_Product_Attributes_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-categories-controller.php b/includes/api/class-wc-rest-product-categories-controller.php index 5b1a5a5b8d1c..0142d20ed1f7 100644 --- a/includes/api/class-wc-rest-product-categories-controller.php +++ b/includes/api/class-wc-rest-product-categories-controller.php @@ -14,199 +14,14 @@ * REST API Product Categories controller class. * * @package WooCommerce/API - * @extends WC_REST_Product_Categories_V1_Controller + * @extends WC_REST_Product_Categories_V2_Controller */ -class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V1_Controller { +class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Prepare a single product category output for response. - * - * @param WP_Term $item Term object. - * @param WP_REST_Request $request Request instance. - * @return WP_REST_Response - */ - public function prepare_item_for_response( $item, $request ) { - // Get category display type. - $display_type = get_woocommerce_term_meta( $item->term_id, 'display_type' ); - - // Get category order. - $menu_order = get_woocommerce_term_meta( $item->term_id, 'order' ); - - $data = array( - 'id' => (int) $item->term_id, - 'name' => $item->name, - 'slug' => $item->slug, - 'parent' => (int) $item->parent, - 'description' => $item->description, - 'display' => $display_type ? $display_type : 'default', - 'image' => null, - 'menu_order' => (int) $menu_order, - 'count' => (int) $item->count, - ); - - // Get category image. - $image_id = get_woocommerce_term_meta( $item->term_id, 'thumbnail_id' ); - if ( $image_id ) { - $attachment = get_post( $image_id ); - - $data['image'] = array( - 'id' => (int) $image_id, - 'date_created' => wc_rest_prepare_date_response( $attachment->post_date ), - 'date_created_gmt' => wc_rest_prepare_date_response( $attachment->post_date_gmt ), - 'date_modified' => wc_rest_prepare_date_response( $attachment->post_modified ), - 'date_modified_gmt' => wc_rest_prepare_date_response( $attachment->post_modified_gmt ), - 'src' => wp_get_attachment_url( $image_id ), - 'title' => get_the_title( $attachment ), - 'alt' => get_post_meta( $image_id, '_wp_attachment_image_alt', true ), - ); - } - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $item, $request ) ); - - /** - * Filter a term item returned from the API. - * - * Allows modification of the term data right before it is returned. - * - * @param WP_REST_Response $response The response object. - * @param object $item The original term object. - * @param WP_REST_Request $request Request used to generate the response. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->taxonomy}", $response, $item, $request ); - } - - /** - * Get the Category schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->taxonomy, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Category name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'slug' => array( - 'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_title', - ), - ), - 'parent' => array( - 'description' => __( 'The ID for the parent of the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'description' => array( - 'description' => __( 'HTML description of the resource.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'wp_filter_post_kses', - ), - ), - 'display' => array( - 'description' => __( 'Category archive display type.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'default', - 'enum' => array( 'default', 'products', 'subcategories', 'both' ), - 'context' => array( 'view', 'edit' ), - ), - 'image' => array( - 'description' => __( 'Image data.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'id' => array( - 'description' => __( 'Image ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'src' => array( - 'description' => __( 'Image URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - ), - 'title' => array( - 'description' => __( 'Image name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'alt' => array( - 'description' => __( 'Image alternative text.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'menu_order' => array( - 'description' => __( 'Menu order, used to custom sort the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'count' => array( - 'description' => __( 'Number of published products for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-reviews-controller.php b/includes/api/class-wc-rest-product-reviews-controller.php index 47b2de99d4fd..8481d5eda084 100644 --- a/includes/api/class-wc-rest-product-reviews-controller.php +++ b/includes/api/class-wc-rest-product-reviews-controller.php @@ -14,186 +14,14 @@ * REST API Product Reviews Controller Class. * * @package WooCommerce/API - * @extends WC_REST_Product_Reviews_V1_Controller + * @extends WC_REST_Product_Reviews_V2_Controller */ -class WC_REST_Product_Reviews_Controller extends WC_REST_Product_Reviews_V1_Controller { +class WC_REST_Product_Reviews_Controller extends WC_REST_Product_Reviews_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'products/(?P[\d]+)/reviews'; - - /** - * Register the routes for product reviews. - */ - public function register_routes() { - parent::register_routes(); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/batch', array( - 'args' => array( - 'product_id' => array( - 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - 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' ), - ) - ); - } - - /** - * Check if a given request has access to batch manage product reviews. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function batch_items_permissions_check( $request ) { - if ( ! wc_rest_check_post_permissions( 'product', 'batch' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Prepare a single product review output for response. - * - * @param WP_Comment $review Product review object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $review, $request ) { - $data = array( - 'id' => (int) $review->comment_ID, - 'date_created' => wc_rest_prepare_date_response( $review->comment_date ), - 'date_created_gmt' => wc_rest_prepare_date_response( $review->comment_date_gmt ), - 'review' => $review->comment_content, - 'rating' => (int) get_comment_meta( $review->comment_ID, 'rating', true ), - 'name' => $review->comment_author, - 'email' => $review->comment_author_email, - 'verified' => wc_review_is_from_verified_owner( $review->comment_ID ), - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $review, $request ) ); - - /** - * Filter product reviews object returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WP_Comment $review Product review object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_product_review', $response, $review, $request ); - } - - - /** - * Bulk create, update and delete items. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array Of WP_Error or WP_REST_Response. - */ - public function batch_items( $request ) { - $items = array_filter( $request->get_params() ); - $params = $request->get_url_params(); - $product_id = $params['product_id']; - $body_params = array(); - - foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) { - if ( ! empty( $items[ $batch_type ] ) ) { - $injected_items = array(); - foreach ( $items[ $batch_type ] as $item ) { - $injected_items[] = is_array( $item ) ? array_merge( array( 'product_id' => $product_id ), $item ) : $item; - } - $body_params[ $batch_type ] = $injected_items; - } - } - - $request = new WP_REST_Request( $request->get_method() ); - $request->set_body_params( $body_params ); - - return parent::batch_items( $request ); - } - - /** - * Get the Product Review's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'product_review', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'review' => array( - 'description' => __( 'The content of the review.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the review was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the review was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'rating' => array( - 'description' => __( 'Review rating (0 to 5).', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Reviewer name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'email' => array( - 'description' => __( 'Reviewer email.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'verified' => array( - 'description' => __( 'Shows if the reviewer bought the product or not.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-shipping-classes-controller.php b/includes/api/class-wc-rest-product-shipping-classes-controller.php index daf3d1befc53..3a3aa7c04446 100644 --- a/includes/api/class-wc-rest-product-shipping-classes-controller.php +++ b/includes/api/class-wc-rest-product-shipping-classes-controller.php @@ -14,14 +14,14 @@ * REST API Product Shipping Classes controller class. * * @package WooCommerce/API - * @extends WC_REST_Product_Shipping_Classes_V1_Controller + * @extends WC_REST_Product_Shipping_Classes_V2_Controller */ -class WC_REST_Product_Shipping_Classes_Controller extends WC_REST_Product_Shipping_Classes_V1_Controller { +class WC_REST_Product_Shipping_Classes_Controller extends WC_REST_Product_Shipping_Classes_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-tags-controller.php b/includes/api/class-wc-rest-product-tags-controller.php index 1110ada41de3..121808721bec 100644 --- a/includes/api/class-wc-rest-product-tags-controller.php +++ b/includes/api/class-wc-rest-product-tags-controller.php @@ -14,14 +14,14 @@ * REST API Product Tags controller class. * * @package WooCommerce/API - * @extends WC_REST_Product_Tags_V1_Controller + * @extends WC_REST_Product_Tags_V2_Controller */ -class WC_REST_Product_Tags_Controller extends WC_REST_Product_Tags_V1_Controller { +class WC_REST_Product_Tags_Controller extends WC_REST_Product_Tags_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-product-variations-controller.php b/includes/api/class-wc-rest-product-variations-controller.php index 621bffd7e5ed..1334edbe2790 100644 --- a/includes/api/class-wc-rest-product-variations-controller.php +++ b/includes/api/class-wc-rest-product-variations-controller.php @@ -14,990 +14,14 @@ * REST API variations controller class. * * @package WooCommerce/API - * @extends WC_REST_Products_Controller + * @extends WC_REST_Product_Variations_V2_Controller */ -class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller { +class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'products/(?P[\d]+)/variations'; - - /** - * Post type. - * - * @var string - */ - protected $post_type = 'product_variation'; - - /** - * Initialize product actions (parent). - */ - public function __construct() { - add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'add_product_id' ), 9, 2 ); - parent::__construct(); - } - - /** - * Register the routes for products. - */ - public function register_routes() { - register_rest_route( - $this->namespace, '/' . $this->rest_base, array( - 'args' => array( - 'product_id' => array( - 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - 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' ), - ) - ); - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( - 'args' => array( - 'product_id' => array( - 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ), - 'type' => 'integer', - ), - 'id' => array( - 'description' => __( 'Unique identifier for the variation.', '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' ), - ) - ); - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/batch', array( - 'args' => array( - 'product_id' => array( - 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - 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' ), - ) - ); - } - - /** - * Get object. - * - * @since 3.0.0 - * @param int $id Object ID. - * @return WC_Data - */ - protected function get_object( $id ) { - return wc_get_product( $id ); - } - - /** - * Check if a given request has access to update an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $object->get_id() ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - // Check if variation belongs to the correct parent product. - if ( $object && 0 !== $object->get_parent_id() && absint( $request['product_id'] ) !== $object->get_parent_id() ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Parent product does not match current variation.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Prepare a single variation output for response. - * - * @since 3.0.0 - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_object_for_response( $object, $request ) { - $data = array( - 'id' => $object->get_id(), - 'date_created' => wc_rest_prepare_date_response( $object->get_date_created(), false ), - 'date_created_gmt' => wc_rest_prepare_date_response( $object->get_date_created() ), - 'date_modified' => wc_rest_prepare_date_response( $object->get_date_modified(), false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( $object->get_date_modified() ), - 'description' => wc_format_content( $object->get_description() ), - 'permalink' => $object->get_permalink(), - 'sku' => $object->get_sku(), - 'price' => $object->get_price(), - 'regular_price' => $object->get_regular_price(), - 'sale_price' => $object->get_sale_price(), - 'date_on_sale_from' => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ), - 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ), - 'date_on_sale_to' => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ), - 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ), - 'on_sale' => $object->is_on_sale(), - 'visible' => $object->is_visible(), - 'purchasable' => $object->is_purchasable(), - 'virtual' => $object->is_virtual(), - 'downloadable' => $object->is_downloadable(), - 'downloads' => $this->get_downloads( $object ), - 'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1, - 'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1, - 'tax_status' => $object->get_tax_status(), - 'tax_class' => $object->get_tax_class(), - 'manage_stock' => $object->managing_stock(), - 'stock_quantity' => $object->get_stock_quantity(), - 'in_stock' => $object->is_in_stock(), - 'backorders' => $object->get_backorders(), - 'backorders_allowed' => $object->backorders_allowed(), - 'backordered' => $object->is_on_backorder(), - 'weight' => $object->get_weight(), - 'dimensions' => array( - 'length' => $object->get_length(), - 'width' => $object->get_width(), - 'height' => $object->get_height(), - ), - 'shipping_class' => $object->get_shipping_class(), - 'shipping_class_id' => $object->get_shipping_class_id(), - 'image' => current( $this->get_images( $object ) ), - 'attributes' => $this->get_attributes( $object ), - 'menu_order' => $object->get_menu_order(), - 'meta_data' => $object->get_meta_data(), - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $object, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, - * refers to object type being prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - $args = parent::prepare_objects_query( $request ); - - $args['post_parent'] = $request['product_id']; - - return $args; - } - - /** - * Prepare a single variation for create or update. - * - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return WP_Error|WC_Data - */ - protected function prepare_object_for_database( $request, $creating = false ) { - if ( isset( $request['id'] ) ) { - $variation = wc_get_product( absint( $request['id'] ) ); - } else { - $variation = new WC_Product_Variation(); - } - - // Update parent ID just once. - if ( 0 === $variation->get_parent_id() ) { - $variation->set_parent_id( absint( $request['product_id'] ) ); - } - - // Status. - if ( isset( $request['visible'] ) ) { - $variation->set_status( false === $request['visible'] ? 'private' : 'publish' ); - } - - // SKU. - if ( isset( $request['sku'] ) ) { - $variation->set_sku( wc_clean( $request['sku'] ) ); - } - - // Thumbnail. - if ( isset( $request['image'] ) ) { - if ( is_array( $request['image'] ) && ! empty( $request['image'] ) ) { - $image = $request['image']; - if ( is_array( $image ) ) { - $image['position'] = 0; - } - - $variation = $this->set_product_images( $variation, array( $image ) ); - } else { - $variation->set_image_id( '' ); - } - } - - // Virtual variation. - if ( isset( $request['virtual'] ) ) { - $variation->set_virtual( $request['virtual'] ); - } - - // Downloadable variation. - if ( isset( $request['downloadable'] ) ) { - $variation->set_downloadable( $request['downloadable'] ); - } - - // Downloads. - if ( $variation->get_downloadable() ) { - // Downloadable files. - if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { - $variation = $this->save_downloadable_files( $variation, $request['downloads'] ); - } - - // Download limit. - if ( isset( $request['download_limit'] ) ) { - $variation->set_download_limit( $request['download_limit'] ); - } - - // Download expiry. - if ( isset( $request['download_expiry'] ) ) { - $variation->set_download_expiry( $request['download_expiry'] ); - } - } - - // Shipping data. - $variation = $this->save_product_shipping_data( $variation, $request ); - - // Stock handling. - if ( isset( $request['manage_stock'] ) ) { - if ( 'parent' === $request['manage_stock'] ) { - $variation->set_manage_stock( false ); // This just indicates the variation does not manage stock, but the parent does. - } else { - $variation->set_manage_stock( wc_string_to_bool( $request['manage_stock'] ) ); - } - } - - if ( isset( $request['in_stock'] ) ) { - $variation->set_stock_status( true === $request['in_stock'] ? 'instock' : 'outofstock' ); - } - - if ( isset( $request['backorders'] ) ) { - $variation->set_backorders( $request['backorders'] ); - } - - if ( $variation->get_manage_stock() ) { - if ( isset( $request['stock_quantity'] ) ) { - $variation->set_stock_quantity( $request['stock_quantity'] ); - } elseif ( isset( $request['inventory_delta'] ) ) { - $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() ); - $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); - $variation->set_stock_quantity( $stock_quantity ); - } - } else { - $variation->set_backorders( 'no' ); - $variation->set_stock_quantity( '' ); - } - - // Regular Price. - if ( isset( $request['regular_price'] ) ) { - $variation->set_regular_price( $request['regular_price'] ); - } - - // Sale Price. - if ( isset( $request['sale_price'] ) ) { - $variation->set_sale_price( $request['sale_price'] ); - } - - if ( isset( $request['date_on_sale_from'] ) ) { - $variation->set_date_on_sale_from( $request['date_on_sale_from'] ); - } - - if ( isset( $request['date_on_sale_from_gmt'] ) ) { - $variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); - } - - if ( isset( $request['date_on_sale_to'] ) ) { - $variation->set_date_on_sale_to( $request['date_on_sale_to'] ); - } - - if ( isset( $request['date_on_sale_to_gmt'] ) ) { - $variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); - } - - // Tax class. - if ( isset( $request['tax_class'] ) ) { - $variation->set_tax_class( $request['tax_class'] ); - } - - // Description. - if ( isset( $request['description'] ) ) { - $variation->set_description( wp_kses_post( $request['description'] ) ); - } - - // Update taxonomies. - if ( isset( $request['attributes'] ) ) { - $attributes = array(); - $parent = wc_get_product( $variation->get_parent_id() ); - $parent_attributes = $parent->get_attributes(); - - foreach ( $request['attributes'] as $attribute ) { - $attribute_id = 0; - $attribute_name = ''; - - // Check ID for global attributes or name for product attributes. - if ( ! empty( $attribute['id'] ) ) { - $attribute_id = absint( $attribute['id'] ); - $raw_attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); - } elseif ( ! empty( $attribute['name'] ) ) { - $raw_attribute_name = sanitize_title( $attribute['name'] ); - } - - if ( ! $attribute_id && ! $raw_attribute_name ) { - continue; - } - - $attribute_name = sanitize_title( $raw_attribute_name ); - - if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) { - continue; - } - - $attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() ); - $attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; - - if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) { - // If dealing with a taxonomy, we need to get the slug from the name posted to the API. - $term = get_term_by( 'name', $attribute_value, $raw_attribute_name ); // @codingStandardsIgnoreLine - - if ( $term && ! is_wp_error( $term ) ) { - $attribute_value = $term->slug; - } else { - $attribute_value = sanitize_title( $attribute_value ); - } - } - - $attributes[ $attribute_key ] = $attribute_value; - } - - $variation->set_attributes( $attributes ); - } - - // Menu order. - if ( $request['menu_order'] ) { - $variation->set_menu_order( $request['menu_order'] ); - } - - // Meta data. - if ( is_array( $request['meta_data'] ) ) { - foreach ( $request['meta_data'] as $meta ) { - $variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - - /** - * Filters an object before it is inserted via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, - * refers to the object type slug. - * - * @param WC_Data $variation Object object. - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - */ - return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $variation, $request, $creating ); - } - - /** - * Clear caches here so in sync with any new variations. - * - * @param WC_Data $object Object data. - */ - public function clear_transients( $object ) { - wc_delete_product_transients( $object->get_parent_id() ); - wp_cache_delete( 'product-' . $object->get_parent_id(), 'products' ); - } - - /** - * Delete a variation. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return bool|WP_Error|WP_REST_Response - */ - public function delete_item( $request ) { - $force = (bool) $request['force']; - $object = $this->get_object( (int) $request['id'] ); - $result = false; - - if ( ! $object || 0 === $object->get_id() ) { - return new WP_Error( - "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( - 'status' => 404, - ) - ); - } - - $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); - - /** - * Filter whether an object is trashable. - * - * Return false to disable trash support for the object. - * - * @param boolean $supports_trash Whether the object type support trashing. - * @param WC_Data $object The object being considered for trashing support. - */ - $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); - - if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { - return new WP_Error( - /* translators: %s: post type */ - "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( - 'status' => rest_authorization_required_code(), - ) - ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_object_for_response( $object, $request ); - - // If we're forcing, then delete permanently. - if ( $force ) { - $object->delete( true ); - $result = 0 === $object->get_id(); - } else { - // If we don't support trashing for this type, error out. - if ( ! $supports_trash ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( - 'status' => 501, - ) - ); - } - - // Otherwise, only trash if we haven't already. - if ( is_callable( array( $object, 'get_status' ) ) ) { - if ( 'trash' === $object->get_status() ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( - 'status' => 410, - ) - ); - } - - $object->delete(); - $result = 'trash' === $object->get_status(); - } - } - - if ( ! $result ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( - 'status' => 500, - ) - ); - } - - // Delete parent product transients. - if ( 0 !== $object->get_parent_id() ) { - wc_delete_product_transients( $object->get_parent_id() ); - } - - /** - * Fires after a single object is deleted or trashed via the REST API. - * - * @param WC_Data $object The deleted or trashed object. - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); - - return $response; - } - - /** - * Bulk create, update and delete items. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array Of WP_Error or WP_REST_Response. - */ - public function batch_items( $request ) { - $items = array_filter( $request->get_params() ); - $params = $request->get_url_params(); - $product_id = $params['product_id']; - $body_params = array(); - - foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) { - if ( ! empty( $items[ $batch_type ] ) ) { - $injected_items = array(); - foreach ( $items[ $batch_type ] as $item ) { - $injected_items[] = is_array( $item ) ? array_merge( - array( - 'product_id' => $product_id, - ), $item - ) : $item; - } - $body_params[ $batch_type ] = $injected_items; - } - } - - $request = new WP_REST_Request( $request->get_method() ); - $request->set_body_params( $body_params ); - - return parent::batch_items( $request ); - } - - /** - * Prepare links for the request. - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $object, $request ) { - $product_id = (int) $request['product_id']; - $base = str_replace( '(?P[\d]+)', $product_id, $this->rest_base ); - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ), - ), - 'up' => array( - 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product_id ) ), - ), - ); - return $links; - } - - /** - * Get the Variation's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $weight_unit = get_option( 'woocommerce_weight_unit' ); - $dimension_unit = get_option( 'woocommerce_dimension_unit' ); - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the variation was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'Variation description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'permalink' => array( - 'description' => __( 'Variation URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'sku' => array( - 'description' => __( 'Unique identifier.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'price' => array( - 'description' => __( 'Current variation price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'regular_price' => array( - 'description' => __( 'Variation regular price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'sale_price' => array( - 'description' => __( 'Variation sale price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_from' => array( - 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_from_gmt' => array( - 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_to' => array( - 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_to_gmt' => array( - 'description' => __( 'End date of sale price, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'on_sale' => array( - 'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'visible' => array( - 'description' => __( "Define if the variation is visible on the product's page.", 'woocommerce' ), - 'type' => 'boolean', - 'default' => true, - 'context' => array( 'view', 'edit' ), - ), - 'purchasable' => array( - 'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'virtual' => array( - 'description' => __( 'If the variation is virtual.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'downloadable' => array( - 'description' => __( 'If the variation is downloadable.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'downloads' => array( - 'description' => __( 'List of downloadable files.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'File MD5 hash.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'File name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'file' => array( - 'description' => __( 'File URL.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'download_limit' => array( - 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), - 'type' => 'integer', - 'default' => -1, - 'context' => array( 'view', 'edit' ), - ), - 'download_expiry' => array( - 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), - 'type' => 'integer', - 'default' => -1, - 'context' => array( 'view', 'edit' ), - ), - 'tax_status' => array( - 'description' => __( 'Tax status.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'taxable', - 'enum' => array( 'taxable', 'shipping', 'none' ), - 'context' => array( 'view', 'edit' ), - ), - 'tax_class' => array( - 'description' => __( 'Tax class.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'manage_stock' => array( - 'description' => __( 'Stock management at variation level.', 'woocommerce' ), - 'type' => 'mixed', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'stock_quantity' => array( - 'description' => __( 'Stock quantity.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'in_stock' => array( - 'description' => __( 'Controls whether or not the variation is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => true, - 'context' => array( 'view', 'edit' ), - ), - 'backorders' => array( - 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'no', - 'enum' => array( 'no', 'notify', 'yes' ), - 'context' => array( 'view', 'edit' ), - ), - 'backorders_allowed' => array( - 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'backordered' => array( - 'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'weight' => array( - /* translators: %s: weight unit */ - 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'dimensions' => array( - 'description' => __( 'Variation dimensions.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'length' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'width' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'height' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'shipping_class' => array( - 'description' => __( 'Shipping class slug.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'shipping_class_id' => array( - 'description' => __( 'Shipping class ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'image' => array( - 'description' => __( 'Variation image data.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'id' => array( - 'description' => __( 'Image ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'src' => array( - 'description' => __( 'Image URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Image name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'alt' => array( - 'description' => __( 'Image alternative text.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'position' => array( - 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'attributes' => array( - 'description' => __( 'List of attributes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Attribute ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Attribute name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'option' => array( - 'description' => __( 'Selected attribute term name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'menu_order' => array( - 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 1f2af73999c3..1073cf6ef40b 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -14,2132 +14,14 @@ * REST API Products controller class. * * @package WooCommerce/API - * @extends WC_REST_CRUD_Controller + * @extends WC_REST_Products_V2_Controller */ -class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller { +class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'products'; - - /** - * Post type. - * - * @var string - */ - protected $post_type = 'product'; - - /** - * If object is hierarchical. - * - * @var bool - */ - protected $hierarchical = true; - - /** - * Initialize product actions. - */ - public function __construct() { - 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' ), - ) - ); - - 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' ), - ) - ); - - 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' ), - ) - ); - } - - /** - * Get object. - * - * @since 3.0.0 - * @param int $id Object ID. - * @return WC_Data - */ - protected function get_object( $id ) { - return wc_get_product( $id ); - } - - /** - * Prepare a single product output for response. - * - * @since 3.0.0 - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_object_for_response( $object, $request ) { - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->get_product_data( $object, $context ); - - // Add variations to variable products. - if ( $object->is_type( 'variable' ) && $object->has_child() ) { - $data['variations'] = $object->get_children(); - } - - // Add grouped products data. - if ( $object->is_type( 'grouped' ) && $object->has_child() ) { - $data['grouped_products'] = $object->get_children(); - } - - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $object, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, - * refers to object type being prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - $args = parent::prepare_objects_query( $request ); - - // Set post_status. - $args['post_status'] = $request['status']; - - // Taxonomy query to filter products by type, category, - // tag, shipping class, and attribute. - $tax_query = array(); - - // Map between taxonomy name and arg's key. - $taxonomies = array( - 'product_cat' => 'category', - 'product_tag' => 'tag', - 'product_shipping_class' => 'shipping_class', - ); - - // Set tax_query for each passed arg. - foreach ( $taxonomies as $taxonomy => $key ) { - if ( ! empty( $request[ $key ] ) ) { - $tax_query[] = array( - 'taxonomy' => $taxonomy, - 'field' => 'term_id', - 'terms' => $request[ $key ], - ); - } - } - - // Filter product type by slug. - if ( ! empty( $request['type'] ) ) { - $tax_query[] = array( - 'taxonomy' => 'product_type', - 'field' => 'slug', - 'terms' => $request['type'], - ); - } - - // Filter by attribute and term. - if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) { - if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) { - $tax_query[] = array( - 'taxonomy' => $request['attribute'], - 'field' => 'term_id', - 'terms' => $request['attribute_term'], - ); - } - } - - if ( ! empty( $tax_query ) ) { - $args['tax_query'] = $tax_query; // WPCS: slow query ok. - } - - // Filter featured. - if ( is_bool( $request['featured'] ) ) { - $args['tax_query'][] = array( - 'taxonomy' => 'product_visibility', - 'field' => 'name', - 'terms' => 'featured', - 'operator' => true === $request['featured'] ? 'IN' : 'NOT IN', - ); - } - - // Filter by sku. - if ( ! empty( $request['sku'] ) ) { - $skus = explode( ',', $request['sku'] ); - // Include the current string as a SKU too. - if ( 1 < count( $skus ) ) { - $skus[] = $request['sku']; - } - - $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( - 'key' => '_sku', - 'value' => $skus, - 'compare' => 'IN', - ) - ); - } - - // Filter by tax class. - if ( ! empty( $request['tax_class'] ) ) { - $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( - 'key' => '_tax_class', - 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', - ) - ); - } - - // Price filter. - if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { - $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); // WPCS: slow query ok. - } - - // Filter product in stock or out of stock. - if ( is_bool( $request['in_stock'] ) ) { - $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( - 'key' => '_stock_status', - 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', - ) - ); - } - - // Filter by on sale products. - if ( is_bool( $request['on_sale'] ) ) { - $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in'; - $on_sale_ids = wc_get_product_ids_on_sale(); - - // Use 0 when there's no on sale products to avoid return all products. - $on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids; - - $args[ $on_sale_key ] += $on_sale_ids; - } - - // Force the post_type argument, since it's not a user input variable. - if ( ! empty( $request['sku'] ) ) { - $args['post_type'] = array( 'product', 'product_variation' ); - } else { - $args['post_type'] = $this->post_type; - } - - return $args; - } - - /** - * Get the downloads for a product or product variation. - * - * @param WC_Product|WC_Product_Variation $product Product instance. - * @return array - */ - protected function get_downloads( $product ) { - $downloads = array(); - - if ( $product->is_downloadable() ) { - foreach ( $product->get_downloads() as $file_id => $file ) { - $downloads[] = array( - 'id' => $file_id, // MD5 hash. - 'name' => $file['name'], - 'file' => $file['file'], - ); - } - } - - return $downloads; - } - - /** - * Get taxonomy terms. - * - * @param WC_Product $product Product instance. - * @param string $taxonomy Taxonomy slug. - * @return array - */ - protected function get_taxonomy_terms( $product, $taxonomy = 'cat' ) { - $terms = array(); - - foreach ( wc_get_object_terms( $product->get_id(), 'product_' . $taxonomy ) as $term ) { - $terms[] = array( - 'id' => $term->term_id, - 'name' => $term->name, - 'slug' => $term->slug, - ); - } - - return $terms; - } - - /** - * Get the images for a product or product variation. - * - * @param WC_Product|WC_Product_Variation $product Product instance. - * @return array - */ - protected function get_images( $product ) { - $images = array(); - $attachment_ids = array(); - - // Add featured image. - if ( has_post_thumbnail( $product->get_id() ) ) { - $attachment_ids[] = $product->get_image_id(); - } - - // Add gallery images. - $attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() ); - - // Build image data. - foreach ( $attachment_ids as $position => $attachment_id ) { - $attachment_post = get_post( $attachment_id ); - if ( is_null( $attachment_post ) ) { - continue; - } - - $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); - if ( ! is_array( $attachment ) ) { - continue; - } - - $images[] = array( - 'id' => (int) $attachment_id, - 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ), - 'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ), - 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified, false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ), - 'src' => current( $attachment ), - 'name' => get_the_title( $attachment_id ), - 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), - 'position' => (int) $position, - ); - } - - // Set a placeholder image if the product has no images set. - if ( empty( $images ) ) { - $images[] = array( - 'id' => 0, - 'date_created' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ), // Default to now. - 'date_created_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ), // Default to now. - 'date_modified' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ), - 'src' => wc_placeholder_img_src(), - 'name' => __( 'Placeholder', 'woocommerce' ), - 'alt' => __( 'Placeholder', 'woocommerce' ), - 'position' => 0, - ); - } - - return $images; - } - - /** - * Get attribute taxonomy label. - * - * @deprecated 3.0.0 - * - * @param string $name Taxonomy name. - * @return string - */ - protected function get_attribute_taxonomy_label( $name ) { - $tax = get_taxonomy( $name ); - $labels = get_taxonomy_labels( $tax ); - - return $labels->singular_name; - } - - /** - * Get product attribute taxonomy name. - * - * @since 3.0.0 - * @param string $slug Taxonomy name. - * @param WC_Product $product Product data. - * @return string - */ - protected function get_attribute_taxonomy_name( $slug, $product ) { - $attributes = $product->get_attributes(); - - if ( ! isset( $attributes[ $slug ] ) ) { - return str_replace( 'pa_', '', $slug ); - } - - $attribute = $attributes[ $slug ]; - - // Taxonomy attribute name. - if ( $attribute->is_taxonomy() ) { - $taxonomy = $attribute->get_taxonomy_object(); - return $taxonomy->attribute_label; - } - - // Custom product attribute name. - return $attribute->get_name(); - } - - /** - * Get default attributes. - * - * @param WC_Product $product Product instance. - * @return array - */ - protected function get_default_attributes( $product ) { - $default = array(); - - if ( $product->is_type( 'variable' ) ) { - foreach ( array_filter( (array) $product->get_default_attributes(), 'strlen' ) as $key => $value ) { - if ( 0 === strpos( $key, 'pa_' ) ) { - $default[] = array( - 'id' => wc_attribute_taxonomy_id_by_name( $key ), - 'name' => $this->get_attribute_taxonomy_name( $key, $product ), - 'option' => $value, - ); - } else { - $default[] = array( - 'id' => 0, - 'name' => $this->get_attribute_taxonomy_name( $key, $product ), - 'option' => $value, - ); - } - } - } - - return $default; - } - - /** - * Get attribute options. - * - * @param int $product_id Product ID. - * @param array $attribute Attribute data. - * @return array - */ - protected function get_attribute_options( $product_id, $attribute ) { - if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { - return wc_get_product_terms( - $product_id, $attribute['name'], array( - 'fields' => 'names', - ) - ); - } elseif ( isset( $attribute['value'] ) ) { - return array_map( 'trim', explode( '|', $attribute['value'] ) ); - } - - return array(); - } - - /** - * Get the attributes for a product or product variation. - * - * @param WC_Product|WC_Product_Variation $product Product instance. - * @return array - */ - protected function get_attributes( $product ) { - $attributes = array(); - - if ( $product->is_type( 'variation' ) ) { - $_product = wc_get_product( $product->get_parent_id() ); - foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) { - $name = str_replace( 'attribute_', '', $attribute_name ); - - if ( ! $attribute ) { - continue; - } - - // Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`. - if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) { - $option_term = get_term_by( 'slug', $attribute, $name ); - $attributes[] = array( - 'id' => wc_attribute_taxonomy_id_by_name( $name ), - 'name' => $this->get_attribute_taxonomy_name( $name, $_product ), - 'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute, - ); - } else { - $attributes[] = array( - 'id' => 0, - 'name' => $this->get_attribute_taxonomy_name( $name, $_product ), - 'option' => $attribute, - ); - } - } - } else { - foreach ( $product->get_attributes() as $attribute ) { - $attributes[] = array( - 'id' => $attribute['is_taxonomy'] ? wc_attribute_taxonomy_id_by_name( $attribute['name'] ) : 0, - 'name' => $this->get_attribute_taxonomy_name( $attribute['name'], $product ), - 'position' => (int) $attribute['position'], - 'visible' => (bool) $attribute['is_visible'], - 'variation' => (bool) $attribute['is_variation'], - 'options' => $this->get_attribute_options( $product->get_id(), $attribute ), - ); - } - } - - return $attributes; - } - - /** - * Get product data. - * - * @param WC_Product $product Product instance. - * @param string $context Request context. - * Options: 'view' and 'edit'. - * @return array - */ - protected function get_product_data( $product, $context = 'view' ) { - $data = array( - 'id' => $product->get_id(), - 'name' => $product->get_name( $context ), - 'slug' => $product->get_slug( $context ), - 'permalink' => $product->get_permalink(), - 'date_created' => wc_rest_prepare_date_response( $product->get_date_created( $context ), false ), - 'date_created_gmt' => wc_rest_prepare_date_response( $product->get_date_created( $context ) ), - 'date_modified' => wc_rest_prepare_date_response( $product->get_date_modified( $context ), false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( $product->get_date_modified( $context ) ), - 'type' => $product->get_type(), - 'status' => $product->get_status( $context ), - 'featured' => $product->is_featured(), - 'catalog_visibility' => $product->get_catalog_visibility( $context ), - 'description' => 'view' === $context ? wpautop( do_shortcode( $product->get_description() ) ) : $product->get_description( $context ), - 'short_description' => 'view' === $context ? apply_filters( 'woocommerce_short_description', $product->get_short_description() ) : $product->get_short_description( $context ), - 'sku' => $product->get_sku( $context ), - 'price' => $product->get_price( $context ), - 'regular_price' => $product->get_regular_price( $context ), - 'sale_price' => $product->get_sale_price( $context ) ? $product->get_sale_price( $context ) : '', - 'date_on_sale_from' => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ), false ), - 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ) ), - 'date_on_sale_to' => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ), false ), - 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ) ), - 'price_html' => $product->get_price_html(), - 'on_sale' => $product->is_on_sale( $context ), - 'purchasable' => $product->is_purchasable(), - 'total_sales' => $product->get_total_sales( $context ), - 'virtual' => $product->is_virtual(), - 'downloadable' => $product->is_downloadable(), - 'downloads' => $this->get_downloads( $product ), - 'download_limit' => $product->get_download_limit( $context ), - 'download_expiry' => $product->get_download_expiry( $context ), - 'external_url' => $product->is_type( 'external' ) ? $product->get_product_url( $context ) : '', - 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text( $context ) : '', - 'tax_status' => $product->get_tax_status( $context ), - 'tax_class' => $product->get_tax_class( $context ), - 'manage_stock' => $product->managing_stock(), - 'stock_quantity' => $product->get_stock_quantity( $context ), - 'in_stock' => $product->is_in_stock(), - 'backorders' => $product->get_backorders( $context ), - 'backorders_allowed' => $product->backorders_allowed(), - 'backordered' => $product->is_on_backorder(), - 'sold_individually' => $product->is_sold_individually(), - 'weight' => $product->get_weight( $context ), - 'dimensions' => array( - 'length' => $product->get_length( $context ), - 'width' => $product->get_width( $context ), - 'height' => $product->get_height( $context ), - ), - 'shipping_required' => $product->needs_shipping(), - 'shipping_taxable' => $product->is_shipping_taxable(), - 'shipping_class' => $product->get_shipping_class(), - 'shipping_class_id' => $product->get_shipping_class_id( $context ), - 'reviews_allowed' => $product->get_reviews_allowed( $context ), - 'average_rating' => 'view' === $context ? wc_format_decimal( $product->get_average_rating(), 2 ) : $product->get_average_rating( $context ), - 'rating_count' => $product->get_rating_count(), - 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), - 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids( $context ) ), - 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids( $context ) ), - 'parent_id' => $product->get_parent_id( $context ), - 'purchase_note' => 'view' === $context ? wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ) : $product->get_purchase_note( $context ), - 'categories' => $this->get_taxonomy_terms( $product ), - 'tags' => $this->get_taxonomy_terms( $product, 'tag' ), - 'images' => $this->get_images( $product ), - 'attributes' => $this->get_attributes( $product ), - 'default_attributes' => $this->get_default_attributes( $product ), - 'variations' => array(), - 'grouped_products' => array(), - 'menu_order' => $product->get_menu_order( $context ), - 'meta_data' => $product->get_meta_data(), - ); - - return $data; - } - - /** - * Prepare links for the request. - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $object, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - if ( $object->get_parent_id() ) { - $links['up'] = array( - 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $object->get_parent_id() ) ), - ); - } - - return $links; - } - - /** - * Prepare a single product for create or update. - * - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return WP_Error|WC_Data - */ - protected function prepare_object_for_database( $request, $creating = false ) { - $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; - - // Type is the most important part here because we need to be using the correct class and methods. - if ( isset( $request['type'] ) ) { - $classname = WC_Product_Factory::get_classname_from_product_type( $request['type'] ); - - if ( ! class_exists( $classname ) ) { - $classname = 'WC_Product_Simple'; - } - - $product = new $classname( $id ); - } elseif ( isset( $request['id'] ) ) { - $product = wc_get_product( $id ); - } else { - $product = new WC_Product_Simple(); - } - - if ( 'variation' === $product->get_type() ) { - return new WP_Error( - "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( - 'status' => 404, - ) - ); - } - - // Post title. - if ( isset( $request['name'] ) ) { - $product->set_name( wp_filter_post_kses( $request['name'] ) ); - } - - // Post content. - if ( isset( $request['description'] ) ) { - $product->set_description( wp_filter_post_kses( $request['description'] ) ); - } - - // Post excerpt. - if ( isset( $request['short_description'] ) ) { - $product->set_short_description( wp_filter_post_kses( $request['short_description'] ) ); - } - - // Post status. - if ( isset( $request['status'] ) ) { - $product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); - } - - // Post slug. - if ( isset( $request['slug'] ) ) { - $product->set_slug( $request['slug'] ); - } - - // Menu order. - if ( isset( $request['menu_order'] ) ) { - $product->set_menu_order( $request['menu_order'] ); - } - - // Comment status. - if ( isset( $request['reviews_allowed'] ) ) { - $product->set_reviews_allowed( $request['reviews_allowed'] ); - } - - // Virtual. - if ( isset( $request['virtual'] ) ) { - $product->set_virtual( $request['virtual'] ); - } - - // Tax status. - if ( isset( $request['tax_status'] ) ) { - $product->set_tax_status( $request['tax_status'] ); - } - - // Tax Class. - if ( isset( $request['tax_class'] ) ) { - $product->set_tax_class( $request['tax_class'] ); - } - - // Catalog Visibility. - if ( isset( $request['catalog_visibility'] ) ) { - $product->set_catalog_visibility( $request['catalog_visibility'] ); - } - - // Purchase Note. - if ( isset( $request['purchase_note'] ) ) { - $product->set_purchase_note( wp_kses_post( wp_unslash( $request['purchase_note'] ) ) ); - } - - // Featured Product. - if ( isset( $request['featured'] ) ) { - $product->set_featured( $request['featured'] ); - } - - // Shipping data. - $product = $this->save_product_shipping_data( $product, $request ); - - // SKU. - if ( isset( $request['sku'] ) ) { - $product->set_sku( wc_clean( $request['sku'] ) ); - } - - // Attributes. - if ( isset( $request['attributes'] ) ) { - $attributes = array(); - - foreach ( $request['attributes'] as $attribute ) { - $attribute_id = 0; - $attribute_name = ''; - - // Check ID for global attributes or name for product attributes. - if ( ! empty( $attribute['id'] ) ) { - $attribute_id = absint( $attribute['id'] ); - $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); - } elseif ( ! empty( $attribute['name'] ) ) { - $attribute_name = wc_clean( $attribute['name'] ); - } - - if ( ! $attribute_id && ! $attribute_name ) { - continue; - } - - if ( $attribute_id ) { - - if ( isset( $attribute['options'] ) ) { - $options = $attribute['options']; - - if ( ! is_array( $attribute['options'] ) ) { - // Text based attributes - Posted values are term names. - $options = explode( WC_DELIMITER, $options ); - } - - $values = array_map( 'wc_sanitize_term_text_based', $options ); - $values = array_filter( $values, 'strlen' ); - } else { - $values = array(); - } - - if ( ! empty( $values ) ) { - // Add attribute to array, but don't set values. - $attribute_object = new WC_Product_Attribute(); - $attribute_object->set_id( $attribute_id ); - $attribute_object->set_name( $attribute_name ); - $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); - $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); - $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); - $attributes[] = $attribute_object; - } - } elseif ( isset( $attribute['options'] ) ) { - // Custom attribute - Add attribute to array and set the values. - if ( is_array( $attribute['options'] ) ) { - $values = $attribute['options']; - } else { - $values = explode( WC_DELIMITER, $attribute['options'] ); - } - $attribute_object = new WC_Product_Attribute(); - $attribute_object->set_name( $attribute_name ); - $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); - $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); - $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); - $attributes[] = $attribute_object; - } - } - $product->set_attributes( $attributes ); - } - - // Sales and prices. - if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) { - $product->set_regular_price( '' ); - $product->set_sale_price( '' ); - $product->set_date_on_sale_to( '' ); - $product->set_date_on_sale_from( '' ); - $product->set_price( '' ); - } else { - // Regular Price. - if ( isset( $request['regular_price'] ) ) { - $product->set_regular_price( $request['regular_price'] ); - } - - // Sale Price. - if ( isset( $request['sale_price'] ) ) { - $product->set_sale_price( $request['sale_price'] ); - } - - if ( isset( $request['date_on_sale_from'] ) ) { - $product->set_date_on_sale_from( $request['date_on_sale_from'] ); - } - - if ( isset( $request['date_on_sale_from_gmt'] ) ) { - $product->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); - } - - if ( isset( $request['date_on_sale_to'] ) ) { - $product->set_date_on_sale_to( $request['date_on_sale_to'] ); - } - - if ( isset( $request['date_on_sale_to_gmt'] ) ) { - $product->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); - } - } - - // Product parent ID for groups. - if ( isset( $request['parent_id'] ) ) { - $product->set_parent_id( $request['parent_id'] ); - } - - // Sold individually. - if ( isset( $request['sold_individually'] ) ) { - $product->set_sold_individually( $request['sold_individually'] ); - } - - // Stock status. - if ( isset( $request['in_stock'] ) ) { - $stock_status = true === $request['in_stock'] ? 'instock' : 'outofstock'; - } else { - $stock_status = $product->get_stock_status(); - } - - // Stock data. - if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { - // Manage stock. - if ( isset( $request['manage_stock'] ) ) { - $product->set_manage_stock( $request['manage_stock'] ); - } - - // Backorders. - if ( isset( $request['backorders'] ) ) { - $product->set_backorders( $request['backorders'] ); - } - - if ( $product->is_type( 'grouped' ) ) { - $product->set_manage_stock( 'no' ); - $product->set_backorders( 'no' ); - $product->set_stock_quantity( '' ); - $product->set_stock_status( $stock_status ); - } elseif ( $product->is_type( 'external' ) ) { - $product->set_manage_stock( 'no' ); - $product->set_backorders( 'no' ); - $product->set_stock_quantity( '' ); - $product->set_stock_status( 'instock' ); - } elseif ( $product->get_manage_stock() ) { - // Stock status is always determined by children so sync later. - if ( ! $product->is_type( 'variable' ) ) { - $product->set_stock_status( $stock_status ); - } - - // Stock quantity. - if ( isset( $request['stock_quantity'] ) ) { - $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); - } elseif ( isset( $request['inventory_delta'] ) ) { - $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); - $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); - $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); - } - } else { - // Don't manage stock. - $product->set_manage_stock( 'no' ); - $product->set_stock_quantity( '' ); - $product->set_stock_status( $stock_status ); - } - } elseif ( ! $product->is_type( 'variable' ) ) { - $product->set_stock_status( $stock_status ); - } - - // Upsells. - if ( isset( $request['upsell_ids'] ) ) { - $upsells = array(); - $ids = $request['upsell_ids']; - - if ( ! empty( $ids ) ) { - foreach ( $ids as $id ) { - if ( $id && $id > 0 ) { - $upsells[] = $id; - } - } - } - - $product->set_upsell_ids( $upsells ); - } - - // Cross sells. - if ( isset( $request['cross_sell_ids'] ) ) { - $crosssells = array(); - $ids = $request['cross_sell_ids']; - - if ( ! empty( $ids ) ) { - foreach ( $ids as $id ) { - if ( $id && $id > 0 ) { - $crosssells[] = $id; - } - } - } - - $product->set_cross_sell_ids( $crosssells ); - } - - // Product categories. - if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) { - $product = $this->save_taxonomy_terms( $product, $request['categories'] ); - } - - // Product tags. - if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) { - $product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' ); - } - - // Downloadable. - if ( isset( $request['downloadable'] ) ) { - $product->set_downloadable( $request['downloadable'] ); - } - - // Downloadable options. - if ( $product->get_downloadable() ) { - - // Downloadable files. - if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { - $product = $this->save_downloadable_files( $product, $request['downloads'] ); - } - - // Download limit. - if ( isset( $request['download_limit'] ) ) { - $product->set_download_limit( $request['download_limit'] ); - } - - // Download expiry. - if ( isset( $request['download_expiry'] ) ) { - $product->set_download_expiry( $request['download_expiry'] ); - } - } - - // Product url and button text for external products. - if ( $product->is_type( 'external' ) ) { - if ( isset( $request['external_url'] ) ) { - $product->set_product_url( $request['external_url'] ); - } - - if ( isset( $request['button_text'] ) ) { - $product->set_button_text( $request['button_text'] ); - } - } - - // Save default attributes for variable products. - if ( $product->is_type( 'variable' ) ) { - $product = $this->save_default_attributes( $product, $request ); - } - - // Set children for a grouped product. - if ( $product->is_type( 'grouped' ) && isset( $request['grouped_products'] ) ) { - $product->set_children( $request['grouped_products'] ); - } - - // Check for featured/gallery images, upload it and set it. - if ( isset( $request['images'] ) ) { - $product = $this->set_product_images( $product, $request['images'] ); - } - - // Allow set meta_data. - if ( is_array( $request['meta_data'] ) ) { - foreach ( $request['meta_data'] as $meta ) { - $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); - } - } - - /** - * Filters an object before it is inserted via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, - * refers to the object type slug. - * - * @param WC_Data $product Object object. - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - */ - return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $product, $request, $creating ); - } - - /** - * Set product images. - * - * @throws WC_REST_Exception REST API exceptions. - * @param WC_Product $product Product instance. - * @param array $images Images data. - * @return WC_Product - */ - protected function set_product_images( $product, $images ) { - $images = is_array( $images ) ? array_filter( $images ) : array(); - - if ( ! empty( $images ) ) { - $gallery = array(); - - foreach ( $images as $image ) { - $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; - - if ( 0 === $attachment_id && isset( $image['src'] ) ) { - $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); - - if ( is_wp_error( $upload ) ) { - if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) { - throw new WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); - } else { - continue; - } - } - - $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() ); - } - - if ( ! wp_attachment_is_image( $attachment_id ) ) { - /* translators: %s: attachment id */ - throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); - } - - if ( isset( $image['position'] ) && 0 === absint( $image['position'] ) ) { - $product->set_image_id( $attachment_id ); - } else { - $gallery[] = $attachment_id; - } - - // Set the image alt if present. - if ( ! empty( $image['alt'] ) ) { - update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); - } - - // Set the image name if present. - if ( ! empty( $image['name'] ) ) { - wp_update_post( - array( - 'ID' => $attachment_id, - 'post_title' => $image['name'], - ) - ); - } - - // Set the image source if present, for future reference. - if ( ! empty( $image['src'] ) ) { - update_post_meta( $attachment_id, '_wc_attachment_source', esc_url_raw( $image['src'] ) ); - } - } - - $product->set_gallery_image_ids( $gallery ); - } else { - $product->set_image_id( '' ); - $product->set_gallery_image_ids( array() ); - } - - return $product; - } - - /** - * Save product shipping data. - * - * @param WC_Product $product Product instance. - * @param array $data Shipping data. - * @return WC_Product - */ - protected function save_product_shipping_data( $product, $data ) { - // Virtual. - if ( isset( $data['virtual'] ) && true === $data['virtual'] ) { - $product->set_weight( '' ); - $product->set_height( '' ); - $product->set_length( '' ); - $product->set_width( '' ); - } else { - if ( isset( $data['weight'] ) ) { - $product->set_weight( $data['weight'] ); - } - - // Height. - if ( isset( $data['dimensions']['height'] ) ) { - $product->set_height( $data['dimensions']['height'] ); - } - - // Width. - if ( isset( $data['dimensions']['width'] ) ) { - $product->set_width( $data['dimensions']['width'] ); - } - - // Length. - if ( isset( $data['dimensions']['length'] ) ) { - $product->set_length( $data['dimensions']['length'] ); - } - } - - // Shipping class. - if ( isset( $data['shipping_class'] ) ) { - $data_store = $product->get_data_store(); - $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) ); - $product->set_shipping_class_id( $shipping_class_id ); - } - - return $product; - } - - /** - * Save downloadable files. - * - * @param WC_Product $product Product instance. - * @param array $downloads Downloads data. - * @param int $deprecated Deprecated since 3.0. - * @return WC_Product - */ - protected function save_downloadable_files( $product, $downloads, $deprecated = 0 ) { - if ( $deprecated ) { - wc_deprecated_argument( 'variation_id', '3.0', 'save_downloadable_files() not requires a variation_id anymore.' ); - } - - $files = array(); - foreach ( $downloads as $key => $file ) { - if ( empty( $file['file'] ) ) { - continue; - } - - $download = new WC_Product_Download(); - $download->set_id( $file['id'] ? $file['id'] : wp_generate_uuid4() ); - $download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) ); - $download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) ); - $files[] = $download; - } - $product->set_downloads( $files ); - - return $product; - } - - /** - * Save taxonomy terms. - * - * @param WC_Product $product Product instance. - * @param array $terms Terms data. - * @param string $taxonomy Taxonomy name. - * @return WC_Product - */ - protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { - $term_ids = wp_list_pluck( $terms, 'id' ); - - if ( 'cat' === $taxonomy ) { - $product->set_category_ids( $term_ids ); - } elseif ( 'tag' === $taxonomy ) { - $product->set_tag_ids( $term_ids ); - } - - return $product; - } - - /** - * Save default attributes. - * - * @since 3.0.0 - * - * @param WC_Product $product Product instance. - * @param WP_REST_Request $request Request data. - * @return WC_Product - */ - protected function save_default_attributes( $product, $request ) { - if ( isset( $request['default_attributes'] ) && is_array( $request['default_attributes'] ) ) { - - $attributes = $product->get_attributes(); - $default_attributes = array(); - - foreach ( $request['default_attributes'] as $attribute ) { - $attribute_id = 0; - $attribute_name = ''; - - // Check ID for global attributes or name for product attributes. - if ( ! empty( $attribute['id'] ) ) { - $attribute_id = absint( $attribute['id'] ); - $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); - } elseif ( ! empty( $attribute['name'] ) ) { - $attribute_name = sanitize_title( $attribute['name'] ); - } - - if ( ! $attribute_id && ! $attribute_name ) { - continue; - } - - if ( isset( $attributes[ $attribute_name ] ) ) { - $_attribute = $attributes[ $attribute_name ]; - - if ( $_attribute['is_variation'] ) { - $value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; - - if ( ! empty( $_attribute['is_taxonomy'] ) ) { - // If dealing with a taxonomy, we need to get the slug from the name posted to the API. - $term = get_term_by( 'name', $value, $attribute_name ); - - if ( $term && ! is_wp_error( $term ) ) { - $value = $term->slug; - } else { - $value = sanitize_title( $value ); - } - } - - if ( $value ) { - $default_attributes[ $attribute_name ] = $value; - } - } - } - } - - $product->set_default_attributes( $default_attributes ); - } - - return $product; - } - - /** - * Clear caches here so in sync with any new variations/children. - * - * @param WC_Data $object Object data. - */ - public function clear_transients( $object ) { - wc_delete_product_transients( $object->get_id() ); - wp_cache_delete( 'product-' . $object->get_id(), 'products' ); - } - - /** - * Delete a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error - */ - public function delete_item( $request ) { - $id = (int) $request['id']; - $force = (bool) $request['force']; - $object = $this->get_object( (int) $request['id'] ); - $result = false; - - if ( ! $object || 0 === $object->get_id() ) { - return new WP_Error( - "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( - 'status' => 404, - ) - ); - } - - if ( 'variation' === $object->get_type() ) { - return new WP_Error( - "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( - 'status' => 404, - ) - ); - } - - $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); - - /** - * Filter whether an object is trashable. - * - * Return false to disable trash support for the object. - * - * @param boolean $supports_trash Whether the object type support trashing. - * @param WC_Data $object The object being considered for trashing support. - */ - $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); - - if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { - return new WP_Error( - /* translators: %s: post type */ - "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( - 'status' => rest_authorization_required_code(), - ) - ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_object_for_response( $object, $request ); - - // If we're forcing, then delete permanently. - if ( $force ) { - if ( $object->is_type( 'variable' ) ) { - foreach ( $object->get_children() as $child_id ) { - $child = wc_get_product( $child_id ); - $child->delete( true ); - } - } elseif ( $object->is_type( 'grouped' ) ) { - foreach ( $object->get_children() as $child_id ) { - $child = wc_get_product( $child_id ); - $child->set_parent_id( 0 ); - $child->save(); - } - } - - $object->delete( true ); - $result = 0 === $object->get_id(); - } else { - // If we don't support trashing for this type, error out. - if ( ! $supports_trash ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( - 'status' => 501, - ) - ); - } - - // Otherwise, only trash if we haven't already. - if ( is_callable( array( $object, 'get_status' ) ) ) { - if ( 'trash' === $object->get_status() ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( - 'status' => 410, - ) - ); - } - - $object->delete(); - $result = 'trash' === $object->get_status(); - } - } - - if ( ! $result ) { - return new WP_Error( - /* translators: %s: post type */ - 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( - 'status' => 500, - ) - ); - } - - // Delete parent product transients. - if ( 0 !== $object->get_parent_id() ) { - wc_delete_product_transients( $object->get_parent_id() ); - } - - /** - * Fires after a single object is deleted or trashed via the REST API. - * - * @param WC_Data $object The deleted or trashed object. - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); - - return $response; - } - - /** - * Get the Product's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $weight_unit = get_option( 'woocommerce_weight_unit' ); - $dimension_unit = get_option( 'woocommerce_dimension_unit' ); - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Product name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'slug' => array( - 'description' => __( 'Product slug.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'permalink' => array( - 'description' => __( 'Product URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the product was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the product was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the product was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'type' => array( - 'description' => __( 'Product type.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'simple', - 'enum' => array_keys( wc_get_product_types() ), - 'context' => array( 'view', 'edit' ), - ), - 'status' => array( - 'description' => __( 'Product status (post status).', 'woocommerce' ), - 'type' => 'string', - 'default' => 'publish', - 'enum' => array_keys( get_post_statuses() ), - 'context' => array( 'view', 'edit' ), - ), - 'featured' => array( - 'description' => __( 'Featured product.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'catalog_visibility' => array( - 'description' => __( 'Catalog visibility.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'visible', - 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ), - 'context' => array( 'view', 'edit' ), - ), - 'description' => array( - 'description' => __( 'Product description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'short_description' => array( - 'description' => __( 'Product short description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'sku' => array( - 'description' => __( 'Unique identifier.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'price' => array( - 'description' => __( 'Current product price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'regular_price' => array( - 'description' => __( 'Product regular price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'sale_price' => array( - 'description' => __( 'Product sale price.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_from' => array( - 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_from_gmt' => array( - 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_to' => array( - 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'date_on_sale_to_gmt' => array( - 'description' => __( 'End date of sale price, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'price_html' => array( - 'description' => __( 'Price formatted in HTML.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'on_sale' => array( - 'description' => __( 'Shows if the product is on sale.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'purchasable' => array( - 'description' => __( 'Shows if the product can be bought.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'total_sales' => array( - 'description' => __( 'Amount of sales.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'virtual' => array( - 'description' => __( 'If the product is virtual.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'downloadable' => array( - 'description' => __( 'If the product is downloadable.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'downloads' => array( - 'description' => __( 'List of downloadable files.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'File MD5 hash.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'File name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'file' => array( - 'description' => __( 'File URL.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'download_limit' => array( - 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), - 'type' => 'integer', - 'default' => -1, - 'context' => array( 'view', 'edit' ), - ), - 'download_expiry' => array( - 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), - 'type' => 'integer', - 'default' => -1, - 'context' => array( 'view', 'edit' ), - ), - 'external_url' => array( - 'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - ), - 'button_text' => array( - 'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'tax_status' => array( - 'description' => __( 'Tax status.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'taxable', - 'enum' => array( 'taxable', 'shipping', 'none' ), - 'context' => array( 'view', 'edit' ), - ), - 'tax_class' => array( - 'description' => __( 'Tax class.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'manage_stock' => array( - 'description' => __( 'Stock management at product level.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'stock_quantity' => array( - 'description' => __( 'Stock quantity.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'in_stock' => array( - 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => true, - 'context' => array( 'view', 'edit' ), - ), - 'backorders' => array( - 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'no', - 'enum' => array( 'no', 'notify', 'yes' ), - 'context' => array( 'view', 'edit' ), - ), - 'backorders_allowed' => array( - 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'backordered' => array( - 'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'sold_individually' => array( - 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'weight' => array( - /* translators: %s: weight unit */ - 'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'dimensions' => array( - 'description' => __( 'Product dimensions.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'length' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'width' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'height' => array( - /* translators: %s: dimension unit */ - 'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - 'shipping_required' => array( - 'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'shipping_taxable' => array( - 'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'shipping_class' => array( - 'description' => __( 'Shipping class slug.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'shipping_class_id' => array( - 'description' => __( 'Shipping class ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'reviews_allowed' => array( - 'description' => __( 'Allow reviews.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => true, - 'context' => array( 'view', 'edit' ), - ), - 'average_rating' => array( - 'description' => __( 'Reviews average rating.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'rating_count' => array( - 'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'related_ids' => array( - 'description' => __( 'List of related products IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'upsell_ids' => array( - 'description' => __( 'List of up-sell products IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'cross_sell_ids' => array( - 'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'parent_id' => array( - 'description' => __( 'Product parent ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'purchase_note' => array( - 'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'categories' => array( - 'description' => __( 'List of categories.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Category ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Category name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'slug' => array( - 'description' => __( 'Category slug.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'tags' => array( - 'description' => __( 'List of tags.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Tag ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Tag name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'slug' => array( - 'description' => __( 'Tag slug.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - 'images' => array( - 'description' => __( 'List of images.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Image ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'src' => array( - 'description' => __( 'Image URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Image name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'alt' => array( - 'description' => __( 'Image alternative text.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'position' => array( - 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'attributes' => array( - 'description' => __( 'List of attributes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Attribute ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Attribute name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'position' => array( - 'description' => __( 'Attribute position.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'visible' => array( - 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'variation' => array( - 'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'view', 'edit' ), - ), - 'options' => array( - 'description' => __( 'List of available term names of the attribute.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'string', - ), - ), - ), - ), - ), - 'default_attributes' => array( - 'description' => __( 'Defaults variation attributes.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Attribute ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'name' => array( - 'description' => __( 'Attribute name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'option' => array( - 'description' => __( 'Selected attribute term name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - 'variations' => array( - 'description' => __( 'List of variations IDs.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'integer', - ), - 'readonly' => true, - ), - 'grouped_products' => array( - 'description' => __( 'List of grouped products ID.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'context' => array( 'view', 'edit' ), - ), - 'menu_order' => array( - 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'meta_data' => array( - 'description' => __( 'Meta data.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Meta ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'key' => array( - 'description' => __( 'Meta key.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'value' => array( - 'description' => __( 'Meta value.', 'woocommerce' ), - 'type' => 'mixed', - 'context' => array( 'view', 'edit' ), - ), - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get the query params for collections of attachments. - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - $params['slug'] = array( - 'description' => __( 'Limit result set to products with a specific slug.', 'woocommerce' ), - 'type' => 'string', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['status'] = array( - 'default' => 'any', - 'description' => __( 'Limit result set to products assigned a specific status.', 'woocommerce' ), - 'type' => 'string', - 'enum' => array_merge( array( 'any' ), array_keys( get_post_statuses() ) ), - 'sanitize_callback' => 'sanitize_key', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['type'] = array( - 'description' => __( 'Limit result set to products assigned a specific type.', 'woocommerce' ), - 'type' => 'string', - 'enum' => array_keys( wc_get_product_types() ), - 'sanitize_callback' => 'sanitize_key', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['sku'] = array( - 'description' => __( 'Limit result set to products with specific SKU(s). Use commas to separate.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['featured'] = array( - 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wc_string_to_bool', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['category'] = array( - 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['tag'] = array( - 'description' => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['shipping_class'] = array( - 'description' => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['attribute'] = array( - 'description' => __( 'Limit result set to products with a specific attribute. Use the taxonomy name/attribute slug.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['attribute_term'] = array( - 'description' => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', - ); - - if ( wc_tax_enabled() ) { - $params['tax_class'] = array( - 'description' => __( 'Limit result set to products with a specific tax class.', 'woocommerce' ), - 'type' => 'string', - 'enum' => array_merge( array( 'standard' ), WC_Tax::get_tax_class_slugs() ), - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - } - - $params['in_stock'] = array( - 'description' => __( 'Limit result set to products in stock or out of stock.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wc_string_to_bool', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['on_sale'] = array( - 'description' => __( 'Limit result set to products on sale.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wc_string_to_bool', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['min_price'] = array( - 'description' => __( 'Limit result set to products based on a minimum price.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['max_price'] = array( - 'description' => __( 'Limit result set to products based on a maximum price.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - - return $params; - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-report-sales-controller.php b/includes/api/class-wc-rest-report-sales-controller.php deleted file mode 100644 index f4a787ece5c6..000000000000 --- a/includes/api/class-wc-rest-report-sales-controller.php +++ /dev/null @@ -1,27 +0,0 @@ -[\w-]+)'; - - /** - * Register routes. - * - * @since 3.0.0 - */ - public function register_routes() { - register_rest_route( - $this->namespace, '/' . $this->rest_base, array( - 'args' => array( - 'group' => array( - 'description' => __( 'Settings group ID.', 'woocommerce' ), - 'type' => 'string', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/batch', array( - 'args' => array( - 'group' => array( - 'description' => __( 'Settings group ID.', 'woocommerce' ), - 'type' => 'string', - ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'batch_items' ), - 'permission_callback' => array( $this, 'update_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_batch_schema' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( - 'args' => array( - 'group' => array( - 'description' => __( 'Settings group ID.', 'woocommerce' ), - 'type' => 'string', - ), - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'string', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Return a single setting. - * - * @since 3.0.0 - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function get_item( $request ) { - $setting = $this->get_setting( $request['group_id'], $request['id'] ); - - if ( is_wp_error( $setting ) ) { - return $setting; - } - - $response = $this->prepare_item_for_response( $setting, $request ); - - return rest_ensure_response( $response ); - } - - /** - * Return all settings in a group. - * - * @since 3.0.0 - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $settings = $this->get_group_settings( $request['group_id'] ); - - if ( is_wp_error( $settings ) ) { - return $settings; - } - - $data = array(); - - foreach ( $settings as $setting_obj ) { - $setting = $this->prepare_item_for_response( $setting_obj, $request ); - $setting = $this->prepare_response_for_collection( $setting ); - if ( $this->is_setting_type_valid( $setting['type'] ) ) { - $data[] = $setting; - } - } - - return rest_ensure_response( $data ); - } - - /** - * Get all settings in a group. - * - * @since 3.0.0 - * @param string $group_id Group ID. - * @return array|WP_Error - */ - public function get_group_settings( $group_id ) { - if ( empty( $group_id ) ) { - return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $settings = apply_filters( 'woocommerce_settings-' . $group_id, array() ); - - if ( empty( $settings ) ) { - return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $filtered_settings = array(); - foreach ( $settings as $setting ) { - $option_key = $setting['option_key']; - $setting = $this->filter_setting( $setting ); - $default = isset( $setting['default'] ) ? $setting['default'] : ''; - // Get the option value. - if ( is_array( $option_key ) ) { - $option = get_option( $option_key[0] ); - $setting['value'] = isset( $option[ $option_key[1] ] ) ? $option[ $option_key[1] ] : $default; - } else { - $admin_setting_value = WC_Admin_Settings::get_option( $option_key, $default ); - $setting['value'] = $admin_setting_value; - } - - if ( 'multi_select_countries' === $setting['type'] ) { - $setting['options'] = WC()->countries->get_countries(); - $setting['type'] = 'multiselect'; - } elseif ( 'single_select_country' === $setting['type'] ) { - $setting['type'] = 'select'; - $setting['options'] = $this->get_countries_and_states(); - } - - $filtered_settings[] = $setting; - } - - return $filtered_settings; - } - - /** - * Returns a list of countries and states for use in the base location setting. - * - * @since 3.0.7 - * @return array Array of states and countries. - */ - private function get_countries_and_states() { - $countries = WC()->countries->get_countries(); - if ( ! $countries ) { - return array(); - } - - $output = array(); - - foreach ( $countries as $key => $value ) { - $states = WC()->countries->get_states( $key ); - if ( $states ) { - foreach ( $states as $state_key => $state_value ) { - $output[ $key . ':' . $state_key ] = $value . ' - ' . $state_value; - } - } else { - $output[ $key ] = $value; - } - } - - return $output; - } - - /** - * Get setting data. - * - * @since 3.0.0 - * @param string $group_id Group ID. - * @param string $setting_id Setting ID. - * @return stdClass|WP_Error - */ - public function get_setting( $group_id, $setting_id ) { - if ( empty( $setting_id ) ) { - return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $settings = $this->get_group_settings( $group_id ); - - if ( is_wp_error( $settings ) ) { - return $settings; - } - - $array_key = array_keys( wp_list_pluck( $settings, 'id' ), $setting_id ); - - if ( empty( $array_key ) ) { - return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $setting = $settings[ $array_key[0] ]; - - if ( ! $this->is_setting_type_valid( $setting['type'] ) ) { - return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - return $setting; - } - - /** - * Bulk create, update and delete items. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array Of WP_Error or WP_REST_Response. - */ - public function batch_items( $request ) { - // Get the request params. - $items = array_filter( $request->get_params() ); - - /* - * Since our batch settings update is group-specific and matches based on the route, - * we inject the URL parameters (containing group) into the batch items - */ - if ( ! empty( $items['update'] ) ) { - $to_update = array(); - foreach ( $items['update'] as $item ) { - $to_update[] = array_merge( $request->get_url_params(), $item ); - } - $request = new WP_REST_Request( $request->get_method() ); - $request->set_body_params( array( 'update' => $to_update ) ); - } - - return parent::batch_items( $request ); - } - - /** - * Update a single setting in a group. - * - * @since 3.0.0 - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function update_item( $request ) { - $setting = $this->get_setting( $request['group_id'], $request['id'] ); - - if ( is_wp_error( $setting ) ) { - return $setting; - } - - if ( is_callable( array( $this, 'validate_setting_' . $setting['type'] . '_field' ) ) ) { - $value = $this->{'validate_setting_' . $setting['type'] . '_field'}( $request['value'], $setting ); - } else { - $value = $this->validate_setting_text_field( $request['value'], $setting ); - } - - if ( is_wp_error( $value ) ) { - return $value; - } - - if ( is_array( $setting['option_key'] ) ) { - $setting['value'] = $value; - $option_key = $setting['option_key']; - $prev = get_option( $option_key[0] ); - $prev[ $option_key[1] ] = $request['value']; - update_option( $option_key[0], $prev ); - } else { - $update_data = array(); - $update_data[ $setting['option_key'] ] = $value; - $setting['value'] = $value; - WC_Admin_Settings::save_fields( array( $setting ), $update_data ); - } - - $response = $this->prepare_item_for_response( $setting, $request ); - - return rest_ensure_response( $response ); - } - - /** - * Prepare a single setting object for response. - * - * @since 3.0.0 - * @param object $item Setting object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $item, $request ) { - unset( $item['option_key'] ); - $data = $this->filter_setting( $item ); - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, empty( $request['context'] ) ? 'view' : $request['context'] ); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $data['id'], $request['group_id'] ) ); - return $response; - } - - /** - * Prepare links for the request. - * - * @since 3.0.0 - * @param string $setting_id Setting ID. - * @param string $group_id Group ID. - * @return array Links for the given setting. - */ - protected function prepare_links( $setting_id, $group_id ) { - $base = str_replace( '(?P[\w-]+)', $group_id, $this->rest_base ); - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $base, $setting_id ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ), - ), - ); - - return $links; - } - - /** - * Makes sure the current user has access to READ the settings APIs. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full data about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Makes sure the current user has access to WRITE the settings APIs. - * - * @since 3.0.0 - * @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; - } - - /** - * Filters out bad values from the settings array/filter so we - * only return known values via the API. - * - * @since 3.0.0 - * @param array $setting Settings. - * @return array - */ - public function filter_setting( $setting ) { - $setting = array_intersect_key( - $setting, - array_flip( array_filter( array_keys( $setting ), array( $this, 'allowed_setting_keys' ) ) ) - ); - - if ( empty( $setting['options'] ) ) { - unset( $setting['options'] ); - } - - if ( 'image_width' === $setting['type'] ) { - $setting = $this->cast_image_width( $setting ); - } - - return $setting; - } - - /** - * For image_width, Crop can return "0" instead of false -- so we want - * to make sure we return these consistently the same we accept them. - * - * @todo remove in 4.0 - * @since 3.0.0 - * @param array $setting Settings. - * @return array - */ - public function cast_image_width( $setting ) { - foreach ( array( 'default', 'value' ) as $key ) { - if ( isset( $setting[ $key ] ) ) { - $setting[ $key ]['width'] = intval( $setting[ $key ]['width'] ); - $setting[ $key ]['height'] = intval( $setting[ $key ]['height'] ); - $setting[ $key ]['crop'] = (bool) $setting[ $key ]['crop']; - } - } - return $setting; - } - - /** - * Callback for allowed keys for each setting response. - * - * @since 3.0.0 - * @param string $key Key to check. - * @return boolean - */ - public function allowed_setting_keys( $key ) { - return in_array( - $key, array( - 'id', - 'label', - 'description', - 'default', - 'tip', - 'placeholder', - 'type', - 'options', - 'value', - 'option_key', - ) - ); - } - - /** - * Boolean for if a setting type is a valid supported setting type. - * - * @since 3.0.0 - * @param string $type Type. - * @return bool - */ - public function is_setting_type_valid( $type ) { - return in_array( - $type, array( - 'text', // Validates with validate_setting_text_field. - 'email', // Validates with validate_setting_text_field. - 'number', // Validates with validate_setting_text_field. - 'color', // Validates with validate_setting_text_field. - 'password', // Validates with validate_setting_text_field. - 'textarea', // Validates with validate_setting_textarea_field. - 'select', // Validates with validate_setting_select_field. - 'multiselect', // Validates with validate_setting_multiselect_field. - 'radio', // Validates with validate_setting_radio_field (-> validate_setting_select_field). - 'checkbox', // Validates with validate_setting_checkbox_field. - 'image_width', // Validates with validate_setting_image_width_field. - 'thumbnail_cropping', // Validates with validate_setting_text_field. - ) - ); - } - - /** - * Get the settings schema, conforming to JSON Schema. - * - * @since 3.0.0 - * @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, - ), - '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', 'thumbnail_cropping' ), - '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 ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-settings-controller.php b/includes/api/class-wc-rest-settings-controller.php index 35d012796ade..e26a6731247c 100644 --- a/includes/api/class-wc-rest-settings-controller.php +++ b/includes/api/class-wc-rest-settings-controller.php @@ -14,219 +14,14 @@ * REST API Settings controller class. * * @package WooCommerce/API - * @extends WC_REST_Controller + * @extends WC_REST_Settings_V2_Controller */ -class WC_REST_Settings_Controller extends WC_REST_Controller { +class WC_REST_Settings_Controller extends WC_REST_Settings_V2_Controller { /** - * WP REST API namespace/version. + * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'settings'; - - /** - * Register routes. - * - * @since 3.0.0 - */ - 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' ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Get all settings groups items. - * - * @since 3.0.0 - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $groups = apply_filters( 'woocommerce_settings_groups', array() ); - if ( empty( $groups ) ) { - return new WP_Error( 'rest_setting_groups_empty', __( 'No setting groups have been registered.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - $defaults = $this->group_defaults(); - $filtered_groups = array(); - foreach ( $groups as $group ) { - $sub_groups = array(); - foreach ( $groups as $_group ) { - if ( ! empty( $_group['parent_id'] ) && $group['id'] === $_group['parent_id'] ) { - $sub_groups[] = $_group['id']; - } - } - $group['sub_groups'] = $sub_groups; - - $group = wp_parse_args( $group, $defaults ); - if ( ! is_null( $group['id'] ) && ! is_null( $group['label'] ) ) { - $group_obj = $this->filter_group( $group ); - $group_data = $this->prepare_item_for_response( $group_obj, $request ); - $group_data = $this->prepare_response_for_collection( $group_data ); - - $filtered_groups[] = $group_data; - } - } - - $response = rest_ensure_response( $filtered_groups ); - return $response; - } - - /** - * Prepare links for the request. - * - * @param string $group_id Group ID. - * @return array Links for the given group. - */ - protected function prepare_links( $group_id ) { - $base = '/' . $this->namespace . '/' . $this->rest_base; - $links = array( - 'options' => array( - 'href' => rest_url( trailingslashit( $base ) . $group_id ), - ), - ); - - return $links; - } - - /** - * Prepare a report sales object for serialization. - * - * @since 3.0.0 - * @param array $item Group object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $item, $request ) { - $context = empty( $request['context'] ) ? 'view' : $request['context']; - $data = $this->add_additional_fields_to_object( $item, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $item['id'] ) ); - - return $response; - } - - /** - * Filters out bad values from the groups array/filter so we - * only return known values via the API. - * - * @since 3.0.0 - * @param array $group Group. - * @return array - */ - public function filter_group( $group ) { - return array_intersect_key( - $group, - array_flip( array_filter( array_keys( $group ), array( $this, 'allowed_group_keys' ) ) ) - ); - } - - /** - * Callback for allowed keys for each group response. - * - * @since 3.0.0 - * @param string $key Key to check. - * @return boolean - */ - public function allowed_group_keys( $key ) { - return in_array( $key, array( 'id', 'label', 'description', 'parent_id', 'sub_groups' ) ); - } - - /** - * Returns default settings for groups. null means the field is required. - * - * @since 3.0.0 - * @return array - */ - protected function group_defaults() { - return array( - 'id' => null, - 'label' => null, - 'description' => '', - 'parent_id' => '', - 'sub_groups' => array(), - ); - } - - /** - * Makes sure the current user has access to READ the settings APIs. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full data about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Get the groups schema, conforming to JSON Schema. - * - * @since 3.0.0 - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'setting_group', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'A unique identifier that can be used to link settings together.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'label' => array( - 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'parent_id' => array( - 'description' => __( 'ID of parent grouping.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'sub_groups' => array( - 'description' => __( 'IDs for settings sub groups.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-shipping-methods-controller.php b/includes/api/class-wc-rest-shipping-methods-controller.php index ab1b8acffc86..075e4f777cd8 100644 --- a/includes/api/class-wc-rest-shipping-methods-controller.php +++ b/includes/api/class-wc-rest-shipping-methods-controller.php @@ -14,218 +14,14 @@ * Shipping methods controller class. * * @package WooCommerce/API - * @extends WC_REST_Controller + * @extends WC_REST_Shipping_Methods_V2_Controller */ -class WC_REST_Shipping_Methods_Controller extends WC_REST_Controller { +class WC_REST_Shipping_Methods_Controller extends WC_REST_Shipping_Methods_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'shipping_methods'; - - /** - * Register the route for /shipping_methods and /shipping_methods/ - */ - 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(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'string', - ), - ), - 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' ) ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Check whether a given request has permission to view shipping methods. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'shipping_methods', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Check if a given request has access to read a shipping method. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'shipping_methods', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Get shipping methods. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $wc_shipping = WC_Shipping::instance(); - $response = array(); - foreach ( $wc_shipping->get_shipping_methods() as $id => $shipping_method ) { - $method = $this->prepare_item_for_response( $shipping_method, $request ); - $method = $this->prepare_response_for_collection( $method ); - $response[] = $method; - } - return rest_ensure_response( $response ); - } - - /** - * Get a single Shipping Method. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $wc_shipping = WC_Shipping::instance(); - $methods = $wc_shipping->get_shipping_methods(); - if ( empty( $methods[ $request['id'] ] ) ) { - return new WP_Error( 'woocommerce_rest_shipping_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $method = $methods[ $request['id'] ]; - $response = $this->prepare_item_for_response( $method, $request ); - - return rest_ensure_response( $response ); - } - - /** - * Prepare a shipping method for response. - * - * @param WC_Shipping_Method $method Shipping method object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $method, $request ) { - $data = array( - 'id' => $method->id, - 'title' => $method->method_title, - 'description' => $method->method_description, - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $method, $request ) ); - - /** - * Filter shipping methods object returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WC_Shipping_Method $method Shipping method object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_shipping_method', $response, $method, $request ); - } - - /** - * Prepare links for the request. - * - * @param WC_Shipping_Method $method Shipping method object. - * @param WP_REST_Request $request Request object. - * @return array - */ - protected function prepare_links( $method, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $method->id ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - return $links; - } - - /** - * Get the shipping method schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'shipping_method', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Method ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'title' => array( - 'description' => __( 'Shipping method title.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'Shipping method description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Get any query params needed. - * - * @return array - */ - public function get_collection_params() { - return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-shipping-zone-locations-controller.php b/includes/api/class-wc-rest-shipping-zone-locations-controller.php index fb97a818a821..cba326a80ca8 100644 --- a/includes/api/class-wc-rest-shipping-zone-locations-controller.php +++ b/includes/api/class-wc-rest-shipping-zone-locations-controller.php @@ -14,177 +14,14 @@ * REST API Shipping Zone Locations class. * * @package WooCommerce/API - * @extends WC_REST_Shipping_Zones_Controller_Base + * @extends WC_REST_Shipping_Zone_Locations_V2_Controller */ -class WC_REST_Shipping_Zone_Locations_Controller extends WC_REST_Shipping_Zones_Controller_Base { +class WC_REST_Shipping_Zone_Locations_Controller extends WC_REST_Shipping_Zone_Locations_V2_Controller { /** - * Register the routes for Shipping Zone Locations. - */ - public function register_routes() { - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/locations', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique ID for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_items' ), - 'permission_callback' => array( $this, 'update_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Get all Shipping Zone Locations. + * Endpoint namespace. * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error + * @var string */ - public function get_items( $request ) { - $zone = $this->get_zone( (int) $request['id'] ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $locations = $zone->get_zone_locations(); - $data = array(); - - foreach ( $locations as $location_obj ) { - $location = $this->prepare_item_for_response( $location_obj, $request ); - $location = $this->prepare_response_for_collection( $location ); - $data[] = $location; - } - - return rest_ensure_response( $data ); - } - - /** - * Update all Shipping Zone Locations. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function update_items( $request ) { - $zone = $this->get_zone( (int) $request['id'] ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - if ( 0 === $zone->get_id() ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_locations_invalid_zone', __( 'The "locations not covered by your other zones" zone cannot be updated.', 'woocommerce' ), array( 'status' => 403 ) ); - } - - $raw_locations = $request->get_json_params(); - $locations = array(); - - foreach ( (array) $raw_locations as $raw_location ) { - if ( empty( $raw_location['code'] ) ) { - continue; - } - - $type = ! empty( $raw_location['type'] ) ? sanitize_text_field( $raw_location['type'] ) : 'country'; - - if ( ! in_array( $type, array( 'postcode', 'state', 'country', 'continent' ), true ) ) { - continue; - } - - $locations[] = array( - 'code' => sanitize_text_field( $raw_location['code'] ), - 'type' => sanitize_text_field( $type ), - ); - } - - $zone->set_locations( $locations ); - $zone->save(); - - return $this->get_items( $request ); - } - - /** - * Prepare the Shipping Zone Location for the REST response. - * - * @param array $item Shipping Zone Location. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response - */ - public function prepare_item_for_response( $item, $request ) { - $context = empty( $request['context'] ) ? 'view' : $request['context']; - $data = $this->add_additional_fields_to_object( $item, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( (int) $request['id'] ) ); - - return $response; - } - - /** - * Prepare links for the request. - * - * @param int $zone_id Given Shipping Zone ID. - * @return array Links for the given Shipping Zone Location. - */ - protected function prepare_links( $zone_id ) { - $base = '/' . $this->namespace . '/' . $this->rest_base . '/' . $zone_id; - $links = array( - 'collection' => array( - 'href' => rest_url( $base . '/locations' ), - ), - 'describes' => array( - 'href' => rest_url( $base ), - ), - ); - - return $links; - } - - /** - * Get the Shipping Zone Locations schema, conforming to JSON Schema - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'shipping_zone_location', - 'type' => 'object', - 'properties' => array( - 'code' => array( - 'description' => __( 'Shipping zone location code.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'type' => array( - 'description' => __( 'Shipping zone location type.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'country', - 'enum' => array( - 'postcode', - 'state', - 'country', - 'continent', - ), - 'context' => array( 'view', 'edit' ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-shipping-zone-methods-controller.php b/includes/api/class-wc-rest-shipping-zone-methods-controller.php index c0bc3c2d6a3e..6058e95baaf5 100644 --- a/includes/api/class-wc-rest-shipping-zone-methods-controller.php +++ b/includes/api/class-wc-rest-shipping-zone-methods-controller.php @@ -14,528 +14,14 @@ * REST API Shipping Zone Methods class. * * @package WooCommerce/API - * @extends WC_REST_Shipping_Zones_Controller_Base + * @extends WC_REST_Shipping_Zone_Methods_V2_Controller */ -class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Controller_Base { +class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zone_Methods_V2_Controller { /** - * Register the routes for Shipping Zone Methods. - */ - public function register_routes() { - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/methods', array( - 'args' => array( - 'zone_id' => array( - 'description' => __( 'Unique ID for the zone.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - 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( - 'method_id' => array( - 'required' => true, - 'readonly' => false, - 'description' => __( 'Shipping method ID.', 'woocommerce' ), - ), - ) - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/methods/(?P[\d]+)', array( - 'args' => array( - 'zone_id' => array( - 'description' => __( 'Unique ID for the zone.', 'woocommerce' ), - 'type' => 'integer', - ), - 'instance_id' => array( - 'description' => __( 'Unique ID for the instance.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_items_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_items_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' ), - ) - ); - } - - /** - * Get a single Shipping Zone Method. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $zone = $this->get_zone( $request['zone_id'] ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $instance_id = (int) $request['instance_id']; - $methods = $zone->get_shipping_methods(); - $method = false; - - foreach ( $methods as $method_obj ) { - if ( $instance_id === $method_obj->instance_id ) { - $method = $method_obj; - break; - } - } - - if ( false === $method ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $data = $this->prepare_item_for_response( $method, $request ); - - return rest_ensure_response( $data ); - } - - /** - * Get all Shipping Zone Methods. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function get_items( $request ) { - $zone = $this->get_zone( $request['zone_id'] ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $methods = $zone->get_shipping_methods(); - $data = array(); - - foreach ( $methods as $method_obj ) { - $method = $this->prepare_item_for_response( $method_obj, $request ); - $data[] = $method; - } - - return rest_ensure_response( $data ); - } - - /** - * Create a new shipping zone method instance. + * Endpoint namespace. * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error + * @var string */ - public function create_item( $request ) { - $method_id = $request['method_id']; - $zone = $this->get_zone( $request['zone_id'] ); - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $instance_id = $zone->add_shipping_method( $method_id ); - $methods = $zone->get_shipping_methods(); - $method = false; - foreach ( $methods as $method_obj ) { - if ( $instance_id === $method_obj->instance_id ) { - $method = $method_obj; - break; - } - } - - if ( false === $method ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( 'Resource cannot be created.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - $method = $this->update_fields( $instance_id, $method, $request ); - if ( is_wp_error( $method ) ) { - return $method; - } - - $data = $this->prepare_item_for_response( $method, $request ); - return rest_ensure_response( $data ); - } - - /** - * Delete a shipping method instance. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function delete_item( $request ) { - $zone = $this->get_zone( $request['zone_id'] ); - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $instance_id = (int) $request['instance_id']; - $force = $request['force']; - - $methods = $zone->get_shipping_methods(); - $method = false; - - foreach ( $methods as $method_obj ) { - if ( $instance_id === $method_obj->instance_id ) { - $method = $method_obj; - break; - } - } - - if ( false === $method ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $method = $this->update_fields( $instance_id, $method, $request ); - if ( is_wp_error( $method ) ) { - return $method; - } - - $request->set_param( 'context', 'view' ); - $response = $this->prepare_item_for_response( $method, $request ); - - // Actually delete. - if ( $force ) { - $zone->delete_shipping_method( $instance_id ); - } else { - return new WP_Error( 'rest_trash_not_supported', __( 'Shipping methods do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); - } - - /** - * Fires after a product review is deleted via the REST API. - * - * @param object $method - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( 'rest_delete_product_review', $method, $response, $request ); - - return $response; - } - - /** - * Update A Single Shipping Zone Method. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function update_item( $request ) { - $zone = $this->get_zone( $request['zone_id'] ); - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $instance_id = (int) $request['instance_id']; - $methods = $zone->get_shipping_methods(); - $method = false; - - foreach ( $methods as $method_obj ) { - if ( $instance_id === $method_obj->instance_id ) { - $method = $method_obj; - break; - } - } - - if ( false === $method ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $method = $this->update_fields( $instance_id, $method, $request ); - if ( is_wp_error( $method ) ) { - return $method; - } - - $data = $this->prepare_item_for_response( $method, $request ); - return rest_ensure_response( $data ); - } - - /** - * Updates settings, order, and enabled status on create. - * - * @param int $instance_id Instance ID. - * @param WC_Shipping_Method $method Shipping method data. - * @param WP_REST_Request $request Request data. - * - * @return WC_Shipping_Method - */ - public function update_fields( $instance_id, $method, $request ) { - global $wpdb; - - // Update settings if present. - if ( isset( $request['settings'] ) ) { - $method->init_instance_settings(); - $instance_settings = $method->instance_settings; - $errors_found = false; - foreach ( $method->get_instance_form_fields() as $key => $field ) { - if ( isset( $request['settings'][ $key ] ) ) { - if ( is_callable( array( $this, 'validate_setting_' . $field['type'] . '_field' ) ) ) { - $value = $this->{'validate_setting_' . $field['type'] . '_field'}( $request['settings'][ $key ], $field ); - } else { - $value = $this->validate_setting_text_field( $request['settings'][ $key ], $field ); - } - if ( is_wp_error( $value ) ) { - $errors_found = true; - break; - } - $instance_settings[ $key ] = $value; - } - } - - if ( $errors_found ) { - return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - update_option( $method->get_instance_option_key(), apply_filters( 'woocommerce_shipping_' . $method->id . '_instance_settings_values', $instance_settings, $method ) ); - } - - // Update order. - if ( isset( $request['order'] ) ) { - $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'method_order' => absint( $request['order'] ) ), array( 'instance_id' => absint( $instance_id ) ) ); - $method->method_order = absint( $request['order'] ); - } - - // Update if this method is enabled or not. - if ( isset( $request['enabled'] ) ) { - if ( $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'is_enabled' => $request['enabled'] ), array( 'instance_id' => absint( $instance_id ) ) ) ) { - do_action( 'woocommerce_shipping_zone_method_status_toggled', $instance_id, $method->id, $request['zone_id'], $request['enabled'] ); - $method->enabled = ( true === $request['enabled'] ? 'yes' : 'no' ); - } - } - - return $method; - } - - /** - * Prepare the Shipping Zone Method for the REST response. - * - * @param array $item Shipping Zone Method. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response - */ - public function prepare_item_for_response( $item, $request ) { - $method = array( - 'id' => $item->instance_id, - 'instance_id' => $item->instance_id, - 'title' => $item->instance_settings['title'], - 'order' => $item->method_order, - 'enabled' => ( 'yes' === $item->enabled ), - 'method_id' => $item->id, - 'method_title' => $item->method_title, - 'method_description' => $item->method_description, - 'settings' => $this->get_settings( $item ), - ); - - $context = empty( $request['context'] ) ? 'view' : $request['context']; - $data = $this->add_additional_fields_to_object( $method, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $request['zone_id'], $item->instance_id ) ); - - $response = $this->prepare_response_for_collection( $response ); - - return $response; - } - - /** - * Return settings associated with this shipping zone method instance. - * - * @param WC_Shipping_Method $item Shipping method data. - * - * @return array - */ - public function get_settings( $item ) { - $item->init_instance_settings(); - $settings = array(); - foreach ( $item->get_instance_form_fields() as $id => $field ) { - $data = array( - 'id' => $id, - 'label' => $field['title'], - 'description' => empty( $field['description'] ) ? '' : $field['description'], - 'type' => $field['type'], - 'value' => $item->instance_settings[ $id ], - 'default' => empty( $field['default'] ) ? '' : $field['default'], - 'tip' => empty( $field['description'] ) ? '' : $field['description'], - 'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'], - ); - if ( ! empty( $field['options'] ) ) { - $data['options'] = $field['options']; - } - $settings[ $id ] = $data; - } - return $settings; - } - - /** - * Prepare links for the request. - * - * @param int $zone_id Given Shipping Zone ID. - * @param int $instance_id Given Shipping Zone Method Instance ID. - * @return array Links for the given Shipping Zone Method. - */ - protected function prepare_links( $zone_id, $instance_id ) { - $base = '/' . $this->namespace . '/' . $this->rest_base . '/' . $zone_id; - $links = array( - 'self' => array( - 'href' => rest_url( $base . '/methods/' . $instance_id ), - ), - 'collection' => array( - 'href' => rest_url( $base . '/methods' ), - ), - 'describes' => array( - 'href' => rest_url( $base ), - ), - ); - - return $links; - } - - /** - * Get the Shipping Zone Methods schema, conforming to JSON Schema - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'shipping_zone_method', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Shipping method instance ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'instance_id' => array( - 'description' => __( 'Shipping method instance ID.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'title' => array( - 'description' => __( 'Shipping method customer facing title.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'order' => array( - 'description' => __( 'Shipping method sort order.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - 'enabled' => array( - 'description' => __( 'Shipping method enabled status.', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view', 'edit' ), - ), - 'method_id' => array( - 'description' => __( 'Shipping method ID.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'method_title' => array( - 'description' => __( 'Shipping method title.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'method_description' => array( - 'description' => __( 'Shipping method description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'settings' => array( - 'description' => __( 'Shipping method settings.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'id' => array( - 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'label' => array( - 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'type' => array( - 'description' => __( 'Type of setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), - 'readonly' => true, - ), - 'value' => array( - 'description' => __( 'Setting value.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'default' => array( - 'description' => __( 'Default value for the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'tip' => array( - 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'placeholder' => array( - 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-shipping-zones-controller.php b/includes/api/class-wc-rest-shipping-zones-controller.php index d2d8e5b2ade9..dd766a3291c5 100644 --- a/includes/api/class-wc-rest-shipping-zones-controller.php +++ b/includes/api/class-wc-rest-shipping-zones-controller.php @@ -14,291 +14,14 @@ * REST API Shipping Zones class. * * @package WooCommerce/API - * @extends WC_REST_Shipping_Zones_Controller_Base + * @extends WC_REST_Shipping_Zones_V2_Controller */ -class WC_REST_Shipping_Zones_Controller extends WC_REST_Shipping_Zones_Controller_Base { +class WC_REST_Shipping_Zones_Controller extends WC_REST_Shipping_Zones_V2_Controller { /** - * Register the routes for Shipping Zones. - */ - 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' ), - ), - 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( - 'name' => array( - 'required' => true, - 'type' => 'string', - 'description' => __( 'Shipping zone name.', 'woocommerce' ), - ), - ) - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d-]+)', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique ID for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_items_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_items_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' ), - ) - ); - } - - /** - * Get a single Shipping Zone. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $zone = $this->get_zone( $request->get_param( 'id' ) ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $data = $zone->get_data(); - $data = $this->prepare_item_for_response( $data, $request ); - $data = $this->prepare_response_for_collection( $data ); - - return rest_ensure_response( $data ); - } - - /** - * Get all Shipping Zones. - * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response - */ - public function get_items( $request ) { - $rest_of_the_world = WC_Shipping_Zones::get_zone_by( 'zone_id', 0 ); - - $zones = WC_Shipping_Zones::get_zones(); - array_unshift( $zones, $rest_of_the_world->get_data() ); - $data = array(); - - foreach ( $zones as $zone_obj ) { - $zone = $this->prepare_item_for_response( $zone_obj, $request ); - $zone = $this->prepare_response_for_collection( $zone ); - $data[] = $zone; - } - - return rest_ensure_response( $data ); - } - - /** - * Create a single Shipping Zone. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function create_item( $request ) { - $zone = new WC_Shipping_Zone( null ); - - if ( ! is_null( $request->get_param( 'name' ) ) ) { - $zone->set_zone_name( $request->get_param( 'name' ) ); - } - - if ( ! is_null( $request->get_param( 'order' ) ) ) { - $zone->set_zone_order( $request->get_param( 'order' ) ); - } - - $zone->save(); - - if ( $zone->get_id() !== 0 ) { - $request->set_param( 'id', $zone->get_id() ); - $response = $this->get_item( $request ); - $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $zone->get_id() ) ) ); - return $response; - } else { - return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( "Resource cannot be created. Check to make sure 'order' and 'name' are present.", 'woocommerce' ), array( 'status' => 500 ) ); - } - } - - /** - * Update a single Shipping Zone. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function update_item( $request ) { - $zone = $this->get_zone( $request->get_param( 'id' ) ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - if ( 0 === $zone->get_id() ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_invalid_zone', __( 'The "locations not covered by your other zones" zone cannot be updated.', 'woocommerce' ), array( 'status' => 403 ) ); - } - - $zone_changed = false; - - if ( ! is_null( $request->get_param( 'name' ) ) ) { - $zone->set_zone_name( $request->get_param( 'name' ) ); - $zone_changed = true; - } - - if ( ! is_null( $request->get_param( 'order' ) ) ) { - $zone->set_zone_order( $request->get_param( 'order' ) ); - $zone_changed = true; - } - - if ( $zone_changed ) { - $zone->save(); - } - - return $this->get_item( $request ); - } - - /** - * Delete a single Shipping Zone. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function delete_item( $request ) { - $zone = $this->get_zone( $request->get_param( 'id' ) ); - - if ( is_wp_error( $zone ) ) { - return $zone; - } - - $force = $request['force']; - - $response = $this->get_item( $request ); - - if ( $force ) { - $zone->delete(); - } else { - return new WP_Error( 'rest_trash_not_supported', __( 'Shipping zones do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); - } - - return $response; - } - - /** - * Prepare the Shipping Zone for the REST response. + * Endpoint namespace. * - * @param array $item Shipping Zone. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response + * @var string */ - public function prepare_item_for_response( $item, $request ) { - $data = array( - 'id' => (int) $item['id'], - 'name' => $item['zone_name'], - 'order' => (int) $item['zone_order'], - ); - - $context = empty( $request['context'] ) ? 'view' : $request['context']; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $data['id'] ) ); - - return $response; - } - - /** - * Prepare links for the request. - * - * @param int $zone_id Given Shipping Zone ID. - * @return array Links for the given Shipping Zone. - */ - protected function prepare_links( $zone_id ) { - $base = '/' . $this->namespace . '/' . $this->rest_base; - $links = array( - 'self' => array( - 'href' => rest_url( trailingslashit( $base ) . $zone_id ), - ), - 'collection' => array( - 'href' => rest_url( $base ), - ), - 'describedby' => array( - 'href' => rest_url( trailingslashit( $base ) . $zone_id . '/locations' ), - ), - ); - - return $links; - } - - /** - * Get the Shipping Zones schema, conforming to JSON Schema - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'shipping_zone', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Shipping zone name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'order' => array( - 'description' => __( 'Shipping zone order.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-system-status-controller.php b/includes/api/class-wc-rest-system-status-controller.php index 4404f4f11d6c..78b37fbe1623 100644 --- a/includes/api/class-wc-rest-system-status-controller.php +++ b/includes/api/class-wc-rest-system-status-controller.php @@ -14,1054 +14,14 @@ * System status controller class. * * @package WooCommerce/API - * @extends WC_REST_Controller + * @extends WC_REST_System_Status_V2_Controller */ -class WC_REST_System_Status_Controller extends WC_REST_Controller { +class WC_REST_System_Status_Controller extends WC_REST_System_Status_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'system_status'; - - /** - * Register the route for /system_status - */ - 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(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Check whether a given request has permission to view system status. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Get a system status info, by section. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $schema = $this->get_item_schema(); - $mappings = $this->get_item_mappings(); - $response = array(); - - foreach ( $mappings as $section => $values ) { - foreach ( $values as $key => $value ) { - if ( isset( $schema['properties'][ $section ]['properties'][ $key ]['type'] ) ) { - settype( $values[ $key ], $schema['properties'][ $section ]['properties'][ $key ]['type'] ); - } - } - settype( $values, $schema['properties'][ $section ]['type'] ); - $response[ $section ] = $values; - } - - $response = $this->prepare_item_for_response( $response, $request ); - - return rest_ensure_response( $response ); - } - - /** - * Get the system status schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'system_status', - 'type' => 'object', - 'properties' => array( - 'environment' => array( - 'description' => __( 'Environment.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'home_url' => array( - 'description' => __( 'Home URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'site_url' => array( - 'description' => __( 'Site URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wc_version' => array( - 'description' => __( 'WooCommerce version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'log_directory' => array( - 'description' => __( 'Log directory.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'log_directory_writable' => array( - 'description' => __( 'Is log directory writable?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wp_version' => array( - 'description' => __( 'WordPress version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wp_multisite' => array( - 'description' => __( 'Is WordPress multisite?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wp_memory_limit' => array( - 'description' => __( 'WordPress memory limit.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wp_debug_mode' => array( - 'description' => __( 'Is WordPress debug mode active?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'wp_cron' => array( - 'description' => __( 'Are WordPress cron jobs enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'language' => array( - 'description' => __( 'WordPress language.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'server_info' => array( - 'description' => __( 'Server info.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'php_version' => array( - 'description' => __( 'PHP version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'php_post_max_size' => array( - 'description' => __( 'PHP post max size.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'php_max_execution_time' => array( - 'description' => __( 'PHP max execution time.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'php_max_input_vars' => array( - 'description' => __( 'PHP max input vars.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'curl_version' => array( - 'description' => __( 'cURL version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'suhosin_installed' => array( - 'description' => __( 'Is SUHOSIN installed?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'max_upload_size' => array( - 'description' => __( 'Max upload size.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'mysql_version' => array( - 'description' => __( 'MySQL version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'default_timezone' => array( - 'description' => __( 'Default timezone.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'fsockopen_or_curl_enabled' => array( - 'description' => __( 'Is fsockopen/cURL enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'soapclient_enabled' => array( - 'description' => __( 'Is SoapClient class enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'domdocument_enabled' => array( - 'description' => __( 'Is DomDocument class enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'gzip_enabled' => array( - 'description' => __( 'Is GZip enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'mbstring_enabled' => array( - 'description' => __( 'Is mbstring enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'remote_post_successful' => array( - 'description' => __( 'Remote POST successful?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'remote_post_response' => array( - 'description' => __( 'Remote POST response.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'remote_get_successful' => array( - 'description' => __( 'Remote GET successful?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'remote_get_response' => array( - 'description' => __( 'Remote GET response.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ), - 'database' => array( - 'description' => __( 'Database.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'wc_database_version' => array( - 'description' => __( 'WC database version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'database_prefix' => array( - 'description' => __( 'Database prefix.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'maxmind_geoip_database' => array( - 'description' => __( 'MaxMind GeoIP database.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'database_tables' => array( - 'description' => __( 'Database tables.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - ), - ), - 'active_plugins' => array( - 'description' => __( 'Active plugins.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'theme' => array( - 'description' => __( 'Theme.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'name' => array( - 'description' => __( 'Theme name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'version' => array( - 'description' => __( 'Theme version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'version_latest' => array( - 'description' => __( 'Latest version of theme.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'author_url' => array( - 'description' => __( 'Theme author URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'is_child_theme' => array( - 'description' => __( 'Is this theme a child theme?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'has_woocommerce_support' => array( - 'description' => __( 'Does the theme declare WooCommerce support?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'has_woocommerce_file' => array( - 'description' => __( 'Does the theme have a woocommerce.php file?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'has_outdated_templates' => array( - 'description' => __( 'Does this theme have outdated templates?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'overrides' => array( - 'description' => __( 'Template overrides.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'parent_name' => array( - 'description' => __( 'Parent theme name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'parent_version' => array( - 'description' => __( 'Parent theme version.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'parent_author_url' => array( - 'description' => __( 'Parent theme author URL.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ), - 'settings' => array( - 'description' => __( 'Settings.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'api_enabled' => array( - 'description' => __( 'REST API enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'force_ssl' => array( - 'description' => __( 'SSL forced?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'currency' => array( - 'description' => __( 'Currency.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'currency_symbol' => array( - 'description' => __( 'Currency symbol.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'currency_position' => array( - 'description' => __( 'Currency position.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'thousand_separator' => array( - 'description' => __( 'Thousand separator.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'decimal_separator' => array( - 'description' => __( 'Decimal separator.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'number_of_decimals' => array( - 'description' => __( 'Number of decimals.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'geolocation_enabled' => array( - 'description' => __( 'Geolocation enabled?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'taxonomies' => array( - 'description' => __( 'Taxonomy terms for product/order statuses.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'product_visibility_terms' => array( - 'description' => __( 'Terms in the product visibility taxonomy.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - ), - ), - 'security' => array( - 'description' => __( 'Security.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view' ), - 'readonly' => true, - 'properties' => array( - 'secure_connection' => array( - 'description' => __( 'Is the connection to your store secure?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'hide_errors' => array( - 'description' => __( 'Hide errors from visitors?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'view' ), - 'readonly' => true, - ), - ), - ), - 'pages' => array( - 'description' => __( 'WooCommerce pages.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Return an array of sections and the data associated with each. - * - * @return array - */ - public function get_item_mappings() { - return array( - 'environment' => $this->get_environment_info(), - 'database' => $this->get_database_info(), - 'active_plugins' => $this->get_active_plugins(), - 'theme' => $this->get_theme_info(), - 'settings' => $this->get_settings(), - 'security' => $this->get_security_info(), - 'pages' => $this->get_pages(), - ); - } - - /** - * Get array of environment information. Includes thing like software - * versions, and various server settings. - * - * @return array - */ - public function get_environment_info() { - global $wpdb; - - // Figure out cURL version, if installed. - $curl_version = ''; - if ( function_exists( 'curl_version' ) ) { - $curl_version = curl_version(); - $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version']; - } - - // WP memory limit. - $wp_memory_limit = wc_let_to_num( WP_MEMORY_LIMIT ); - if ( function_exists( 'memory_get_usage' ) ) { - $wp_memory_limit = max( $wp_memory_limit, wc_let_to_num( @ini_get( 'memory_limit' ) ) ); - } - - // Test POST requests. - $post_response = wp_safe_remote_post( - 'https://www.paypal.com/cgi-bin/webscr', - array( - 'timeout' => 10, - 'user-agent' => 'WooCommerce/' . WC()->version, - 'httpversion' => '1.1', - 'body' => array( - 'cmd' => '_notify-validate', - ), - ) - ); - $post_response_successful = false; - if ( ! is_wp_error( $post_response ) && $post_response['response']['code'] >= 200 && $post_response['response']['code'] < 300 ) { - $post_response_successful = true; - } - - // Test GET requests. - $get_response = wp_safe_remote_get( 'https://woocommerce.com/wc-api/product-key-api?request=ping&network=' . ( is_multisite() ? '1' : '0' ) ); - $get_response_successful = false; - if ( ! is_wp_error( $post_response ) && $post_response['response']['code'] >= 200 && $post_response['response']['code'] < 300 ) { - $get_response_successful = true; - } - - // Return all environment info. Described by JSON Schema. - return array( - 'home_url' => get_option( 'home' ), - 'site_url' => get_option( 'siteurl' ), - 'version' => WC()->version, - 'log_directory' => WC_LOG_DIR, - 'log_directory_writable' => (bool) @fopen( WC_LOG_DIR . 'test-log.log', 'a' ), - 'wp_version' => get_bloginfo( 'version' ), - 'wp_multisite' => is_multisite(), - 'wp_memory_limit' => $wp_memory_limit, - 'wp_debug_mode' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), - 'wp_cron' => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ), - 'language' => get_locale(), - 'external_object_cache' => wp_using_ext_object_cache(), - 'server_info' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? wc_clean( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '', - 'php_version' => phpversion(), - 'php_post_max_size' => wc_let_to_num( ini_get( 'post_max_size' ) ), - 'php_max_execution_time' => ini_get( 'max_execution_time' ), - 'php_max_input_vars' => ini_get( 'max_input_vars' ), - 'curl_version' => $curl_version, - 'suhosin_installed' => extension_loaded( 'suhosin' ), - 'max_upload_size' => wp_max_upload_size(), - 'mysql_version' => ( ! empty( $wpdb->is_mysql ) ? $wpdb->db_version() : '' ), - 'default_timezone' => date_default_timezone_get(), - 'fsockopen_or_curl_enabled' => ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ), - 'soapclient_enabled' => class_exists( 'SoapClient' ), - 'domdocument_enabled' => class_exists( 'DOMDocument' ), - 'gzip_enabled' => is_callable( 'gzopen' ), - 'mbstring_enabled' => extension_loaded( 'mbstring' ), - 'remote_post_successful' => $post_response_successful, - 'remote_post_response' => ( is_wp_error( $post_response ) ? $post_response->get_error_message() : $post_response['response']['code'] ), - 'remote_get_successful' => $get_response_successful, - 'remote_get_response' => ( is_wp_error( $get_response ) ? $get_response->get_error_message() : $get_response['response']['code'] ), - ); - } - - /** - * Add prefix to table. - * - * @param string $table Table name. - * @return stromg - */ - protected function add_db_table_prefix( $table ) { - global $wpdb; - return $wpdb->prefix . $table; - } - - /** - * Get array of database information. Version, prefix, and table existence. - * - * @return array - */ - public function get_database_info() { - global $wpdb; - - $database_table_sizes = $wpdb->get_results( - $wpdb->prepare( - "SELECT - table_name AS 'name', - round( ( data_length / 1024 / 1024 ), 2 ) 'data', - round( ( index_length / 1024 / 1024 ), 2 ) 'index' - FROM information_schema.TABLES - WHERE table_schema = %s - ORDER BY name ASC;", - DB_NAME - ) - ); - - // WC Core tables to check existence of. - $core_tables = apply_filters( - 'woocommerce_database_tables', - array( - 'woocommerce_sessions', - 'woocommerce_api_keys', - 'woocommerce_attribute_taxonomies', - 'woocommerce_downloadable_product_permissions', - 'woocommerce_order_items', - 'woocommerce_order_itemmeta', - 'woocommerce_tax_rates', - 'woocommerce_tax_rate_locations', - 'woocommerce_shipping_zones', - 'woocommerce_shipping_zone_locations', - 'woocommerce_shipping_zone_methods', - 'woocommerce_payment_tokens', - 'woocommerce_payment_tokenmeta', - 'woocommerce_log', - ) - ); - - if ( get_option( 'db_version' ) < 34370 ) { - $core_tables[] = 'woocommerce_termmeta'; - } - - /** - * Adding the prefix to the tables array, for backwards compatibility. - * - * If we changed the tables above to include the prefix, then any filters against that table could break. - */ - $core_tables = array_map( array( $this, 'add_db_table_prefix' ), $core_tables ); - - /** - * Organize WooCommerce and non-WooCommerce tables separately for display purposes later. - * - * To ensure we include all WC tables, even if they do not exist, pre-populate the WC array with all the tables. - */ - $tables = array( - 'woocommerce' => array_fill_keys( $core_tables, false ), - 'other' => array(), - ); - - $database_size = array( - 'data' => 0, - 'index' => 0, - ); - - foreach ( $database_table_sizes as $table ) { - $table_type = in_array( $table->name, $core_tables ) ? 'woocommerce' : 'other'; - - $tables[ $table_type ][ $table->name ] = array( - 'data' => $table->data, - 'index' => $table->index, - ); - - $database_size['data'] += $table->data; - $database_size['index'] += $table->index; - } - - // Return all database info. Described by JSON Schema. - return array( - 'wc_database_version' => get_option( 'woocommerce_db_version' ), - 'database_prefix' => $wpdb->prefix, - 'maxmind_geoip_database' => WC_Geolocation::get_local_database_path(), - 'database_tables' => $tables, - 'database_size' => $database_size, - ); - } - - /** - * Get array of counts of objects. Orders, products, etc. - * - * @return array - */ - public function get_post_type_counts() { - global $wpdb; - - $post_type_counts = $wpdb->get_results( "SELECT post_type AS 'type', count(1) AS 'count' FROM {$wpdb->posts} GROUP BY post_type;" ); - - return is_array( $post_type_counts ) ? $post_type_counts : array(); - } - - /** - * Get a list of plugins active on the site. - * - * @return array - */ - public function get_active_plugins() { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - require_once ABSPATH . 'wp-admin/includes/update.php'; - - if ( ! function_exists( 'get_plugin_updates' ) ) { - return array(); - } - - // Get both site plugins and network plugins. - $active_plugins = (array) get_option( 'active_plugins', array() ); - if ( is_multisite() ) { - $network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); - $active_plugins = array_merge( $active_plugins, $network_activated_plugins ); - } - - $active_plugins_data = array(); - $available_updates = get_plugin_updates(); - - foreach ( $active_plugins as $plugin ) { - $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); - $dirname = dirname( $plugin ); - $version_latest = ''; - $slug = explode( '/', $plugin ); - $slug = explode( '.', end( $slug ) ); - $slug = $slug[0]; - - if ( 'woocommerce' !== $slug && ( strstr( $data['PluginURI'], 'woothemes.com' ) || strstr( $data['PluginURI'], 'woocommerce.com' ) ) ) { - $version_data = get_transient( md5( $plugin ) . '_version_data' ); - if ( false === $version_data ) { - $changelog = wp_safe_remote_get( 'http://dzv365zjfbd8v.cloudfront.net/changelogs/' . $dirname . '/changelog.txt' ); - $cl_lines = explode( "\n", wp_remote_retrieve_body( $changelog ) ); - if ( ! empty( $cl_lines ) ) { - foreach ( $cl_lines as $line_num => $cl_line ) { - if ( preg_match( '/^[0-9]/', $cl_line ) ) { - $date = str_replace( '.', '-', trim( substr( $cl_line, 0, strpos( $cl_line, '-' ) ) ) ); - $version = preg_replace( '~[^0-9,.]~', '', stristr( $cl_line, 'version' ) ); - $update = trim( str_replace( '*', '', $cl_lines[ $line_num + 1 ] ) ); - $version_data = array( - 'date' => $date, - 'version' => $version, - 'update' => $update, - 'changelog' => $changelog, - ); - set_transient( md5( $plugin ) . '_version_data', $version_data, DAY_IN_SECONDS ); - break; - } - } - } - } - $version_latest = $version_data['version']; - } elseif ( isset( $available_updates[ $plugin ]->update->new_version ) ) { - $version_latest = $available_updates[ $plugin ]->update->new_version; - } - - // convert plugin data to json response format. - $active_plugins_data[] = array( - 'plugin' => $plugin, - 'name' => $data['Name'], - 'version' => $data['Version'], - 'version_latest' => $version_latest, - 'url' => $data['PluginURI'], - 'author_name' => $data['AuthorName'], - 'author_url' => esc_url_raw( $data['AuthorURI'] ), - 'network_activated' => $data['Network'], - ); - } - - return $active_plugins_data; - } - - /** - * Get info on the current active theme, info on parent theme (if presnet) - * and a list of template overrides. - * - * @return array - */ - public function get_theme_info() { - $active_theme = wp_get_theme(); - - // Get parent theme info if this theme is a child theme, otherwise - // pass empty info in the response. - if ( is_child_theme() ) { - $parent_theme = wp_get_theme( $active_theme->template ); - $parent_theme_info = array( - 'parent_name' => $parent_theme->name, - 'parent_version' => $parent_theme->version, - 'parent_version_latest' => WC_Admin_Status::get_latest_theme_version( $parent_theme ), - 'parent_author_url' => $parent_theme->{'Author URI'}, - ); - } else { - $parent_theme_info = array( - 'parent_name' => '', - 'parent_version' => '', - 'parent_version_latest' => '', - 'parent_author_url' => '', - ); - } - - /** - * Scan the theme directory for all WC templates to see if our theme - * overrides any of them. - */ - $override_files = array(); - $outdated_templates = false; - $scan_files = WC_Admin_Status::scan_template_files( WC()->plugin_path() . '/templates/' ); - foreach ( $scan_files as $file ) { - $located = apply_filters( 'wc_get_template', $file, $file, array(), WC()->template_path(), WC()->plugin_path() . '/templates/' ); - - if ( file_exists( $located ) ) { - $theme_file = $located; - } elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) { - $theme_file = get_stylesheet_directory() . '/' . $file; - } elseif ( file_exists( get_stylesheet_directory() . '/' . WC()->template_path() . $file ) ) { - $theme_file = get_stylesheet_directory() . '/' . WC()->template_path() . $file; - } elseif ( file_exists( get_template_directory() . '/' . $file ) ) { - $theme_file = get_template_directory() . '/' . $file; - } elseif ( file_exists( get_template_directory() . '/' . WC()->template_path() . $file ) ) { - $theme_file = get_template_directory() . '/' . WC()->template_path() . $file; - } else { - $theme_file = false; - } - - if ( ! empty( $theme_file ) ) { - $core_version = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $file ); - $theme_version = WC_Admin_Status::get_file_version( $theme_file ); - if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) { - if ( ! $outdated_templates ) { - $outdated_templates = true; - } - } - $override_files[] = array( - 'file' => str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ), - 'version' => $theme_version, - 'core_version' => $core_version, - ); - } - } - - $active_theme_info = array( - 'name' => $active_theme->name, - 'version' => $active_theme->version, - 'version_latest' => WC_Admin_Status::get_latest_theme_version( $active_theme ), - 'author_url' => esc_url_raw( $active_theme->{'Author URI'} ), - 'is_child_theme' => is_child_theme(), - 'has_woocommerce_support' => current_theme_supports( 'woocommerce' ), - 'has_woocommerce_file' => ( file_exists( get_stylesheet_directory() . '/woocommerce.php' ) || file_exists( get_template_directory() . '/woocommerce.php' ) ), - 'has_outdated_templates' => $outdated_templates, - 'overrides' => $override_files, - ); - - return array_merge( $active_theme_info, $parent_theme_info ); - } - - /** - * Get some setting values for the site that are useful for debugging - * purposes. For full settings access, use the settings api. - * - * @return array - */ - public function get_settings() { - // Get a list of terms used for product/order taxonomies. - $term_response = array(); - $terms = get_terms( 'product_type', array( 'hide_empty' => 0 ) ); - foreach ( $terms as $term ) { - $term_response[ $term->slug ] = strtolower( $term->name ); - } - - // Get a list of terms used for product visibility. - $product_visibility_terms = array(); - $terms = get_terms( 'product_visibility', array( 'hide_empty' => 0 ) ); - foreach ( $terms as $term ) { - $product_visibility_terms[ $term->slug ] = strtolower( $term->name ); - } - - // Return array of useful settings for debugging. - return array( - 'api_enabled' => 'yes' === get_option( 'woocommerce_api_enabled' ), - 'force_ssl' => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ), - 'currency' => get_woocommerce_currency(), - 'currency_symbol' => get_woocommerce_currency_symbol(), - 'currency_position' => get_option( 'woocommerce_currency_pos' ), - 'thousand_separator' => wc_get_price_thousand_separator(), - 'decimal_separator' => wc_get_price_decimal_separator(), - 'number_of_decimals' => wc_get_price_decimals(), - 'geolocation_enabled' => in_array( get_option( 'woocommerce_default_customer_address' ), array( 'geolocation_ajax', 'geolocation' ) ), - 'taxonomies' => $term_response, - 'product_visibility_terms' => $product_visibility_terms, - ); - } - - /** - * Returns security tips. - * - * @return array - */ - public function get_security_info() { - $check_page = 0 < wc_get_page_id( 'shop' ) ? get_permalink( wc_get_page_id( 'shop' ) ) : get_home_url(); - return array( - 'secure_connection' => 'https' === substr( $check_page, 0, 5 ), - 'hide_errors' => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), - ); - } - - /** - * Returns a mini-report on WC pages and if they are configured correctly: - * Present, visible, and including the correct shortcode. - * - * @return array - */ - public function get_pages() { - // WC pages to check against. - $check_pages = array( - _x( 'Shop base', 'Page setting', 'woocommerce' ) => array( - 'option' => 'woocommerce_shop_page_id', - 'shortcode' => '', - ), - _x( 'Cart', 'Page setting', 'woocommerce' ) => array( - 'option' => 'woocommerce_cart_page_id', - 'shortcode' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']', - ), - _x( 'Checkout', 'Page setting', 'woocommerce' ) => array( - 'option' => 'woocommerce_checkout_page_id', - 'shortcode' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']', - ), - _x( 'My account', 'Page setting', 'woocommerce' ) => array( - 'option' => 'woocommerce_myaccount_page_id', - 'shortcode' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']', - ), - _x( 'Terms and conditions', 'Page setting', 'woocommerce' ) => array( - 'option' => 'woocommerce_terms_page_id', - 'shortcode' => '', - ), - ); - - $pages_output = array(); - foreach ( $check_pages as $page_name => $values ) { - $page_id = get_option( $values['option'] ); - $page_set = false; - $page_exists = false; - $page_visible = false; - $shortcode_present = false; - $shortcode_required = false; - - // Page checks. - if ( $page_id ) { - $page_set = true; - } - if ( get_post( $page_id ) ) { - $page_exists = true; - } - if ( 'publish' === get_post_status( $page_id ) ) { - $page_visible = true; - } - - // Shortcode checks. - if ( $values['shortcode'] && get_post( $page_id ) ) { - $shortcode_required = true; - $page = get_post( $page_id ); - if ( strstr( $page->post_content, $values['shortcode'] ) ) { - $shortcode_present = true; - } - } - - // Wrap up our findings into an output array. - $pages_output[] = array( - 'page_name' => $page_name, - 'page_id' => $page_id, - 'page_set' => $page_set, - 'page_exists' => $page_exists, - 'page_visible' => $page_visible, - 'shortcode' => $values['shortcode'], - 'shortcode_required' => $shortcode_required, - 'shortcode_present' => $shortcode_present, - ); - } - - return $pages_output; - } - - /** - * Get any query params needed. - * - * @return array - */ - public function get_collection_params() { - return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ); - } - - /** - * Prepare the system status response - * - * @param array $system_status System status data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_item_for_response( $system_status, $request ) { - $data = $this->add_additional_fields_to_object( $system_status, $request ); - $data = $this->filter_response_by_context( $data, 'view' ); - - $response = rest_ensure_response( $data ); - - /** - * Filter the system status returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param mixed $system_status System status - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_system_status', $response, $system_status, $request ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-system-status-tools-controller.php b/includes/api/class-wc-rest-system-status-tools-controller.php index b5302f06aca0..f1b0bef61d72 100644 --- a/includes/api/class-wc-rest-system-status-tools-controller.php +++ b/includes/api/class-wc-rest-system-status-tools-controller.php @@ -14,544 +14,14 @@ * System status tools controller. * * @package WooCommerce/API - * @extends WC_REST_Controller + * @extends WC_REST_System_Status_Tools_V2_Controller */ -class WC_REST_System_Status_Tools_Controller extends WC_REST_Controller { +class WC_REST_System_Status_Tools_Controller extends WC_REST_System_Status_Tools_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Route base. - * - * @var string - */ - protected $rest_base = 'system_status/tools'; - - /** - * Register the routes for /system_status/tools/*. - */ - 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(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'string', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - ), - 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 ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Check whether a given request has permission to view system status tools. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Check whether a given request has permission to view a specific system status tool. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * Check whether a given request has permission to execute a specific system status tool. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - if ( ! wc_rest_check_manager_permissions( 'system_status', 'edit' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - return true; - } - - /** - * A list of available tools for use in the system status section. - * 'button' becomes 'action' in the API. - * - * @return array - */ - public function get_tools() { - $tools = array( - 'clear_transients' => array( - 'name' => __( 'WooCommerce transients', 'woocommerce' ), - 'button' => __( 'Clear transients', 'woocommerce' ), - 'desc' => __( 'This tool will clear the product/shop transients cache.', 'woocommerce' ), - ), - 'clear_expired_transients' => array( - 'name' => __( 'Expired transients', 'woocommerce' ), - 'button' => __( 'Clear transients', 'woocommerce' ), - 'desc' => __( 'This tool will clear ALL expired transients from WordPress.', 'woocommerce' ), - ), - 'delete_orphaned_variations' => array( - 'name' => __( 'Orphaned variations', 'woocommerce' ), - 'button' => __( 'Delete orphaned variations', 'woocommerce' ), - 'desc' => __( 'This tool will delete all variations which have no parent.', 'woocommerce' ), - ), - 'clear_expired_download_permissions' => array( - 'name' => __( 'Used-up download permissions', 'woocommerce' ), - 'button' => __( 'Clean up download permissions', 'woocommerce' ), - 'desc' => __( 'This tool will delete expired download permissions and permissions with 0 remaining downloads.', 'woocommerce' ), - ), - 'add_order_indexes' => array( - 'name' => __( 'Order address indexes', 'woocommerce' ), - 'button' => __( 'Index orders', 'woocommerce' ), - 'desc' => __( 'This tool will add address indexes to orders that do not have them yet. This improves order search results.', 'woocommerce' ), - ), - 'recount_terms' => array( - 'name' => __( 'Term counts', 'woocommerce' ), - 'button' => __( 'Recount terms', 'woocommerce' ), - 'desc' => __( 'This tool will recount product terms - useful when changing your settings in a way which hides products from the catalog.', 'woocommerce' ), - ), - 'reset_roles' => array( - 'name' => __( 'Capabilities', 'woocommerce' ), - 'button' => __( 'Reset capabilities', 'woocommerce' ), - 'desc' => __( 'This tool will reset the admin, customer and shop_manager roles to default. Use this if your users cannot access all of the WooCommerce admin pages.', 'woocommerce' ), - ), - 'clear_sessions' => array( - 'name' => __( 'Clear customer sessions', 'woocommerce' ), - 'button' => __( 'Clear', 'woocommerce' ), - 'desc' => sprintf( - '%1$s %2$s', - __( 'Note:', 'woocommerce' ), - __( 'This tool will delete all customer session data from the database, including current carts and saved carts in the database.', 'woocommerce' ) - ), - ), - 'install_pages' => array( - 'name' => __( 'Create default WooCommerce pages', 'woocommerce' ), - 'button' => __( 'Create pages', 'woocommerce' ), - 'desc' => sprintf( - '%1$s %2$s', - __( 'Note:', 'woocommerce' ), - __( 'This tool will install all the missing WooCommerce pages. Pages already defined and set up will not be replaced.', 'woocommerce' ) - ), - ), - 'delete_taxes' => array( - 'name' => __( 'Delete WooCommerce tax rates', 'woocommerce' ), - 'button' => __( 'Delete tax rates', 'woocommerce' ), - 'desc' => sprintf( - '%1$s %2$s', - __( 'Note:', 'woocommerce' ), - __( 'This option will delete ALL of your tax rates, use with caution. This action cannot be reversed.', 'woocommerce' ) - ), - ), - 'reset_tracking' => array( - 'name' => __( 'Reset usage tracking', 'woocommerce' ), - 'button' => __( 'Reset', 'woocommerce' ), - 'desc' => __( 'This will reset your usage tracking settings, causing it to show the opt-in banner again and not sending any data.', 'woocommerce' ), - ), - 'regenerate_thumbnails' => array( - 'name' => __( 'Regenerate shop thumbnails', 'woocommerce' ), - 'button' => __( 'Regenerate', 'woocommerce' ), - 'desc' => __( 'This will regenerate all shop thumbnails to match your theme and/or image settings.', 'woocommerce' ), - ), - ); - - // Jetpack does the image resizing heavy lifting so you don't have to. - if ( ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) || ! apply_filters( 'woocommerce_background_image_regeneration', true ) ) { - unset( $tools['regenerate_thumbnails'] ); - } - - return apply_filters( 'woocommerce_debug_tools', $tools ); - } - - /** - * Get a list of system status tools. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $tools = array(); - foreach ( $this->get_tools() as $id => $tool ) { - $tools[] = $this->prepare_response_for_collection( - $this->prepare_item_for_response( - array( - 'id' => $id, - 'name' => $tool['name'], - 'action' => $tool['button'], - 'description' => $tool['desc'], - ), $request - ) - ); - } - - $response = rest_ensure_response( $tools ); - return $response; - } - - /** - * Return a single tool. - * - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function get_item( $request ) { - $tools = $this->get_tools(); - if ( empty( $tools[ $request['id'] ] ) ) { - return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - $tool = $tools[ $request['id'] ]; - return rest_ensure_response( - $this->prepare_item_for_response( - array( - 'id' => $request['id'], - 'name' => $tool['name'], - 'action' => $tool['button'], - 'description' => $tool['desc'], - ), $request - ) - ); - } - - /** - * Update (execute) a tool. - * - * @param WP_REST_Request $request Request data. - * @return WP_Error|WP_REST_Response - */ - public function update_item( $request ) { - $tools = $this->get_tools(); - if ( empty( $tools[ $request['id'] ] ) ) { - return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $tool = $tools[ $request['id'] ]; - $tool = array( - 'id' => $request['id'], - 'name' => $tool['name'], - 'action' => $tool['button'], - 'description' => $tool['desc'], - ); - - $execute_return = $this->execute_tool( $request['id'] ); - $tool = array_merge( $tool, $execute_return ); - - /** - * Fires after a WooCommerce REST system status tool has been executed. - * - * @param array $tool Details about the tool that has been executed. - * @param WP_REST_Request $request The current WP_REST_Request object. - */ - do_action( 'woocommerce_rest_insert_system_status_tool', $tool, $request ); - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $tool, $request ); - return rest_ensure_response( $response ); - } - - /** - * Prepare a tool item for serialization. - * - * @param array $item Object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response Response data. - */ - public function prepare_item_for_response( $item, $request ) { - $context = empty( $request['context'] ) ? 'view' : $request['context']; - $data = $this->add_additional_fields_to_object( $item, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $item['id'] ) ); - - return $response; - } - - /** - * Get the system status tools schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'system_status_tool', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'A unique identifier for the tool.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_title', - ), - ), - 'name' => array( - 'description' => __( 'Tool name.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'action' => array( - 'description' => __( 'What running the tool will do.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'description' => array( - 'description' => __( 'Tool description.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - 'success' => array( - 'description' => __( 'Did the tool run successfully?', 'woocommerce' ), - 'type' => 'boolean', - 'context' => array( 'edit' ), - ), - 'message' => array( - 'description' => __( 'Tool return message.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } - - /** - * Prepare links for the request. - * - * @param string $id ID. - * @return array - */ - protected function prepare_links( $id ) { - $base = '/' . $this->namespace . '/' . $this->rest_base; - $links = array( - 'item' => array( - 'href' => rest_url( trailingslashit( $base ) . $id ), - 'embeddable' => true, - ), - ); - - return $links; - } - - /** - * Get any query params needed. - * - * @return array - */ - public function get_collection_params() { - return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ); - } - - /** - * Actually executes a tool. - * - * @param string $tool Tool. - * @return array - */ - public function execute_tool( $tool ) { - global $wpdb; - $ran = true; - switch ( $tool ) { - case 'clear_transients': - wc_delete_product_transients(); - wc_delete_shop_order_transients(); - WC_Cache_Helper::get_transient_version( 'shipping', true ); - $message = __( 'Product transients cleared', 'woocommerce' ); - break; - - case 'clear_expired_transients': - /* translators: %d: amount of expired transients */ - $message = sprintf( __( '%d transients rows cleared', 'woocommerce' ), wc_delete_expired_transients() ); - break; - - case 'delete_orphaned_variations': - // Delete orphans. - $result = absint( - $wpdb->query( - "DELETE products - FROM {$wpdb->posts} products - LEFT JOIN {$wpdb->posts} wp ON wp.ID = products.post_parent - WHERE wp.ID IS NULL AND products.post_type = 'product_variation';" - ) - ); - /* translators: %d: amount of orphaned variations */ - $message = sprintf( __( '%d orphaned variations deleted', 'woocommerce' ), $result ); - break; - - case 'clear_expired_download_permissions': - // Delete expired download permissions and ones with 0 downloads remaining. - $result = absint( - $wpdb->query( - $wpdb->prepare( - "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions - WHERE ( downloads_remaining != '' AND downloads_remaining = 0 ) OR ( access_expires IS NOT NULL AND access_expires < %s )", - date( 'Y-m-d', current_time( 'timestamp' ) ) - ) - ) - ); - /* translators: %d: amount of permissions */ - $message = sprintf( __( '%d permissions deleted', 'woocommerce' ), $result ); - break; - - case 'add_order_indexes': - /* - * Add billing and shipping address indexes containing the customer name for orders - * that don't have address indexes yet. - */ - $sql = "INSERT INTO {$wpdb->postmeta}( post_id, meta_key, meta_value ) - SELECT post_id, '%s', GROUP_CONCAT( meta_value SEPARATOR ' ' ) - FROM {$wpdb->postmeta} - WHERE meta_key IN ( '%s', '%s' ) - AND post_id IN ( SELECT DISTINCT post_id FROM {$wpdb->postmeta} - WHERE post_id NOT IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='%s' ) - AND post_id IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='%s' ) ) - GROUP BY post_id"; - $rows = $wpdb->query( $wpdb->prepare( $sql, '_billing_address_index', '_billing_first_name', '_billing_last_name', '_billing_address_index', '_billing_last_name' ) ); // WPCS: unprepared SQL ok. - $rows += $wpdb->query( $wpdb->prepare( $sql, '_shipping_address_index', '_shipping_first_name', '_shipping_last_name', '_shipping_address_index', '_shipping_last_name' ) ); // WPCS: unprepared SQL ok. - - /* translators: %d: amount of indexes */ - $message = sprintf( __( '%d indexes added', 'woocommerce' ), $rows ); - break; - - case 'reset_roles': - // Remove then re-add caps and roles. - WC_Install::remove_roles(); - WC_Install::create_roles(); - $message = __( 'Roles successfully reset', 'woocommerce' ); - break; - - case 'recount_terms': - $product_cats = get_terms( - 'product_cat', array( - 'hide_empty' => false, - 'fields' => 'id=>parent', - ) - ); - _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); - $product_tags = get_terms( - 'product_tag', array( - 'hide_empty' => false, - 'fields' => 'id=>parent', - ) - ); - _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); - $message = __( 'Terms successfully recounted', 'woocommerce' ); - break; - - case 'clear_sessions': - $wpdb->query( "TRUNCATE {$wpdb->prefix}woocommerce_sessions" ); - $result = absint( $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key='_woocommerce_persistent_cart_" . get_current_blog_id() . "';" ) ); // WPCS: unprepared SQL ok. - wp_cache_flush(); - /* translators: %d: amount of sessions */ - $message = sprintf( __( 'Deleted all active sessions, and %d saved carts.', 'woocommerce' ), absint( $result ) ); - break; - - case 'install_pages': - WC_Install::create_pages(); - $message = __( 'All missing WooCommerce pages successfully installed', 'woocommerce' ); - break; - - case 'delete_taxes': - $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rates;" ); - $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations;" ); - WC_Cache_Helper::incr_cache_prefix( 'taxes' ); - $message = __( 'Tax rates successfully deleted', 'woocommerce' ); - break; - - case 'reset_tracking': - if ( ! class_exists( 'WC_Tracker' ) ) { - include_once WC_ABSPATH . 'includes/class-wc-tracker.php'; - } - WC_Tracker::opt_out_request(); - delete_option( 'woocommerce_allow_tracking' ); - WC_Admin_Notices::add_notice( 'tracking' ); - $message = __( 'Usage tracking settings successfully reset.', 'woocommerce' ); - break; - - case 'regenerate_thumbnails': - WC_Regenerate_Images::queue_image_regeneration(); - $message = __( 'Thumbnail regeneration has been scheduled to run in the background.', 'woocommerce' ); - break; - - default: - $tools = $this->get_tools(); - if ( isset( $tools[ $tool ]['callback'] ) ) { - $callback = $tools[ $tool ]['callback']; - $return = call_user_func( $callback ); - if ( is_string( $return ) ) { - $message = $return; - } elseif ( false === $return ) { - $callback_string = is_array( $callback ) ? get_class( $callback[0] ) . '::' . $callback[1] : $callback; - $ran = false; - /* translators: %s: callback string */ - $message = sprintf( __( 'There was an error calling %s', 'woocommerce' ), $callback_string ); - } else { - $message = __( 'Tool ran.', 'woocommerce' ); - } - } else { - $ran = false; - $message = __( 'There was an error calling this tool. There is no callback present.', 'woocommerce' ); - } - break; - } - - return array( - 'success' => $ran, - 'message' => $message, - ); - } + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-tax-classes-controller.php b/includes/api/class-wc-rest-tax-classes-controller.php index eb36a2da3d7a..39879e20ef45 100644 --- a/includes/api/class-wc-rest-tax-classes-controller.php +++ b/includes/api/class-wc-rest-tax-classes-controller.php @@ -14,14 +14,14 @@ * REST API Tax Classes controller class. * * @package WooCommerce/API - * @extends WC_REST_Tax_Classes_V1_Controller + * @extends WC_REST_Tax_Classes_V2_Controller */ -class WC_REST_Tax_Classes_Controller extends WC_REST_Tax_Classes_V1_Controller { +class WC_REST_Tax_Classes_Controller extends WC_REST_Tax_Classes_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; + protected $namespace = 'wc/v3'; } diff --git a/includes/api/class-wc-rest-taxes-controller.php b/includes/api/class-wc-rest-taxes-controller.php index 5bd1b6092114..bd650f87c41e 100644 --- a/includes/api/class-wc-rest-taxes-controller.php +++ b/includes/api/class-wc-rest-taxes-controller.php @@ -1,4 +1,4 @@ -/deliveries endpoint. - * - * @package WooCommerce/API - * @since 2.6.0 - */ - -defined( 'ABSPATH' ) || exit; - -/** - * REST API Webhook Deliveries controller class. - * - * @deprecated 3.3.0 Webhooks deliveries logs now uses logging system. - * @package WooCommerce/API - * @extends WC_REST_Webhook_Deliveries_V1_Controller - */ -class WC_REST_Webhook_Deliveries_Controller extends WC_REST_Webhook_Deliveries_V1_Controller { - - /** - * Endpoint namespace. - * - * @var string - */ - protected $namespace = 'wc/v2'; - - /** - * Prepare a single webhook delivery output for response. - * - * @param stdClass $log Delivery log object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_item_for_response( $log, $request ) { - $data = (array) $log; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $log ) ); - - /** - * Filter webhook delivery object returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param stdClass $log Delivery log object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_rest_prepare_webhook_delivery', $response, $log, $request ); - } - - /** - * Get the Webhook's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'webhook_delivery', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'duration' => array( - 'description' => __( 'The delivery duration, in seconds.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'summary' => array( - 'description' => __( 'A friendly summary of the response including the HTTP response code, message, and body.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'request_url' => array( - 'description' => __( 'The URL where the webhook was delivered.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'request_headers' => array( - 'description' => __( 'Request headers.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'request_body' => array( - 'description' => __( 'Request body.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'response_code' => array( - 'description' => __( 'The HTTP response code from the receiving server.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'response_message' => array( - 'description' => __( 'The HTTP response message from the receiving server.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'response_headers' => array( - 'description' => __( 'Array of the response headers from the receiving server.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'response_body' => array( - 'description' => __( 'The response body from the receiving server.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view' ), - 'readonly' => true, - ), - 'date_created' => array( - 'description' => __( "The date the webhook delivery was logged, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the webhook delivery was logged, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } -} diff --git a/includes/api/class-wc-rest-webhooks-controller.php b/includes/api/class-wc-rest-webhooks-controller.php index f5cc00e235c5..f876adb3f21c 100644 --- a/includes/api/class-wc-rest-webhooks-controller.php +++ b/includes/api/class-wc-rest-webhooks-controller.php @@ -14,172 +14,14 @@ * REST API Webhooks controller class. * * @package WooCommerce/API - * @extends WC_REST_Webhooks_V1_Controller + * @extends WC_REST_Webhooks_V2_Controller */ -class WC_REST_Webhooks_Controller extends WC_REST_Webhooks_V1_Controller { +class WC_REST_Webhooks_Controller extends WC_REST_Webhooks_V2_Controller { /** * Endpoint namespace. * * @var string */ - protected $namespace = 'wc/v2'; - - /** - * Prepare a single webhook output for response. - * - * @param int $id Webhook ID. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response - */ - public function prepare_item_for_response( $id, $request ) { - $webhook = wc_get_webhook( $id ); - - if ( empty( $webhook ) || is_null( $webhook ) ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $data = array( - 'id' => $webhook->get_id(), - 'name' => $webhook->get_name(), - 'status' => $webhook->get_status(), - 'topic' => $webhook->get_topic(), - 'resource' => $webhook->get_resource(), - 'event' => $webhook->get_event(), - 'hooks' => $webhook->get_hooks(), - 'delivery_url' => $webhook->get_delivery_url(), - 'date_created' => wc_rest_prepare_date_response( $webhook->get_date_created(), false ), - 'date_created_gmt' => wc_rest_prepare_date_response( $webhook->get_date_created() ), - 'date_modified' => wc_rest_prepare_date_response( $webhook->get_date_modified(), false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( $webhook->get_date_modified() ), - ); - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $webhook->get_id(), $request ) ); - - /** - * Filter webhook object returned from the REST API. - * - * @param WP_REST_Response $response The response object. - * @param WC_Webhook $webhook Webhook object used to create response. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $webhook, $request ); - } - - /** - * Get the default REST API version. - * - * @since 3.0.0 - * @return string - */ - protected function get_default_api_version() { - return 'wp_api_v2'; - } - - /** - * Get the Webhook's schema, conforming to JSON Schema. - * - * @return array - */ - public function get_item_schema() { - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'webhook', - 'type' => 'object', - 'properties' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'A friendly name for the webhook.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'status' => array( - 'description' => __( 'Webhook status.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'active', - 'enum' => array( 'active', 'paused', 'disabled' ), - 'context' => array( 'view', 'edit' ), - 'arg_options' => array( - 'sanitize_callback' => 'wc_is_webhook_valid_topic', - ), - ), - 'topic' => array( - 'description' => __( 'Webhook topic.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ), - 'resource' => array( - 'description' => __( 'Webhook resource.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'event' => array( - 'description' => __( 'Webhook event.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'hooks' => array( - 'description' => __( 'WooCommerce action names associated with the webhook.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - ), - ), - 'delivery_url' => array( - 'description' => __( 'The URL where the webhook payload is delivered.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'secret' => array( - 'description' => __( "Secret key used to generate a hash of the delivered webhook and provided in the request headers. This will default to a MD5 hash from the current user's ID|username if not provided.", 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'edit' ), - ), - 'date_created' => array( - 'description' => __( "The date the webhook was created, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_created_gmt' => array( - 'description' => __( 'The date the webhook was created, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified' => array( - 'description' => __( "The date the webhook was last modified, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_modified_gmt' => array( - 'description' => __( 'The date the webhook was last modified, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - ), - ); - - return $this->add_additional_fields_schema( $schema ); - } + protected $namespace = 'wc/v3'; }