From 3ceb18911885ace040f5e364c6a31968fc78e823 Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Thu, 25 Aug 2016 11:48:17 -0700 Subject: [PATCH 1/3] Add GET /shipping_methods and GET /shipping_methods/METHOD_ID Also adds tests. --- ...ss-wc-rest-shipping-methods-controller.php | 221 ++++++++++++++++++ includes/class-wc-api.php | 2 + includes/wc-rest-functions.php | 9 +- tests/unit-tests/api/shipping-methods.php | 136 +++++++++++ 4 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 includes/api/class-wc-rest-shipping-methods-controller.php create mode 100644 tests/unit-tests/api/shipping-methods.php diff --git a/includes/api/class-wc-rest-shipping-methods-controller.php b/includes/api/class-wc-rest-shipping-methods-controller.php new file mode 100644 index 00000000000..b7f9cdac390 --- /dev/null +++ b/includes/api/class-wc-rest-shipping-methods-controller.php @@ -0,0 +1,221 @@ + + */ + 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( + 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 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( '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 + * @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 doesn't 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 WP_Comment $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 product reviews object returned from the REST API. + * + * @param WP_REST_Response $response The response object. + * @param Object $method Product review 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 Object $method Shipping method object. + * @param WP_REST_Request $request Request object. + * @return array Links for the given product review. + */ + 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' ), + ), + 'title' => array( + 'description' => __( 'Shipping method title.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + ), + 'description' => array( + 'description' => __( 'Shipping method description.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + ), + ), + ); + + 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' ) ), + ); + } + +} diff --git a/includes/class-wc-api.php b/includes/class-wc-api.php index 61756f651f2..ba496e0affc 100644 --- a/includes/class-wc-api.php +++ b/includes/class-wc-api.php @@ -174,6 +174,7 @@ class WC_API extends WC_Legacy_API { include_once( dirname( __FILE__ ) . '/api/class-wc-rest-webhooks-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-system-status-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-system-status-tools-controller.php' ); + include_once( dirname( __FILE__ ) . '/api/class-wc-rest-shipping-methods-controller.php' ); } /** @@ -212,6 +213,7 @@ class WC_API extends WC_Legacy_API { 'WC_REST_Webhooks_Controller', 'WC_REST_System_Status_Controller', 'WC_REST_System_Status_Tools_Controller', + 'WC_REST_Shipping_Methods_Controller', ); foreach ( $controllers as $controller ) { diff --git a/includes/wc-rest-functions.php b/includes/wc-rest-functions.php index 083fe11c330..48b3a725248 100644 --- a/includes/wc-rest-functions.php +++ b/includes/wc-rest-functions.php @@ -306,10 +306,11 @@ function wc_rest_check_product_term_permissions( $taxonomy, $context = 'read', $ */ function wc_rest_check_manager_permissions( $object, $context = 'read' ) { $objects = array( - 'reports' => 'view_woocommerce_reports', - 'settings' => 'manage_woocommerce', - 'system_status' => 'manage_woocommerce', - 'attributes' => 'manage_product_terms', + 'reports' => 'view_woocommerce_reports', + 'settings' => 'manage_woocommerce', + 'system_status' => 'manage_woocommerce', + 'attributes' => 'manage_product_terms', + 'shipping_methods' => 'manage_woocommerce', ); $permission = current_user_can( $objects[ $object ] ); diff --git a/tests/unit-tests/api/shipping-methods.php b/tests/unit-tests/api/shipping-methods.php new file mode 100644 index 00000000000..ff23b7e9022 --- /dev/null +++ b/tests/unit-tests/api/shipping-methods.php @@ -0,0 +1,136 @@ +endpoint = new WC_REST_Shipping_Methods_Controller(); + $this->user = $this->factory->user->create( array( + 'role' => 'administrator', + ) ); + } + + /** + * Test route registration. + * + * @since 2.7.0 + */ + public function test_register_routes() { + $routes = $this->server->get_routes(); + $this->assertArrayHasKey( '/wc/v1/shipping_methods', $routes ); + $this->assertArrayHasKey( '/wc/v1/shipping_methods/(?P[\w-]+)', $routes ); + } + + /** + * Test getting all shipping methods. + * + * @since 2.7.0 + */ + public function test_get_shipping_methods() { + wp_set_current_user( $this->user ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods' ) ); + $methods = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertContains( array( + 'id' => 'free_shipping', + 'title' => 'Free Shipping', + 'description' => 'Free Shipping is a special method which can be triggered with coupons and minimum spends.', + '_links' => array( + 'self' => array( + array( + 'href' => rest_url( '/wc/v1/shipping_methods/free_shipping' ), + ), + ), + 'collection' => array( + array( + 'href' => rest_url( '/wc/v1/shipping_methods' ), + ), + ), + ), + ), $methods ); + } + + /** + * Tests to make sure shipping methods cannot viewed without valid permissions. + * + * @since 2.7.0 + */ + public function test_get_shipping_methods_without_permission() { + wp_set_current_user( 0 ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods' ) ); + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Tests getting a single shipping method. + * + * @since 2.7.0 + */ + public function test_get_shipping_method() { + wp_set_current_user( $this->user ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/local_pickup' ) ); + $method = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( array( + 'id' => 'local_pickup', + 'title' => 'Local Pickup', + 'description' => 'Allow customers to pick up orders themselves. By default, when using local pickup store base taxes will apply regardless of customer address.', + ), $method ); + } + + /** + * Tests getting a single shipping method without the correct permissions. + * + * @since 2.7.0 + */ + public function test_get_shipping_method_without_permission() { + wp_set_current_user( 0 ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/local_pickup' ) ); + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Tests getting a shipping method with an invalid ID. + * + * @since 2.7.0 + */ + public function test_get_shipping_method_invalid_id() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/fake_method' ) ); + $this->assertEquals( 404, $response->get_status() ); + } + + /** + * Test the product review schema. + * + * @since 2.7.0 + */ + public function test_shipping_method_schema() { + wp_set_current_user( $this->user ); + + $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/shipping_methods' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $properties = $data['schema']['properties']; + + $this->assertEquals( 3, count( $properties ) ); + $this->assertArrayHasKey( 'id', $properties ); + $this->assertArrayHasKey( 'title', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + } + +} From 147c18c197f5713f3df0ff20c91216c46b01f86c Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Thu, 25 Aug 2016 20:34:26 -0700 Subject: [PATCH 2/3] Add the ability to update/create/delete shipping method instances --- .../abstracts/abstract-wc-shipping-method.php | 2 +- ...-rest-shipping-zone-methods-controller.php | 247 +++++++++++++++++- tests/unit-tests/api/shipping-zones.php | 136 ++++++++++ 3 files changed, 382 insertions(+), 3 deletions(-) diff --git a/includes/abstracts/abstract-wc-shipping-method.php b/includes/abstracts/abstract-wc-shipping-method.php index aaf0e716ee6..9dbff414c75 100644 --- a/includes/abstracts/abstract-wc-shipping-method.php +++ b/includes/abstracts/abstract-wc-shipping-method.php @@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) { * Extended by shipping methods to handle shipping calculations etc. * * @class WC_Shipping_Method - * @version 2.6.0 + * @version 2.7.0 * @package WooCommerce/Abstracts * @category Abstract Class * @author WooThemes 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 2e729101a5a..1895eaf356f 100644 --- a/includes/api/class-wc-rest-shipping-zone-methods-controller.php +++ b/includes/api/class-wc-rest-shipping-zone-methods-controller.php @@ -32,6 +32,12 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co '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' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), + ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); @@ -41,6 +47,23 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co '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, 'update_items_permissions_check' ), + 'args' => array( + 'force' => array( + 'default' => false, + 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), + ), + ), + ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } @@ -102,6 +125,195 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co return rest_ensure_response( $data ); } + /** + * Create a new shipping zone method instance. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Request|WP_Error + */ + public function create_item( $request ) { + global $wpdb; + + $method_id = $request['method_id']; + $zone = $this->get_zone( $request['zone_id'] ); + if ( is_wp_error( $zone ) ) { + return $zone; + } + + $instance_id = 0; + $wc_shipping = WC_Shipping::instance(); + $allowed_classes = $wc_shipping->get_shipping_method_class_names(); + $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d", $zone->get_id() ) ); + if ( in_array( $method_id, array_keys( $allowed_classes ) ) ) { + $wpdb->insert( + $wpdb->prefix . 'woocommerce_shipping_zone_methods', + array( + 'method_id' => $method_id , + 'zone_id' => $zone->get_id(), + 'method_order' => ( $count + 1 ) + ), + array( + '%s', + '%d', + '%d' + ) + ); + $instance_id = $wpdb->insert_id; + } + + if ( $instance_id ) { + do_action( 'woocommerce_shipping_zone_method_added', $instance_id, $method_id, $zone->get_id() ); + } + + WC_Cache_Helper::get_transient_version( 'shipping', true ); + + $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 ); + + $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 ) { + global $wpdb; + + $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 doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) ); + } + + $method = $this->update_fields( $instance_id, $method, $request ); + $request->set_param( 'context', 'view' ); + $response = $this->prepare_item_for_response( $method, $request ); + + if ( $force ) { + $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) ); + } else { + return new WP_Error( 'rest_trash_not_supported', __( 'Shipping methods do not support trashing.' ), 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 + * @return WP_REST_Response|WP_Error + */ + public function update_item( $request ) { + global $wpdb; + + $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 doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) ); + } + + $method = $this->update_fields( $instance_id, $method, $request ); + + $data = $this->prepare_item_for_response( $method, $request ); + return rest_ensure_response( $data ); + } + + /** + * Updates settings, order, and enabled status on create. + * + * @param $instance_id integer + * @param $method + * @param WP_REST_Request $request + * @return $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; + foreach ( $method->get_instance_form_fields() as $key => $field ) { + if ( isset( $request['settings'][ $key ] ) ) { + $instance_settings[ $key ] = $request['settings'][ $key ]; + } + } + 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. * @@ -118,6 +330,7 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co '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']; @@ -134,6 +347,31 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co return $response; } + /** + * Return settings associated with this shipping zone method instance. + */ + 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. * @@ -195,9 +433,9 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co 'required' => false, ), 'method_id' => array( - 'description' => __( 'Shipping method ID.', 'woocommerce' ), + 'description' => __( 'Shipping method ID. Write on create only.', 'woocommerce' ), 'type' => 'string', - 'context' => array( 'view' ), + 'context' => array( 'view', 'edit.' ), ), 'method_title' => array( 'description' => __( 'Shipping method title.', 'woocommerce' ), @@ -209,6 +447,11 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co 'type' => 'string', 'context' => array( 'view' ), ), + 'settings' => array( + 'description' => __( 'Shipping method settings.', 'woocommerce' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + ), ), ); diff --git a/tests/unit-tests/api/shipping-zones.php b/tests/unit-tests/api/shipping-zones.php index 714a5fd97c8..8c032759204 100644 --- a/tests/unit-tests/api/shipping-zones.php +++ b/tests/unit-tests/api/shipping-zones.php @@ -488,6 +488,25 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case { $methods = $zone->get_shipping_methods(); $method = $methods[ $instance_id ]; + $settings = array(); + $method->init_instance_settings(); + foreach ( $method->get_instance_form_fields() as $id => $field ) { + $data = array( + 'id' => $id, + 'label' => $field['title'], + 'description' => ( empty( $field['description'] ) ? '' : $field['description'] ), + 'type' => $field['type'], + 'value' => $method->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; + } + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods' ) ); $data = $response->get_data(); $expected = array( @@ -498,6 +517,7 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case { 'method_id' => $method->id, 'method_title' => $method->method_title, 'method_description' => $method->method_description, + 'settings' => $settings, '_links' => array( 'self' => array( array( @@ -556,4 +576,120 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case { $this->assertEquals( 404, $response->get_status() ); } + + /** + * Test updating a Shipping Zone Method. + * @since 2.7.0 + */ + public function test_update_methods() { + wp_set_current_user( $this->user ); + + $zone = $this->create_shipping_zone( 'Zone 1' ); + $instance_id = $zone->add_shipping_method( 'flat_rate' ); + $methods = $zone->get_shipping_methods(); + $method = $methods[ $instance_id ]; + + // Test defaults + $request = new WP_REST_Request( 'GET', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertArrayHasKey( 'title', $data['settings'] ); + $this->assertEquals( 'Flat Rate', $data['settings']['title']['value'] ); + $this->assertArrayHasKey( 'tax_status', $data['settings'] ); + $this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] ); + $this->assertArrayHasKey( 'cost', $data['settings'] ); + $this->assertEquals( '0', $data['settings']['cost']['value'] ); + + // Update a single value + $request = new WP_REST_Request( 'POST', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id ); + $request->set_body_params( array( + 'settings' => array( + 'cost' => 5, + ), + ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertArrayHasKey( 'title', $data['settings'] ); + $this->assertEquals( 'Flat Rate', $data['settings']['title']['value'] ); + $this->assertArrayHasKey( 'tax_status', $data['settings'] ); + $this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] ); + $this->assertArrayHasKey( 'cost', $data['settings'] ); + $this->assertEquals( '5', $data['settings']['cost']['value'] ); + + // Test multiple settings + $request = new WP_REST_Request( 'POST', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id ); + $request->set_body_params( array( + 'settings' => array( + 'cost' => 10, + 'tax_status' => 'none', + ), + ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertArrayHasKey( 'title', $data['settings'] ); + $this->assertEquals( 'Flat Rate', $data['settings']['title']['value'] ); + $this->assertArrayHasKey( 'tax_status', $data['settings'] ); + $this->assertEquals( 'none', $data['settings']['tax_status']['value'] ); + $this->assertArrayHasKey( 'cost', $data['settings'] ); + $this->assertEquals( '10', $data['settings']['cost']['value'] ); + + // Test other parameters + $this->assertTrue( $data['enabled'] ); + $this->assertEquals( 1, $data['order'] ); + + $request = new WP_REST_Request( 'POST', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id ); + $request->set_body_params( array( + 'enabled' => false, + 'order' => 2, + ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertFalse( $data['enabled'] ); + $this->assertEquals( 2, $data['order'] ); + $this->assertArrayHasKey( 'cost', $data['settings'] ); + $this->assertEquals( '10', $data['settings']['cost']['value'] ); + } + + /** + * Test creating a Shipping Zone Method. + * @since 2.7.0 + */ + public function test_create_method() { + wp_set_current_user( $this->user ); + $zone = $this->create_shipping_zone( 'Zone 1' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods' ); + $request->set_body_params( array( + 'method_id' => 'flat_rate', + 'enabled' => false, + 'order' => 2, + ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertFalse( $data['enabled'] ); + $this->assertEquals( 2, $data['order'] ); + $this->assertArrayHasKey( 'cost', $data['settings'] ); + $this->assertEquals( '0', $data['settings']['cost']['value'] ); + } + + /** + * Test deleting a Shipping Zone Method. + * @since 2.7.0 + */ + public function test_delete_method() { + wp_set_current_user( $this->user ); + $zone = $this->create_shipping_zone( 'Zone 1' ); + $instance_id = $zone->add_shipping_method( 'flat_rate' ); + $methods = $zone->get_shipping_methods(); + $method = $methods[ $instance_id ]; + $request = new WP_REST_Request( 'DELETE', '/wc/v1/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id ); + $request->set_param( 'force', true ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + } + } From 849a208e54a75e0ce6996bd7c642b364858eb36a Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Fri, 26 Aug 2016 04:57:30 -0700 Subject: [PATCH 3/3] Fix issues based on feedback. Make sure to use WC_Shipping_Zone's methods when avaiable, fix up some coding standards, and add a new delete_shipping_method to WC_Shipping_Zone --- ...ss-wc-rest-shipping-methods-controller.php | 14 +++--- ...-rest-shipping-zone-methods-controller.php | 43 ++++--------------- includes/class-wc-shipping-zone.php | 21 +++++++++ 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/includes/api/class-wc-rest-shipping-methods-controller.php b/includes/api/class-wc-rest-shipping-methods-controller.php index b7f9cdac390..b2dbfea1146 100644 --- a/includes/api/class-wc-rest-shipping-methods-controller.php +++ b/includes/api/class-wc-rest-shipping-methods-controller.php @@ -93,14 +93,14 @@ class WC_REST_Shipping_Methods_Controller extends WC_REST_Controller { * @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 ); + $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 ); + } + return rest_ensure_response( $response ); } /** @@ -112,7 +112,7 @@ class WC_REST_Shipping_Methods_Controller extends WC_REST_Controller { public function get_item( $request ) { $wc_shipping = WC_Shipping::instance(); $methods = $wc_shipping->get_shipping_methods(); - if ( empty ( $methods[ $request['id'] ] ) ) { + if ( empty( $methods[ $request['id'] ] ) ) { return new WP_Error( 'woocommerce_rest_shipping_method_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) ); } 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 1895eaf356f..2f687ac47d7 100644 --- a/includes/api/class-wc-rest-shipping-zone-methods-controller.php +++ b/includes/api/class-wc-rest-shipping-zone-methods-controller.php @@ -140,36 +140,9 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co return $zone; } - $instance_id = 0; - $wc_shipping = WC_Shipping::instance(); - $allowed_classes = $wc_shipping->get_shipping_method_class_names(); - $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d", $zone->get_id() ) ); - if ( in_array( $method_id, array_keys( $allowed_classes ) ) ) { - $wpdb->insert( - $wpdb->prefix . 'woocommerce_shipping_zone_methods', - array( - 'method_id' => $method_id , - 'zone_id' => $zone->get_id(), - 'method_order' => ( $count + 1 ) - ), - array( - '%s', - '%d', - '%d' - ) - ); - $instance_id = $wpdb->insert_id; - } - - if ( $instance_id ) { - do_action( 'woocommerce_shipping_zone_method_added', $instance_id, $method_id, $zone->get_id() ); - } - - WC_Cache_Helper::get_transient_version( 'shipping', true ); - + $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; @@ -178,7 +151,7 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co } if ( false === $method ) { - return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( "Resource cannot be created.", 'woocommerce' ), array( 'status' => 500 ) ); + 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 ); @@ -203,6 +176,7 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co $instance_id = (int) $request['instance_id']; $force = $request['force']; + $methods = $zone->get_shipping_methods(); $method = false; @@ -221,8 +195,9 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $method, $request ); + // Actually delete if ( $force ) { - $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) ); + $zone->delete_shipping_method( $instance_id ) ; } else { return new WP_Error( 'rest_trash_not_supported', __( 'Shipping methods do not support trashing.' ), array( 'status' => 501 ) ); } @@ -357,12 +332,12 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Co $data = array( 'id' => $id, 'label' => $field['title'], - 'description' => ( empty( $field['description'] ) ? '' : $field['description'] ), + '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'] ), + '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']; diff --git a/includes/class-wc-shipping-zone.php b/includes/class-wc-shipping-zone.php index 0a0e6e9b8c3..1c2e3180ea2 100644 --- a/includes/class-wc-shipping-zone.php +++ b/includes/class-wc-shipping-zone.php @@ -487,4 +487,25 @@ class WC_Shipping_Zone extends WC_Data { return $instance_id; } + + /** + * Delete a shipping method from a zone. + * @param int $instance_id + * @return True on success, false on failure + */ + public function delete_shipping_method( $instance_id ) { + global $wpdb; + + if ( null === $this->get_id() ) { + return false; + } + + $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) ); + do_action( 'woocommerce_shipping_zone_method_deleted', $instance_id, $this->get_id() ); + + WC_Cache_Helper::get_transient_version( 'shipping', true ); + + return true; + } + }