Skip to content

Commit

Permalink
Add custom order fields drop-down to custom fields metabox under HPOS (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
jorgeatorres authored and Konamiman committed Mar 13, 2024
1 parent f0f66ca commit 0e3c79e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 48 deletions.
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/fix-44029
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Add used meta keys dropdown in HPOS custom fields metabox.
11 changes: 11 additions & 0 deletions plugins/woocommerce/client/legacy/css/admin.scss
Expand Up @@ -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 {
Expand Down
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand All @@ -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 );
?>
<p><strong><?php esc_html_e( 'Add New Custom Field:', 'woocommerce' ); ?></strong></p>
<table id="newmeta">
Expand All @@ -145,43 +162,42 @@ public function render_meta_form( \WC_Order $order ) : void {
<option value="#NONE#"><?php esc_html_e( '&mdash; Select &mdash;', 'woocommerce' ); ?></option>
<?php
foreach ( $keys as $key ) {
if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'edit_others_shop_order', $order->get_id() ) ) {
if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'edit_others_shop_orders' ) ) {
continue;
}
echo "\n<option value='" . esc_attr( $key ) . "'>" . esc_html( $key ) . '</option>';
}
?>
</select>
<input class="hide-if-js" type="text" id="metakeyinput" name="metakeyinput" value="" />
<a href="#postcustomstuff" class="hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggle();return false;">
<span id="enternew"><?php esc_html_e( 'Enter new', 'woocommerce' ); ?></span>
<span id="cancelnew" class="hidden"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></span></a>
<input class="hidden" type="text" id="metakeyinput" name="metakeyinput" value="" aria-label="<?php esc_attr_e( 'New custom field name', 'woocommerce' ); ?>" />
<button type="button" id="newmeta-button" class="button button-small hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggleClass('hidden');jQuery('#metakeyinput, #metakeyselect').filter(':visible').trigger('focus');">
<span id="enternew"><?php esc_html_e( 'Enter new', 'woocommerce' ); ?></span>
<span id="cancelnew" class="hidden"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></span>
<?php } else { ?>
<input type="text" id="metakeyinput" name="metakeyinput" value="" />
<?php } ?>
</td>
<td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea></td>
<td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea>
<?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
</td>
</tr>

<tr><td colspan="2">
<div class="submit">
<?php
submit_button(
__( 'Add Custom Field', 'woocommerce' ),
'',
'addmeta',
false,
array(
'id' => 'newmeta-submit',
'data-wp-lists' => 'add:the-list:newmeta',
)
);
?>
</div>
<?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
</td></tr>
</tbody>
</table>

<div class="submit add-custom-field">
<?php
submit_button(
__( 'Add Custom Field', 'woocommerce' ),
'',
'addmeta',
false,
array(
'id' => 'newmeta-submit',
'data-wp-lists' => 'add:the-list:newmeta',
)
);
?>
</div>
<?php
}

Expand Down Expand Up @@ -218,17 +234,20 @@ public function add_meta_ajax() {
$order_id = (int) $_POST['order_id'] ?? 0;
$order = $this->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 );
}
}

Expand Down
Expand Up @@ -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.
}

}

0 comments on commit 0e3c79e

Please sign in to comment.