From 140b64ae04fa9c65e3c1f3e6c096a28f89f74e09 Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Fri, 5 Jul 2019 17:27:14 -0400 Subject: [PATCH 1/6] "deleteOrder" mutation implemented and tested. --- includes/class-actions.php | 2 + .../data/mutation/class-order-mutation.php | 9 +- includes/model/class-crud-cpt.php | 22 +++ includes/model/class-order.php | 2 +- includes/mutation/class-order-create.php | 2 +- includes/mutation/class-order-delete.php | 146 ++++++++++++++++ includes/mutation/class-order-update.php | 2 +- tests/_support/Helper/crud-helpers/order.php | 14 +- tests/wpunit/OrderMutationsTest.php | 158 +++++++++++++++++- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_static.php | 1 + 11 files changed, 341 insertions(+), 18 deletions(-) create mode 100644 includes/mutation/class-order-delete.php diff --git a/includes/class-actions.php b/includes/class-actions.php index 4adefbcd9..9b7ea3ce6 100644 --- a/includes/class-actions.php +++ b/includes/class-actions.php @@ -72,6 +72,7 @@ use WPGraphQL\Extensions\WooCommerce\Mutation\Cart_Add_Fee; use WPGraphQL\Extensions\WooCommerce\Mutation\Order_Create; use WPGraphQL\Extensions\WooCommerce\Mutation\Order_Update; +use WPGraphQL\Extensions\WooCommerce\Mutation\Order_Delete; /** * Class Actions @@ -165,5 +166,6 @@ public static function graphql_register_types() { Cart_Add_Fee::register_mutation(); Order_Create::register_mutation(); Order_Update::register_mutation(); + Order_Delete::register_mutation(); } } diff --git a/includes/data/mutation/class-order-mutation.php b/includes/data/mutation/class-order-mutation.php index 90fe98b52..c7a227591 100644 --- a/includes/data/mutation/class-order-mutation.php +++ b/includes/data/mutation/class-order-mutation.php @@ -337,12 +337,15 @@ public static function validate_customer( $input ) { /** * Purge object when creating. * - * @param WC_Order $order Object data. + * @param WC_Order|Order $order Object data. + * @param AppContext $context AppContext instance. + * @param ResolveInfo $info ResolveInfo instance. * * @return bool + * @throws UserError Failed to delete order. */ - public static function purge( $order ) { - if ( $order instanceof WC_Order ) { + public static function purge( $order, $context, $info ) { + if ( is_callable( array( $order, 'delete' ) ) ) { return $order->delete( true ); } diff --git a/includes/model/class-crud-cpt.php b/includes/model/class-crud-cpt.php index 2e7d69b11..acb15a6da 100644 --- a/includes/model/class-crud-cpt.php +++ b/includes/model/class-crud-cpt.php @@ -10,6 +10,7 @@ namespace WPGraphQL\Extensions\WooCommerce\Model; +use GraphQL\Error\UserError; use WPGraphQL\Model\Model; /** @@ -88,6 +89,27 @@ protected function is_private() { return false; } + /** + * Wrapper function for deleting + * + * @param boolean $force_delete Should the data be deleted permanently. + * + * @return boolean + * @throws UserError Not authorized. + */ + public function delete( $force_delete = false ) { + if ( ! current_user_can( $this->post_type_object->cap->edit_posts ) ) { + throw new UserError( + __( + 'User does not have the capabilities necessary to delete this object.', + 'wp-graphql-woocommerce' + ) + ); + } + + return $this->data->delete( $force_delete ); + } + /** * Retrieve the cap to check if the data should be restricted for the crud object * diff --git a/includes/model/class-order.php b/includes/model/class-order.php index 0ff64361a..6648ce9af 100644 --- a/includes/model/class-order.php +++ b/includes/model/class-order.php @@ -224,7 +224,7 @@ protected function init() { return ! is_null( $this->data->needs_processing() ) ? $this->data->needs_processing() : null; }, 'downloadableItems' => function() { - return ! is_null( $this->data->get_downloadable_items() ) ? $this->data->get_downloadable_items() : null; + return ! empty( $this->data->get_downloadable_items() ) ? $this->data->get_downloadable_items() : null; }, /** * Connection resolvers fields diff --git a/includes/mutation/class-order-create.php b/includes/mutation/class-order-create.php index 9caffda37..d7138114d 100644 --- a/includes/mutation/class-order-create.php +++ b/includes/mutation/class-order-create.php @@ -136,7 +136,7 @@ public static function mutate_and_get_payload() { $post_type_object = get_post_type_object( 'shop_order' ); if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { - throw new UserError( __( 'Sorry, you are not allowed to create a new order.', 'wp-graphql-woocommerce' ) ); + throw new UserError( __( 'User does not have the capabilities necessary to create an order.', 'wp-graphql-woocommerce' ) ); } // Create order. diff --git a/includes/mutation/class-order-delete.php b/includes/mutation/class-order-delete.php new file mode 100644 index 000000000..8dc364170 --- /dev/null +++ b/includes/mutation/class-order-delete.php @@ -0,0 +1,146 @@ + self::get_input_fields(), + 'outputFields' => self::get_output_fields(), + 'mutateAndGetPayload' => self::mutate_and_get_payload(), + ) + ); + } + + /** + * Defines the mutation input field configuration + * + * @return array + */ + public static function get_input_fields() { + $input_fields = array_merge( + array( + 'id' => array( + 'type' => 'ID', + 'description' => __( 'Order global ID', 'wp-graphql-woocommerce' ), + ), + 'order' => array( + 'type' => 'Int', + 'description' => __( 'Order WP ID', 'wp-graphql-woocommerce' ), + ), + ) + ); + + return $input_fields; + } + + /** + * Defines the mutation output field configuration + * + * @return array + */ + public static function get_output_fields() { + return array( + 'order' => array( + 'type' => 'Order', + 'resolve' => function( $payload ) { + return $payload['order']; + }, + ), + ); + } + + /** + * Defines the mutation data modification closure. + * + * @return callable + */ + public static function mutate_and_get_payload() { + return function( $input, AppContext $context, ResolveInfo $info ) { + $post_type_object = get_post_type_object( 'shop_order' ); + + if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { + throw new UserError( __( 'Sorry, you are not allowed to update this order.', 'wp-graphql-woocommerce' ) ); + } + + // Retrieve order ID. + $order_id = null; + if ( ! empty( $input['id'] ) ) { + $id_components = Relay::fromGlobalId( $input['id'] ); + if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) { + throw new UserError( __( 'The "id" provided is invalid', 'wp-graphql-woocommerce' ) ); + } + $order_id = absint( $id_components['id'] ); + } elseif ( ! empty( $input['orderId'] ) ) { + $order_id = absint( $input['orderId'] ); + } else { + throw new UserError( __( 'No order ID provided.', 'wp-graphql-woocommerce' ) ); + } + + // Get Order model instance for output. + $order = new Order( $order_id ); + + // Cache items to prevent null value errors. + // @codingStandardsIgnoreStart + $order->downloadableItems; + $order->get_items(); + $order->get_items( 'fee' ); + $order->get_items( 'shipping' ); + $order->get_items( 'tax' ); + $order->get_items( 'coupon' ); + // @codingStandardsIgnoreEnd. + + /** + * Action called before order is deleted. + * + * @param WC_Order $order WC_Order instance. + * @param array $props Order props array. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_before_order_delete', $order, $context, $info ); + + // Delete order. + $success = Order_Mutation::purge( + \WC_Order_Factory::get_order( $order->ID ), + $context, + $info + ); + + if ( ! $success ) { + throw new UserError( + sprintf( + /* translators: Deletion failed message */ + __( 'Removal of Order %d failed', 'wp-graphql-woocommerce' ), + $order->get_id() + ) + ); + } + + return array( 'order' => $order ); + }; + } +} diff --git a/includes/mutation/class-order-update.php b/includes/mutation/class-order-update.php index a9aa83339..bad879e8d 100644 --- a/includes/mutation/class-order-update.php +++ b/includes/mutation/class-order-update.php @@ -88,7 +88,7 @@ public static function mutate_and_get_payload() { $post_type_object = get_post_type_object( 'shop_order' ); if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { - throw new UserError( __( 'Sorry, you are not allowed to update this order.', 'wp-graphql-woocommerce' ) ); + throw new UserError( __( 'User does not have the capabilities necessary to update an order.', 'wp-graphql-woocommerce' ) ); } // Retrieve order ID. diff --git a/tests/_support/Helper/crud-helpers/order.php b/tests/_support/Helper/crud-helpers/order.php index 842c8aa4b..6ccd384e2 100644 --- a/tests/_support/Helper/crud-helpers/order.php +++ b/tests/_support/Helper/crud-helpers/order.php @@ -295,12 +295,14 @@ public function print_query( $id ) { 'isDownloadPermitted' => $data->is_download_permitted(), 'needsShippingAddress' => $data->needs_shipping_address(), 'hasDownloadableItem' => $data->has_downloadable_item(), - 'downloadableItems' => array_map( - function( $download ) { - return array( 'downloadId' => $download->get_id() ); - }, - $data->get_downloadable_items() - ), + 'downloadableItems' => ! empty( $data->get_downloadable_items() ) + ? array_map( + function( $download ) { + return array( 'downloadId' => $download->get_id() ); + }, + $data->get_downloadable_items() + ) + : null, 'needsPayment' => $data->needs_payment(), 'needsProcessing' => $data->needs_processing(), ); diff --git a/tests/wpunit/OrderMutationsTest.php b/tests/wpunit/OrderMutationsTest.php index 6eba99e2c..72c47dace 100644 --- a/tests/wpunit/OrderMutationsTest.php +++ b/tests/wpunit/OrderMutationsTest.php @@ -180,9 +180,6 @@ private function orderMutation( $input, $operation_name = 'createOrder', $input_ subtotalTax total totalTax - itemDownloads { - downloadId - } taxStatus product { id @@ -429,7 +426,6 @@ function( $item ) { 'subtotalTax' => ! empty( $item->get_subtotal_tax() ) ? $item->get_subtotal_tax() : null, 'total' => ! empty( $item->get_total() ) ? $item->get_total() : null, 'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null, - 'itemDownloads' => null, 'taxStatus' => strtoupper( $item->get_tax_status() ), 'product' => array( 'id' => $this->product->to_relay_id( $item->get_product_id() ) ), 'variation' => ! empty( $item->get_variation_id() ) @@ -753,7 +749,6 @@ function( $item ) { 'subtotalTax' => ! empty( $item->get_subtotal_tax() ) ? $item->get_subtotal_tax() : null, 'total' => ! empty( $item->get_total() ) ? $item->get_total() : null, 'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null, - 'itemDownloads' => null, 'taxStatus' => strtoupper( $item->get_tax_status() ), 'product' => array( 'id' => $this->product->to_relay_id( $item->get_product_id() ) ), 'variation' => ! empty( $item->get_variation_id() ) @@ -773,6 +768,157 @@ function( $item ) { ) ); - $this->assertEqualSets( $expected, $actual ); + $this->assertEquals( $expected, $actual ); + $this->assertNotEquals( $initial_response, $actual ); + } + + public function testDeleteOrderMutation() { + // Create products and coupons to be used in order creation. + $variable = $this->variation->create( $this->product->create_variable() ); + $product_ids = array( + $this->product->create_simple(), + $this->product->create_simple(), + $variable['product'], + ); + $coupon = new WC_Coupon( + $this->coupon->create( array( 'product_ids' => $product_ids ) ) + ); + + // Create initial order input. + $initial_input = array( + 'clientMutationId' => 'someId', + 'customerId' => $this->customer, + 'customerNote' => 'Customer test note', + 'coupons' => array( + $coupon->get_code(), + ), + 'paymentMethod' => 'bacs', + 'paymentMethodTitle' => 'Direct Bank Transfer', + 'billing' => array( + 'firstName' => 'May', + 'lastName' => 'Parker', + 'address1' => '20 Ingram St', + 'city' => 'New York City', + 'state' => 'NY', + 'postcode' => '12345', + 'country' => 'US', + 'email' => 'superfreak500@gmail.com', + 'phone' => '555-555-1234', + ), + 'shipping' => array( + 'firstName' => 'May', + 'lastName' => 'Parker', + 'address1' => '20 Ingram St', + 'city' => 'New York City', + 'state' => 'NY', + 'postcode' => '12345', + 'country' => 'US', + ), + 'lineItems' => array( + array( + 'productId' => $product_ids[0], + 'quantity' => 5, + 'metaData' => array( + array( + 'key' => 'test_product_key', + 'value' => 'test product value', + ), + ), + ), + array( + 'productId' => $product_ids[1], + 'quantity' => 2, + ), + array( + 'productId' => $product_ids[2], + 'quantity' => 6, + 'variationId' => $variable['variations'][0] + ), + ), + 'shippingLines' => array( + array( + 'methodId' => 'flat_rate_shipping', + 'methodTitle' => 'Flat Rate shipping', + 'total' => '10', + ), + ), + 'feeLines' => array( + array( + 'name' => 'Some Fee', + 'taxStatus' => 'TAXABLE', + 'total' => '100', + 'taxClass' => 'STANDARD', + ), + ), + 'metaData' => array( + array( + 'key' => 'test_key', + 'value' => 'test value', + ), + ), + 'isPaid' => false, + ); + + // Create order to delete. + wp_set_current_user( $this->shop_manager ); + $initial_response = $this->orderMutation( $initial_input ); + + // use --debug flag to view. + codecept_debug( $initial_response ); + + // Clear loader cache. + $this->getModule('\Helper\Wpunit')->clear_loader_cache( 'wc_post_crud' ); + + // Retrieve order and items + $order_id = $initial_response['data']['createOrder']['order']['orderId']; + $order = \WC_Order_Factory::get_order( $order_id ); + $line_items = $order->get_items(); + $shipping_lines = $order->get_items( 'shipping' ); + $fee_lines = $order->get_items( 'fee' ); + $coupon_lines = $order->get_items( 'coupon' ); + $tax_lines = $order->get_items( 'tax' ); + + // Create DeleteOrderInput. + $deleted_input = array( + 'clientMutationId' => 'someId', + 'id' => $this->order->to_relay_id( $order->get_id() ), + ); + + /** + * Assertion One + * + * User without necessary capabilities cannot delete order an order. + */ + wp_set_current_user( $this->customer ); + $actual = $this->orderMutation( + $deleted_input, + 'deleteOrder', + 'DeleteOrderInput' + ); + + // use --debug flag to view. + codecept_debug( $actual ); + + $this->assertArrayHasKey('errors', $actual ); + + /** + * Assertion Two + * + * Test mutation and input. + */ + wp_set_current_user( $this->shop_manager ); + $actual = $this->orderMutation( + $deleted_input, + 'deleteOrder', + 'DeleteOrderInput' + ); + + // use --debug flag to view. + codecept_debug( $actual ); + + $this->assertArrayHasKey( 'data', $actual ); + $this->assertArrayHasKey( 'deleteOrder', $actual['data'] ); + $this->assertEquals( $initial_response['data']['createOrder'], $actual['data']['deleteOrder'] ); + $this->assertFalse( \WC_Order_Factory::get_order( $order->get_id() ) ); } } \ No newline at end of file diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index e8e0a0cec..4c5e730b7 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -64,6 +64,7 @@ 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Customer_Register' => $baseDir . '/includes/mutation/class-customer-register.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Customer_Update' => $baseDir . '/includes/mutation/class-customer-update.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Create' => $baseDir . '/includes/mutation/class-order-create.php', + 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Delete' => $baseDir . '/includes/mutation/class-order-delete.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Update' => $baseDir . '/includes/mutation/class-order-update.php', 'WPGraphQL\\Extensions\\WooCommerce\\Type\\WPEnum\\Backorders' => $baseDir . '/includes/type/enum/class-backorders.php', 'WPGraphQL\\Extensions\\WooCommerce\\Type\\WPEnum\\Catalog_Visibility' => $baseDir . '/includes/type/enum/class-catalog-visibility.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 83aa999ff..b4217724a 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -79,6 +79,7 @@ class ComposerStaticInitee0d17af17b841ed3a93c4a0e5cc5e5f 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Customer_Register' => __DIR__ . '/../..' . '/includes/mutation/class-customer-register.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Customer_Update' => __DIR__ . '/../..' . '/includes/mutation/class-customer-update.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Create' => __DIR__ . '/../..' . '/includes/mutation/class-order-create.php', + 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Delete' => __DIR__ . '/../..' . '/includes/mutation/class-order-delete.php', 'WPGraphQL\\Extensions\\WooCommerce\\Mutation\\Order_Update' => __DIR__ . '/../..' . '/includes/mutation/class-order-update.php', 'WPGraphQL\\Extensions\\WooCommerce\\Type\\WPEnum\\Backorders' => __DIR__ . '/../..' . '/includes/type/enum/class-backorders.php', 'WPGraphQL\\Extensions\\WooCommerce\\Type\\WPEnum\\Catalog_Visibility' => __DIR__ . '/../..' . '/includes/type/enum/class-catalog-visibility.php', From c10613cc6c8e17912117da4bdac99459fe5bdfd8 Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Fri, 5 Jul 2019 17:33:30 -0400 Subject: [PATCH 2/6] input field name corrected from "order" to "orderId". --- includes/mutation/class-order-delete.php | 6 +++--- includes/mutation/class-order-update.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/mutation/class-order-delete.php b/includes/mutation/class-order-delete.php index 8dc364170..2a5972679 100644 --- a/includes/mutation/class-order-delete.php +++ b/includes/mutation/class-order-delete.php @@ -43,11 +43,11 @@ public static function register_mutation() { public static function get_input_fields() { $input_fields = array_merge( array( - 'id' => array( + 'id' => array( 'type' => 'ID', 'description' => __( 'Order global ID', 'wp-graphql-woocommerce' ), ), - 'order' => array( + 'orderId' => array( 'type' => 'Int', 'description' => __( 'Order WP ID', 'wp-graphql-woocommerce' ), ), @@ -83,7 +83,7 @@ public static function mutate_and_get_payload() { $post_type_object = get_post_type_object( 'shop_order' ); if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { - throw new UserError( __( 'Sorry, you are not allowed to update this order.', 'wp-graphql-woocommerce' ) ); + throw new UserError( __( 'User does not have the capabilities necessary to delete an order.', 'wp-graphql-woocommerce' ) ); } // Retrieve order ID. diff --git a/includes/mutation/class-order-update.php b/includes/mutation/class-order-update.php index bad879e8d..a21d8d032 100644 --- a/includes/mutation/class-order-update.php +++ b/includes/mutation/class-order-update.php @@ -48,7 +48,7 @@ public static function get_input_fields() { 'type' => 'ID', 'description' => __( 'Order global ID', 'wp-graphql-woocommerce' ), ), - 'order' => array( + 'orderId' => array( 'type' => 'Int', 'description' => __( 'Order WP ID', 'wp-graphql-woocommerce' ), ), From 2ee563d4b0da3aac7db45e7cec4b9aba6727dbeb Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Fri, 5 Jul 2019 17:40:38 -0400 Subject: [PATCH 3/6] "forceDelete" input field added for choosing between deletion and recycling. --- .../data/mutation/class-order-mutation.php | 9 ++++----- includes/mutation/class-order-delete.php | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/includes/data/mutation/class-order-mutation.php b/includes/data/mutation/class-order-mutation.php index c7a227591..78b482514 100644 --- a/includes/data/mutation/class-order-mutation.php +++ b/includes/data/mutation/class-order-mutation.php @@ -337,16 +337,15 @@ public static function validate_customer( $input ) { /** * Purge object when creating. * - * @param WC_Order|Order $order Object data. - * @param AppContext $context AppContext instance. - * @param ResolveInfo $info ResolveInfo instance. + * @param WC_Order|Order $order Object data. + * @param boolean $force_delete Delete or put in trash. * * @return bool * @throws UserError Failed to delete order. */ - public static function purge( $order, $context, $info ) { + public static function purge( $order, $force_delete = true ) { if ( is_callable( array( $order, 'delete' ) ) ) { - return $order->delete( true ); + return $order->delete( $force_delete ); } return false; diff --git a/includes/mutation/class-order-delete.php b/includes/mutation/class-order-delete.php index 2a5972679..787c3a4c1 100644 --- a/includes/mutation/class-order-delete.php +++ b/includes/mutation/class-order-delete.php @@ -43,14 +43,18 @@ public static function register_mutation() { public static function get_input_fields() { $input_fields = array_merge( array( - 'id' => array( + 'id' => array( 'type' => 'ID', 'description' => __( 'Order global ID', 'wp-graphql-woocommerce' ), ), - 'orderId' => array( + 'orderId' => array( 'type' => 'Int', 'description' => __( 'Order WP ID', 'wp-graphql-woocommerce' ), ), + 'forceDelete' => array( + 'type' => 'Boolean', + 'description' => __( 'Delete or simply place in trash.', 'wp-graphql-woocommerce' ), + ), ) ); @@ -100,6 +104,11 @@ public static function mutate_and_get_payload() { throw new UserError( __( 'No order ID provided.', 'wp-graphql-woocommerce' ) ); } + $force_delete = false; + if ( ! empty( $input['forceDelete'] ) ) { + $force_delete = $input['forceDelete']; + } + // Get Order model instance for output. $order = new Order( $order_id ); @@ -124,11 +133,7 @@ public static function mutate_and_get_payload() { do_action( 'woocommerce_graphql_before_order_delete', $order, $context, $info ); // Delete order. - $success = Order_Mutation::purge( - \WC_Order_Factory::get_order( $order->ID ), - $context, - $info - ); + $success = Order_Mutation::purge( \WC_Order_Factory::get_order( $order->ID ), $force_delete ); if ( ! $success ) { throw new UserError( From bb4f5cbd50b5bde8c0f943509874bed73ddbe8aa Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Fri, 5 Jul 2019 17:50:27 -0400 Subject: [PATCH 4/6] "testDeleteOrderMutation" updated --- tests/wpunit/OrderMutationsTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/wpunit/OrderMutationsTest.php b/tests/wpunit/OrderMutationsTest.php index 72c47dace..0c2b20edc 100644 --- a/tests/wpunit/OrderMutationsTest.php +++ b/tests/wpunit/OrderMutationsTest.php @@ -882,6 +882,7 @@ public function testDeleteOrderMutation() { $deleted_input = array( 'clientMutationId' => 'someId', 'id' => $this->order->to_relay_id( $order->get_id() ), + 'forceDelete' => true, ); /** From fd80867ac0ca247dbdddc593e2da554fbfc678b4 Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Sat, 6 Jul 2019 10:22:07 -0400 Subject: [PATCH 5/6] Authorization hook implemented. --- .../data/mutation/class-order-mutation.php | 19 +++++++++++++++++++ includes/mutation/class-order-create.php | 4 +--- includes/mutation/class-order-delete.php | 4 +--- includes/mutation/class-order-update.php | 4 +--- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/includes/data/mutation/class-order-mutation.php b/includes/data/mutation/class-order-mutation.php index 78b482514..614de7a84 100644 --- a/includes/data/mutation/class-order-mutation.php +++ b/includes/data/mutation/class-order-mutation.php @@ -14,6 +14,25 @@ * Class - Order_Mutation */ class Order_Mutation { + /** + * Filterable authentication function. + * + * @param string $mutation Mutation being executed. + * + * @return boolean + */ + public static function authorized( $mutation = 'create', $input, $context, $info ) { + $post_type_object = get_post_type_object( 'shop_order' ); + + return apply_filters( + "authorized_to_{$mutation}_orders", + ! current_user_can( $post_type_object->cap->create_posts ), + $input, + $context, + $info + ); + } + /** * Create an order. * diff --git a/includes/mutation/class-order-create.php b/includes/mutation/class-order-create.php index d7138114d..91c17ecd3 100644 --- a/includes/mutation/class-order-create.php +++ b/includes/mutation/class-order-create.php @@ -133,9 +133,7 @@ public static function get_output_fields() { */ public static function mutate_and_get_payload() { return function( $input, AppContext $context, ResolveInfo $info ) { - $post_type_object = get_post_type_object( 'shop_order' ); - - if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { + if ( Order_Mutation::authorized( 'create', $input, $context, $info ) ) { throw new UserError( __( 'User does not have the capabilities necessary to create an order.', 'wp-graphql-woocommerce' ) ); } diff --git a/includes/mutation/class-order-delete.php b/includes/mutation/class-order-delete.php index 787c3a4c1..2d174186d 100644 --- a/includes/mutation/class-order-delete.php +++ b/includes/mutation/class-order-delete.php @@ -84,9 +84,7 @@ public static function get_output_fields() { */ public static function mutate_and_get_payload() { return function( $input, AppContext $context, ResolveInfo $info ) { - $post_type_object = get_post_type_object( 'shop_order' ); - - if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { + if ( Order_Mutation::authorized( 'delete', $input, $context, $info ) ) { throw new UserError( __( 'User does not have the capabilities necessary to delete an order.', 'wp-graphql-woocommerce' ) ); } diff --git a/includes/mutation/class-order-update.php b/includes/mutation/class-order-update.php index a21d8d032..5ee0d6cba 100644 --- a/includes/mutation/class-order-update.php +++ b/includes/mutation/class-order-update.php @@ -85,9 +85,7 @@ public static function get_output_fields() { */ public static function mutate_and_get_payload() { return function( $input, AppContext $context, ResolveInfo $info ) { - $post_type_object = get_post_type_object( 'shop_order' ); - - if ( ! current_user_can( $post_type_object->cap->create_posts ) ) { + if ( Order_Mutation::authorized( 'update', $input, $context, $info ) ) { throw new UserError( __( 'User does not have the capabilities necessary to update an order.', 'wp-graphql-woocommerce' ) ); } From 9cb22dd3951a0776a12f06a6c77694d18db445cc Mon Sep 17 00:00:00 2001 From: Geoff Taylor Date: Sat, 6 Jul 2019 11:00:43 -0400 Subject: [PATCH 6/6] More hooks added --- .../data/mutation/class-order-mutation.php | 48 ++++++++++++++++++- includes/mutation/class-order-create.php | 10 ++++ includes/mutation/class-order-delete.php | 14 +++++- includes/mutation/class-order-update.php | 20 ++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/includes/data/mutation/class-order-mutation.php b/includes/data/mutation/class-order-mutation.php index 614de7a84..6f7b89e1a 100644 --- a/includes/data/mutation/class-order-mutation.php +++ b/includes/data/mutation/class-order-mutation.php @@ -17,7 +17,10 @@ class Order_Mutation { /** * Filterable authentication function. * - * @param string $mutation Mutation being executed. + * @param string $mutation Mutation being executed. + * @param array $input Input data describing order. + * @param AppContext $context AppContext instance. + * @param ResolveInfo $info ResolveInfo instance. * * @return boolean */ @@ -61,11 +64,31 @@ public static function create_order( $input, $context, $info ) { } } + /** + * Action called before order is created. + * + * @param WC_Order $order WC_Order instance. + * @param array $input Input data describing order. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_before_order_create', $input, $context, $info ); + $order = \wc_create_order( $args ); if ( is_wp_error( $order ) ) { throw UserError( $order->get_error_code() . $order->get_message() ); } + /** + * Action called after order is created. + * + * @param WC_Order $order WC_Order instance. + * @param array $input Input data describing order. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_after_order_create', $order, $input, $context, $info ); + return $order->get_id(); } @@ -88,6 +111,17 @@ public static function add_items( $input, $order_id, $context, $info ) { foreach ( $input as $key => $items ) { if ( array_key_exists( $key, $item_group_keys ) ) { $type = $item_group_keys[ $key ]; + + /** + * Action called before an item group is added to an order. + * + * @param array $items Item data being added. + * @param integer $order_id ID of target order. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( "woocommerce_graphql_before_{$type}s_added_to_order", $items, $order_id, $context, $info ); + foreach ( $items as $item_data ) { // Create Order item. $item_id = ( ! empty( $item_data['id'] ) && \WC_Order_Factory::get_order_item( $item_data['id'] ) ) @@ -103,6 +137,16 @@ public static function add_items( $input, $order_id, $context, $info ) { $item_keys = self::get_order_item_keys( $type ); self::map_input_to_item( $item_id, $item_data, $item_keys, $context, $info ); } + + /** + * Action called after an item group is added to an order. + * + * @param array $items Item data being added. + * @param integer $order_id ID of target order. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( "woocommerce_graphql_after_{$type}s_added_to_order", $items, $order_id, $context, $info ); } } } @@ -274,7 +318,7 @@ public static function add_order_meta( $order_id, $input, $context, $info ) { } /** - * Action called before order meta saved. + * Action called before changes to order meta are saved. * * @param WC_Order $order WC_Order instance. * @param array $props Order props array. diff --git a/includes/mutation/class-order-create.php b/includes/mutation/class-order-create.php index 91c17ecd3..25daf9513 100644 --- a/includes/mutation/class-order-create.php +++ b/includes/mutation/class-order-create.php @@ -179,6 +179,16 @@ public static function mutate_and_get_payload() { ); } + /** + * Action called after order is created. + * + * @param WC_Order $order WC_Order instance. + * @param array $input Input data describing order. + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_after_order_create', $order, $input, $context, $info ); + return array( 'id' => $order->get_id() ); } catch ( \Exception $e ) { Order_Mutation::purge( $order ); diff --git a/includes/mutation/class-order-delete.php b/includes/mutation/class-order-delete.php index 2d174186d..4411f4964 100644 --- a/includes/mutation/class-order-delete.php +++ b/includes/mutation/class-order-delete.php @@ -124,11 +124,11 @@ public static function mutate_and_get_payload() { * Action called before order is deleted. * * @param WC_Order $order WC_Order instance. - * @param array $props Order props array. + * @param array $input Input data describing order. * @param AppContext $context Request AppContext instance. * @param ResolveInfo $info Request ResolveInfo instance. */ - do_action( 'woocommerce_graphql_before_order_delete', $order, $context, $info ); + do_action( 'woocommerce_graphql_before_order_delete', $order, $input, $context, $info ); // Delete order. $success = Order_Mutation::purge( \WC_Order_Factory::get_order( $order->ID ), $force_delete ); @@ -143,6 +143,16 @@ public static function mutate_and_get_payload() { ); } + /** + * Action called before order is deleted. + * + * @param WC_Order $order WC_Order instance. + * @param array $input Input data describing order + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_after_order_delete', $order, $input, $context, $info ); + return array( 'order' => $order ); }; } diff --git a/includes/mutation/class-order-update.php b/includes/mutation/class-order-update.php index 5ee0d6cba..dcfca69d0 100644 --- a/includes/mutation/class-order-update.php +++ b/includes/mutation/class-order-update.php @@ -103,6 +103,16 @@ public static function mutate_and_get_payload() { throw new UserError( __( 'No order ID provided.', 'wp-graphql-woocommerce' ) ); } + /** + * Action called before order is updated. + * + * @param int $order_id Order ID. + * @param array $input Input data describing order + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_before_order_update', $order_id, $input, $context, $info ); + Order_Mutation::add_order_meta( $order_id, $input, $context, $info ); Order_Mutation::add_items( $input, $order_id, $context, $info ); @@ -141,6 +151,16 @@ public static function mutate_and_get_payload() { ); } + /** + * Action called after order is updated. + * + * @param WC_Order $order WC_Order instance. + * @param array $input Input data describing order + * @param AppContext $context Request AppContext instance. + * @param ResolveInfo $info Request ResolveInfo instance. + */ + do_action( 'woocommerce_graphql_after_order_update', $order, $input, $context, $info ); + return array( 'id' => $order->get_id() ); }; }