Update tracks events hooks for HPOS (#42879)

* Update ‘orders_view’ tracks event for HPOS

* Update ‘orders_view_search’ tracks event for HPOS

* Update ‘order_edit_date_created’ tracks event for HPOS

* Update ‘order_edit_add_order’ tracks event for HPOS

* Correctly enqueue order tracking JS scripts when HPOS is active

* Address TS script

* Change add_filter() to add_action()

* Fix tests

* Add changelog

* Fix some linting issues

* Make PHPCS happy

* Better handle deprecating old method

* Fix condition
This commit is contained in:
Jorge A. Torres 2023-12-29 08:46:59 +00:00 committed by GitHub
parent 5d5fc57e8c
commit 69a864af67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 80 deletions

View File

@ -10,9 +10,10 @@ import { staticFormDataToObject } from '~/utils/static-form-helper';
type FormElements = {
post?: HTMLFormElement;
order?: HTMLFormElement;
} & HTMLCollectionOf< HTMLFormElement >;
const forms: FormElements = document.forms;
if ( forms?.post ) {
if ( forms?.post || forms?.order ) {
let triggeredSaveOrDeleteButton = false;
const saveButton = document.querySelector( '.save_order' );
const deleteButton = document.querySelector( '.submitdelete' );
@ -27,14 +28,19 @@ if ( forms?.post ) {
triggeredSaveOrDeleteButton = true;
} );
}
const formData = staticFormDataToObject( forms.post );
const formData = staticFormDataToObject(
( forms?.post || forms?.order ) as HTMLFormElement
);
addCustomerEffortScoreExitPageListener( 'shop_order_update', () => {
if ( triggeredSaveOrDeleteButton ) {
return false;
}
const newFormData = forms.post
? staticFormDataToObject( forms.post )
: {};
const newFormData =
forms?.post || forms?.order
? staticFormDataToObject(
( forms?.post || forms?.order ) as HTMLFormElement
)
: {};
for ( const key of Object.keys( formData ) ) {
const value =
typeof formData[ key ] === 'object'

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Update a few Tracks events to be HPOS compatible.

View File

@ -19,23 +19,35 @@ class WC_Orders_Tracking {
*/
public function init() {
add_action( 'woocommerce_order_status_changed', array( $this, 'track_order_status_change' ), 10, 3 );
add_action( 'load-edit.php', array( $this, 'track_orders_view' ), 10 );
add_action( 'pre_post_update', array( $this, 'track_created_date_change' ), 10 );
// WC_Meta_Box_Order_Actions::save() hooks in at priority 50.
add_action( 'woocommerce_process_shop_order_meta', array( $this, 'track_order_action' ), 51 );
add_action( 'load-edit.php', array( $this, 'track_orders_view' ), 10 );
add_action( 'load-woocommerce_page_wc-orders', array( $this, 'track_orders_view' ), 10 ); // HPOS.
add_action( 'load-post-new.php', array( $this, 'track_add_order_from_edit' ), 10 );
add_filter( 'woocommerce_shop_order_search_results', array( $this, 'track_order_search' ), 10, 3 );
add_action( 'load-woocommerce_page_wc-orders', array( $this, 'track_add_order_from_edit' ), 10 ); // HPOS.
add_action( 'woocommerce_process_shop_order_meta', array( $this, 'track_created_date_change' ), 10 );
add_action( 'load-edit.php', array( $this, 'track_search_in_orders_list' ) );
add_action( 'load-woocommerce_page_wc-orders', array( $this, 'track_search_in_orders_list' ) ); // HPOS.
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_order_tracking_scripts' ) );
}
/**
* Send a track event when on the Order Listing page, and search results are being displayed.
*
* @deprecated 8.6.0
*
* @param array $order_ids Array of order_ids that are matches for the search.
* @param string $term The string that was used in the search.
* @param array $search_fields Fields that were used in the original search.
*/
public function track_order_search( $order_ids, $term, $search_fields ) {
wc_deprecated_function( __METHOD__, '8.6.0', 'WC_Orders_Tracking::track_search_in_orders_list' );
// Since `woocommerce_shop_order_search_results` can run in the front-end context, exit if get_current_screen isn't defined.
if ( ! function_exists( 'get_current_screen' ) ) {
return $order_ids;
@ -52,20 +64,34 @@ class WC_Orders_Tracking {
return $order_ids;
}
/**
* Send a track event when on the Order Listing page, and search results are being displayed.
*
* @since 8.6.0
*/
public function track_search_in_orders_list() {
if ( ! OrderUtil::is_order_list_table_screen() || empty( $_REQUEST['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}
WC_Tracks::record_event( 'orders_view_search' );
}
/**
* Send a Tracks event when the Orders page is viewed.
*/
public function track_orders_view() {
if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
$properties = array(
'status' => isset( $_GET['post_status'] ) ? sanitize_text_field( $_GET['post_status'] ) : 'all',
);
// phpcs:enable
WC_Tracks::record_event( 'orders_view', $properties );
if ( ! OrderUtil::is_order_list_table_screen() ) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
$properties = array(
'status' => sanitize_text_field( $_GET['post_status'] ?? ( $_GET['status'] ?? 'all' ) ),
);
// phpcs:enable
WC_Tracks::record_event( 'orders_view', $properties );
}
/**
@ -100,15 +126,15 @@ class WC_Orders_Tracking {
return;
}
if ( 'auto-draft' === get_post_status( $id ) ) {
$order = wc_get_order( $id );
if ( ! $order || 'auto-draft' === $order->get_status() ) {
return;
}
$order = wc_get_order( $id );
$date_created = $order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d H:i:s' ) : '';
// phpcs:disable WordPress.Security.NonceVerification
$new_date = sprintf(
'%s %2d:%2d:%2d',
'%s %2d:%02d:%02d',
isset( $_POST['order_date'] ) ? wc_clean( wp_unslash( $_POST['order_date'] ) ) : '',
isset( $_POST['order_date_hour'] ) ? wc_clean( wp_unslash( $_POST['order_date_hour'] ) ) : '',
isset( $_POST['order_date_minute'] ) ? wc_clean( wp_unslash( $_POST['order_date_minute'] ) ) : '',
@ -151,46 +177,50 @@ class WC_Orders_Tracking {
* Track "add order" button on the Edit Order screen.
*/
public function track_add_order_from_edit() {
// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) {
$referer = wp_get_referer();
if ( ! OrderUtil::is_new_order_screen() ) {
return;
}
if ( $referer ) {
$referring_page = wp_parse_url( $referer );
$referring_args = array();
$post_edit_page = wp_parse_url( admin_url( 'post.php' ) );
$referer = wp_get_referer();
if ( ! $referer ) {
return;
}
if ( ! empty( $referring_page['query'] ) ) {
parse_str( $referring_page['query'], $referring_args );
}
$referring_page = wp_parse_url( $referer );
// Determine if we arrived from an Order Edit screen.
if (
$post_edit_page['path'] === $referring_page['path'] &&
isset( $referring_args['action'] ) &&
'edit' === $referring_args['action'] &&
isset( $referring_args['post'] ) &&
'shop_order' === OrderUtil::get_order_type( $referring_args['post'] )
) {
WC_Tracks::record_event( 'order_edit_add_order' );
}
}
if ( empty( $referring_page['query'] ) ) {
// Edit Order screen has query args.
return;
}
parse_str( $referring_page['query'], $referring_args );
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$post_edit_page = admin_url( 'admin.php?page=wc-orders' );
$order_id = $referring_args['id'] ?? 0;
} else {
$post_edit_page = admin_url( 'post.php' );
$order_id = $referring_args['post'] ?? 0;
}
$post_edit_page = wp_parse_url( $post_edit_page );
if (
( $post_edit_page['path'] === $referring_page['path'] ) &&
( ! isset( $post_edit_page['query'] ) || false !== strpos( $referring_page['query'], $post_edit_page['query'] ) ) &&
( isset( $referring_args['action'] ) && 'edit' === $referring_args['action'] ) &&
'shop_order' === OrderUtil::get_order_type( $order_id )
) {
WC_Tracks::record_event( 'order_edit_add_order' );
}
}
/**
* Adds the tracking scripts for product setting pages.
*
* @param string $hook Page hook.
*/
public function possibly_add_order_tracking_scripts( $hook ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
if (
( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) ||
( 'post.php' === $hook && isset( $_GET['post'] ) && 'shop_order' === get_post_type( intval( $_GET['post'] ) ) )
) {
WCAdminAssets::register_script( 'wp-admin-scripts', 'order-tracking', false );
public function possibly_add_order_tracking_scripts() {
if ( ! OrderUtil::is_new_order_screen() && ! OrderUtil::is_order_edit_screen() && ! OrderUtil::is_order_list_table_screen() ) {
return;
}
// phpcs:enable
WCAdminAssets::register_script( 'wp-admin-scripts', 'order-tracking', false );
}
}

View File

@ -1,25 +1,16 @@
<?php
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
if ( ! class_exists( 'CurrentScreenMock' ) ) {
/**
* Class CurrentScreenMock
*/
class CurrentScreenMock {
/**
* CustomerEffortScoreTracks only works in wp-admin, so let's fake it.
*/
public function in_admin() {
return true;
}
}
}
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
use Automattic\WooCommerce\RestApi\UnitTests\HPOSToggleTrait;
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* Class WC_Orders_Tracking_Test.
*/
class WC_Orders_Tracking_Test extends \WC_Unit_Test_Case {
use HPOSToggleTrait;
/**
* @var object Backup object of $GLOBALS['current_screen'];
*/
@ -33,14 +24,21 @@ class WC_Orders_Tracking_Test extends \WC_Unit_Test_Case {
public function setUp(): void {
include_once WC_ABSPATH . 'includes/tracks/events/class-wc-orders-tracking.php';
update_option( 'woocommerce_allow_tracking', 'yes' );
if ( isset( $GLOBALS['current_screen'] ) ) {
$this->current_screen_backup = $GLOBALS['current_screen'];
// Mock screen.
$this->current_screen_backup = $GLOBALS['current_screen'] ?? null;
$GLOBALS['current_screen'] = $this->get_screen_mock(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
if ( ! did_action( 'current_screen' ) ) {
do_action( 'current_screen', $GLOBALS['current_screen'] ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
}
$GLOBALS['current_screen'] = new CurrentScreenMock(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$orders_tracking = new WC_Orders_Tracking();
$orders_tracking = new WC_Orders_Tracking();
$orders_tracking->init();
parent::setUp();
$this->setup_cot();
}
/**
* Teardown test
*
@ -52,39 +50,109 @@ class WC_Orders_Tracking_Test extends \WC_Unit_Test_Case {
$GLOBALS['current_screen'] = $this->current_screen_backup; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
parent::tearDown();
$this->clean_up_cot_setup();
remove_all_filters( 'wc_allow_changing_orders_storage_while_sync_is_pending' );
}
/**
* Test wcadmin_orders_edit_status_change Tracks event
* Test wcadmin_orders_edit_status_change Tracks event.
*
* @testWith [true]
* [false]
*
* @param bool $hpos_enabled Whether to test with HPOS enabled or not.
*/
public function test_orders_view_search() {
public function test_orders_status_change( $hpos_enabled ) {
$this->toggle_cot_authoritative( $hpos_enabled );
$order = wc_create_order();
$order->save();
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
do_action( 'woocommerce_order_status_changed', $order->get_id(), 'pending', 'finished' );
$this->assertRecordedTracksEvent( 'wcadmin_orders_edit_status_change' );
}
/**
* Test wcadmin_orders_view Tracks event
* Test wcadmin_orders_view Tracks event.
*
* @testWith [true]
* [false]
*
* @param bool $hpos_enabled Whether to test with HPOS enabled or not.
*/
public function test_orders_view() {
$_GET['post_type'] = 'shop_order';
public function test_orders_view( $hpos_enabled ) {
$this->toggle_cot_authoritative( $hpos_enabled );
$this->setup_screen( 'list' );
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
// phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
do_action( 'load-edit.php' );
do_action( $hpos_enabled ? 'load-woocommerce_page_wc-orders' : 'load-edit.php' );
$this->assertRecordedTracksEvent( 'wcadmin_orders_view' );
}
/**
* Test wcadmin_orders_view_search Tracks event
* Test wcadmin_orders_view_search Tracks event.
*
* @testWith [true]
* [false]
*
* @param bool $hpos_enabled Whether to test with HPOS enabled or not.
*/
public function test_orders_search() {
$GLOBALS['current_screen']->id = 'edit-shop_order';
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
apply_filters( 'woocommerce_shop_order_search_results', array( 'order_id1' ), 'term', array() );
public function test_orders_search( $hpos_enabled ) {
$this->toggle_cot_authoritative( $hpos_enabled );
$_REQUEST['s'] = 'term';
$this->setup_screen( 'list' );
do_action( 'load-edit.php' );
$this->assertRecordedTracksEvent( 'wcadmin_orders_view_search' );
}
/**
* Configure the screen as if it were the "list" orders screen.
*/
private function setup_screen() {
$GLOBALS['current_screen']->post_type = 'shop_order';
$GLOBALS['current_screen']->base = 'edit';
$_GET['action'] = '';
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$GLOBALS['pagenow'] = 'admin.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$GLOBALS['plugin_page'] = 'wc-orders'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
add_filter( 'map_meta_cap', array( $this, 'allow_edit_shop_orders' ), 10, 3 );
wc_get_container()->get( PageController::class )->setup();
remove_filter( 'map_meta_cap', array( $this, 'allow_edit_shop_orders' ), 10 );
}
}
/**
* Returns an object mocking what we need from `\WP_Screen`.
*
* @return object
*/
private function get_screen_mock() {
$screen_mock = $this->getMockBuilder( stdClass::class )->setMethods( array( 'in_admin', 'add_option' ) )->getMock();
$screen_mock->method( 'in_admin' )->willReturn( true );
foreach ( array( 'id', 'base', 'action', 'post_type' ) as $key ) {
$screen_mock->{$key} = '';
}
return $screen_mock;
}
/**
* Used to temporarily grant the current user the 'edit_shop_orders' permission.
*
* @param string[] $caps Primitive capabilities required for the user.
* @param string $cap Capability being checked.
* @param int $user_id The user ID.
* @return array
*/
public function allow_edit_shop_orders( $caps, $cap, $user_id ) {
return ( 0 === $user_id && 'edit_shop_orders' === $cap ) ? array() : $caps;
}
}