From 0e3c79ebb0263909d7fedc2a0243bf15ca92c65a Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Wed, 28 Feb 2024 22:00:53 -0300 Subject: [PATCH] Add custom order fields drop-down to custom fields metabox under HPOS (#44739) This PR brings to HPOS the dropdown of custom fields that is available for regular posts in the WP admin edit screen. This dropdown lists a selection of non-private meta keys from all other orders allowing easy re-use. Fixes #44029 --- plugins/woocommerce/changelog/fix-44029 | 4 + .../woocommerce/client/legacy/css/admin.scss | 11 ++ .../Admin/Orders/MetaBoxes/CustomMetaBox.php | 115 ++++++++++-------- .../DataStores/CustomMetaDataStore.php | 31 +++++ 4 files changed, 113 insertions(+), 48 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-44029 diff --git a/plugins/woocommerce/changelog/fix-44029 b/plugins/woocommerce/changelog/fix-44029 new file mode 100644 index 0000000000000..ec3c34c156530 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-44029 @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Add used meta keys dropdown in HPOS custom fields metabox. diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index d13df5a096f6d..15e7f99f5faaa 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -2786,6 +2786,17 @@ ul.wc_coupon_list_block { } } +#order_custom #postcustomstuff { + .add-custom-field { + padding: 12px 8px 8px; + } + + .submit { + border: 0 none; + float: none; + } +} + #side-sortables #woocommerce-order-downloads { .buttons, .select2-container { diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomMetaBox.php b/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomMetaBox.php index dd44808ca72ee..66bb48b1f1a66 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomMetaBox.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomMetaBox.php @@ -5,8 +5,8 @@ namespace Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes; -use WC_Data_Store; -use WC_Meta_Data; +use Automattic\WooCommerce\Internal\DataStores\CustomMetaDataStore; +use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStoreMeta; use WC_Order; use WP_Ajax_Response; @@ -92,16 +92,44 @@ private function render_custom_meta_form( array $metadata_to_list, \WC_Order $or * Compute keys to display in autofill when adding new meta key entry in custom meta box. * Currently, returns empty keys, will be implemented after caching is merged. * - * @param array|null $keys Keys to display in autofill. - * @param \WP_Post|\WC_Order $order Order object. + * @param mixed $deprecated Unused argument. For backwards compatibility. + * @param \WP_Post|\WC_Order $order Order object. * - * @return array|mixed Array of keys to display in autofill. + * @return array Array of keys to display in autofill. */ - public function order_meta_keys_autofill( $keys, $order ) { - if ( is_a( $order, \WC_Order::class ) ) { + public function order_meta_keys_autofill( $deprecated, $order ) { + if ( ! is_a( $order, \WC_Order::class ) ) { return array(); } + /** + * Filters values for the meta key dropdown in the Custom Fields meta box. + * + * Compatibility filter for `postmeta_form_keys` filter. + * + * @since 6.9.0 + * + * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null. + * @param \WC_Order $order The current post object. + */ + $keys = apply_filters( 'postmeta_form_keys', null, $order ); + if ( null === $keys || ! is_array( $keys ) ) { + /** + * Compatibility filter for 'postmeta_form_limit', which filters the number of custom fields to retrieve + * for the drop-down in the Custom Fields meta box. + * + * @since 8.8.0 + * + * @param int $limit Number of custom fields to retrieve. Default 30. + */ + $limit = apply_filters( 'postmeta_form_limit', 30 ); + $keys = wc_get_container()->get( OrdersTableDataStoreMeta::class )->get_meta_keys( $limit ); + } + + if ( $keys ) { + natcasesort( $keys ); + } + return $keys; } @@ -116,17 +144,6 @@ public function render_meta_form( \WC_Order $order ) : void { $meta_key_input_id = 'metakeyselect'; $keys = $this->order_meta_keys_autofill( null, $order ); - /** - * Filters values for the meta key dropdown in the Custom Fields meta box. - * - * Compatibility filter for `postmeta_form_keys` filter. - * - * @since 6.9.0 - * - * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null. - * @param \WC_Order $order The current post object. - */ - $keys = apply_filters( 'postmeta_form_keys', $keys, $order ); ?>

@@ -145,43 +162,42 @@ public function render_meta_form( \WC_Order $order ) : void { get_id() ) ) { + if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'edit_others_shop_orders' ) ) { continue; } echo "\n'; } ?> - - - - + + + - -
+ +
-
- 'newmeta-submit', - 'data-wp-lists' => 'add:the-list:newmeta', - ) - ); - ?> -
- -
+ +
+ 'newmeta-submit', + 'data-wp-lists' => 'add:the-list:newmeta', + ) + ); + ?> +
verify_order_edit_permission_for_ajax( $order_id ); - if ( isset( $_POST['metakeyselect'] ) && '#NONE#' === $_POST['metakeyselect'] && empty( $_POST['metakeyinput'] ) ) { + $select_meta_key = trim( sanitize_text_field( wp_unslash( $_POST['metakeyselect'] ?? '' ) ) ); + $input_meta_key = trim( sanitize_text_field( wp_unslash( $_POST['metakeyinput'] ?? '' ) ) ); + + if ( empty( $_POST['meta'] ) && in_array( $select_meta_key, array( '', '#NONE#' ), true ) && ! $input_meta_key ) { wp_die( 1 ); } - if ( isset( $_POST['metakeyinput'] ) ) { // add meta. - $meta_key = sanitize_text_field( wp_unslash( $_POST['metakeyinput'] ) ); + if ( ! empty( $_POST['meta'] ) ) { // update. + $meta = wp_unslash( $_POST['meta'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitization done below in array_walk. + $this->handle_update_meta( $order, $meta ); + } else { // add meta. $meta_value = sanitize_text_field( wp_unslash( $_POST['metavalue'] ?? '' ) ); + $meta_key = $input_meta_key ? $input_meta_key : $select_meta_key; $this->handle_add_meta( $order, $meta_key, $meta_value ); - } else { // update. - $meta = wp_unslash( $_POST['meta'] ?? array() ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitization done below in array_walk. - $this->handle_update_meta( $order, $meta ); } } diff --git a/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php b/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php index fc3082b1decde..06cca2d7da757 100644 --- a/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php +++ b/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php @@ -232,4 +232,35 @@ public function get_metadata_by_key( &$object, string $meta_key ) { return $meta; } + /** + * Returns distinct meta keys in use. + * + * @since 8.8.0 + * + * @param int $limit Maximum number of meta keys to return. Defaults to 100. + * @param string $order Order to use for the results. Either 'ASC' or 'DESC'. Defaults to 'ASC'. + * @param bool $include_private Whether to include private meta keys in the results. Defaults to FALSE. + * @return string[] + */ + public function get_meta_keys( $limit = 100, $order = 'ASC', $include_private = false ) { + global $wpdb; + + $db_info = $this->get_db_info(); + + $query = "SELECT DISTINCT meta_key FROM {$db_info['table']} "; + + if ( ! $include_private ) { + $query .= $wpdb->prepare( 'WHERE meta_key NOT LIKE %s ', $wpdb->esc_like( '_' ) . '%' ); + } + + $order = in_array( strtoupper( $order ), array( 'ASC', 'DESC' ), true ) ? $order : 'ASC'; + $query .= 'ORDER BY meta_key ' . $order . ' '; + + if ( $limit ) { + $query .= $wpdb->prepare( 'LIMIT %d ', $limit ); + } + + return $wpdb->get_col( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query is prepared. + } + }