From 43ce3c1c4d991f90aa0c95818f686f794a516c31 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Fri, 6 Sep 2024 12:13:03 +0800 Subject: [PATCH] [WC Analytics] Add filters for custom orderby query params (#49722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a filter `woocommerce_report_categories_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_coupons_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_coupons_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_customers_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_downloads_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_downloads_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_orders_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_orders_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_products_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_products_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_revenue_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_stock_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_taxes_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_taxes_stats_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_variations_orderby_params` for custom orderby query params * Add a filter `woocommerce_report_variations_stats_orderby_params` for custom orderby query params * Add changelog * Add a method `apply_custom_orderby_filters()` in analytics GenericController * Use the shared method to in analytics GenericController to apply custom orderby enum * Fix typo, add more comments in analytics custom orderby filter PHPDoc --------- Co-authored-by: Tomek Wytrębowicz --- ...46275-custom-order-by-for-analytics-report | 4 +++ .../API/Reports/Categories/Controller.php | 16 ++++++----- .../Admin/API/Reports/Coupons/Controller.php | 12 +++++---- .../API/Reports/Coupons/Stats/Controller.php | 12 +++++---- .../API/Reports/Customers/Controller.php | 26 +++++++++--------- .../API/Reports/Downloads/Controller.php | 8 +++--- .../Reports/Downloads/Stats/Controller.php | 8 +++--- .../Admin/API/Reports/GenericController.php | 27 +++++++++++++++++++ .../Admin/API/Reports/Orders/Controller.php | 10 ++++--- .../API/Reports/Orders/Stats/Controller.php | 12 +++++---- .../Admin/API/Reports/Products/Controller.php | 18 +++++++------ .../API/Reports/Products/Stats/Controller.php | 22 ++++++++------- .../API/Reports/Revenue/Stats/Controller.php | 24 +++++++++-------- .../Admin/API/Reports/Stock/Controller.php | 18 +++++++------ .../Admin/API/Reports/Taxes/Controller.php | 20 +++++++------- .../API/Reports/Taxes/Stats/Controller.php | 14 +++++----- .../API/Reports/Variations/Controller.php | 14 +++++----- .../Reports/Variations/Stats/Controller.php | 22 ++++++++------- 18 files changed, 175 insertions(+), 112 deletions(-) create mode 100644 plugins/woocommerce/changelog/add-46275-custom-order-by-for-analytics-report diff --git a/plugins/woocommerce/changelog/add-46275-custom-order-by-for-analytics-report b/plugins/woocommerce/changelog/add-46275-custom-order-by-for-analytics-report new file mode 100644 index 00000000000..0c1f01e9754 --- /dev/null +++ b/plugins/woocommerce/changelog/add-46275-custom-order-by-for-analytics-report @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add filters for custom orderby query params for WC Analytics diff --git a/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php index 463ed1e3482..6a32808bc32 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php @@ -171,13 +171,15 @@ class Controller extends GenericController implements ExportableInterface { public function get_collection_params() { $params = parent::get_collection_params(); $params['orderby']['default'] = 'category_id'; - $params['orderby']['enum'] = array( - 'category_id', - 'items_sold', - 'net_revenue', - 'orders_count', - 'products_count', - 'category', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'category_id', + 'items_sold', + 'net_revenue', + 'orders_count', + 'products_count', + 'category', + ) ); $params['interval'] = array( 'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php index d7f1fd5d818..71944e820dd 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php @@ -184,11 +184,13 @@ class Controller extends GenericController implements ExportableInterface { public function get_collection_params() { $params = parent::get_collection_params(); $params['orderby']['default'] = 'coupon_id'; - $params['orderby']['enum'] = array( - 'coupon_id', - 'code', - 'amount', - 'orders_count', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'coupon_id', + 'code', + 'amount', + 'orders_count', + ) ); $params['coupons'] = array( 'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php index 593b3c28915..63e02dde0db 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php @@ -142,11 +142,13 @@ class Controller extends GenericStatsController { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'amount', - 'coupons_count', - 'orders_count', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'amount', + 'coupons_count', + 'orders_count', + ) ); $params['coupons'] = array( 'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php index 003fc089b0d..56861a82c9c 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php @@ -304,18 +304,20 @@ class Controller extends GenericController implements ExportableInterface { 'validate_callback' => 'rest_validate_request_arg', ); $params['orderby']['default'] = 'date_registered'; - $params['orderby']['enum'] = array( - 'username', - 'name', - 'country', - 'city', - 'state', - 'postcode', - 'date_registered', - 'date_last_active', - 'orders_count', - 'total_spend', - 'avg_order_value', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'username', + 'name', + 'country', + 'city', + 'state', + 'postcode', + 'date_registered', + 'date_last_active', + 'orders_count', + 'total_spend', + 'avg_order_value', + ) ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php index 1330e2ad4e1..fa79cd25ea5 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php @@ -219,9 +219,11 @@ class Controller extends GenericController implements ExportableInterface { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'product', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'product', + ) ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: products, orders, username, ip_address.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php index 9d8e5f1bed2..fb445a91d26 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php @@ -196,9 +196,11 @@ class Controller extends GenericStatsController { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'download_count', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'download_count', + ) ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/GenericController.php b/plugins/woocommerce/src/Admin/API/Reports/GenericController.php index 2910e23b7d9..079ab68138a 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/GenericController.php +++ b/plugins/woocommerce/src/Admin/API/Reports/GenericController.php @@ -257,4 +257,31 @@ abstract class GenericController extends \WC_REST_Reports_Controller { return $args; } + + /** + * Apply a filter for custom orderby enum. + * + * @param array $orderby_enum An array of orderby enum options. + * + * @return array An array of filtered orderby enum options. + * + * @since 9.4.0 + */ + protected function apply_custom_orderby_filters( $orderby_enum ) { + /** + * Filter orderby query parameter enum. + * + * There was an initial concern about potential SQL injection with the custom orderby. + * However, testing shows it is safely blocked by validation in the controller, + * which results in an "Invalid parameter(s): orderby" error. + * + * Additionally, it's the responsibility of the merchant/developer to ensure the custom orderby is valid, + * or a WordPress database error will occur for unknown columns. + * + * @since 9.4.0 + * + * @param array $orderby_enum The orderby query parameter enum. + */ + return apply_filters( "woocommerce_analytics_orderby_enum_{$this->rest_base}", $orderby_enum ); + } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php index 2a6b81fad94..a30341e6d73 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php @@ -233,10 +233,12 @@ class Controller extends GenericController implements ExportableInterface { public function get_collection_params() { $params = parent::get_collection_params(); $params['per_page']['minimum'] = 0; - $params['orderby']['enum'] = array( - 'date', - 'num_items_sold', - 'net_total', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'num_items_sold', + 'net_total', + ) ); $params['product_includes'] = array( 'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php index ee5d3f24a4f..f050291a0ce 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php @@ -202,11 +202,13 @@ class Controller extends GenericStatsController { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'net_revenue', - 'orders_count', - 'avg_order_value', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'net_revenue', + 'orders_count', + 'avg_order_value', + ) ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php index 60df83d6800..9f1728bbf59 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php @@ -231,14 +231,16 @@ class Controller extends GenericController implements ExportableInterface { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'net_revenue', - 'orders_count', - 'items_sold', - 'product_name', - 'variations', - 'sku', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'net_revenue', + 'orders_count', + 'items_sold', + 'product_name', + 'variations', + 'sku', + ) ); $params['categories'] = array( 'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php index acf047a37f9..2cccdbf046e 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php @@ -197,16 +197,18 @@ class Controller extends GenericStatsController { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'net_revenue', - 'coupons', - 'refunds', - 'shipping', - 'taxes', - 'net_revenue', - 'orders_count', - 'items_sold', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'net_revenue', + 'coupons', + 'refunds', + 'shipping', + 'taxes', + 'net_revenue', + 'orders_count', + 'items_sold', + ) ); $params['categories'] = array( 'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php index 826153a3b60..097588a658e 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php @@ -223,17 +223,19 @@ class Controller extends GenericStatsController implements ExportableInterface { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'total_sales', - 'coupons', - 'refunds', - 'shipping', - 'taxes', - 'net_revenue', - 'orders_count', - 'items_sold', - 'gross_sales', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'total_sales', + 'coupons', + 'refunds', + 'shipping', + 'taxes', + 'net_revenue', + 'orders_count', + 'items_sold', + 'gross_sales', + ) ); $params['segmentby'] = array( 'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php index 11524a422f0..6fadf4f377d 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php @@ -443,14 +443,16 @@ class Controller extends GenericController implements ExportableInterface { ); $params['order']['default'] = 'asc'; $params['orderby']['default'] = 'stock_status'; - $params['orderby']['enum'] = array( - 'stock_status', - 'stock_quantity', - 'date', - 'id', - 'include', - 'title', - 'sku', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'stock_status', + 'stock_quantity', + 'date', + 'id', + 'include', + 'title', + 'sku', + ) ); $params['parent'] = array( 'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php index 9d6cbd54364..d31db463c0c 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php @@ -195,15 +195,17 @@ class Controller extends GenericController implements ExportableInterface { public function get_collection_params() { $params = parent::get_collection_params(); $params['orderby']['default'] = 'tax_rate_id'; - $params['orderby']['enum'] = array( - 'name', - 'tax_rate_id', - 'tax_code', - 'rate', - 'order_tax', - 'total_tax', - 'shipping_tax', - 'orders_count', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'name', + 'tax_rate_id', + 'tax_code', + 'rate', + 'order_tax', + 'total_tax', + 'shipping_tax', + 'orders_count', + ) ); $params['taxes'] = array( 'description' => __( 'Limit result set to items assigned one or more tax rates.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php index f551d54d04e..be02d4a5f5e 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php @@ -186,12 +186,14 @@ class Controller extends GenericStatsController { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'items_sold', - 'total_sales', - 'orders_count', - 'products_count', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'items_sold', + 'total_sales', + 'orders_count', + 'products_count', + ) ); $params['taxes'] = array( 'description' => __( 'Limit result set to all items that have the specified term assigned in the taxes taxonomy.', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php index 43c499e2636..a054dbef1d6 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php @@ -246,12 +246,14 @@ class Controller extends GenericController implements ExportableInterface { */ public function get_collection_params() { $params = parent::get_collection_params(); - $params['orderby']['enum'] = array( - 'date', - 'net_revenue', - 'orders_count', - 'items_sold', - 'sku', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'net_revenue', + 'orders_count', + 'items_sold', + 'sku', + ) ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php index 68e6590dce7..7bcd87f657b 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php @@ -210,16 +210,18 @@ class Controller extends GenericStatsController { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['orderby']['enum'] = array( - 'date', - 'net_revenue', - 'coupons', - 'refunds', - 'shipping', - 'taxes', - 'net_revenue', - 'orders_count', - 'items_sold', + $params['orderby']['enum'] = $this->apply_custom_orderby_filters( + array( + 'date', + 'net_revenue', + 'coupons', + 'refunds', + 'shipping', + 'taxes', + 'net_revenue', + 'orders_count', + 'items_sold', + ) ); $params['category_includes'] = array( 'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ),