Make OSA fields extendable, simplify naming (#41690)
- Use `referrer` & `source_type` field names consistently Remove the need to translate it back and forth. - Make fields actually extendable using `wc_order_attribution_tracking_fields`. Propagate the field configuration to the client side as well. Disambiguate fields in variables and functions. Co-authored-by: Justin Palmer <228780+layoutd@users.noreply.github.com>
This commit is contained in:
parent
245fea22ac
commit
fa62e63037
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
|
||||
Make OSA fields extendable
|
|
@ -8,30 +8,6 @@
|
|||
const propertyAccessor = ( obj, path ) => path.split( '.' ).reduce( ( acc, part ) => acc && acc[ part ], obj );
|
||||
const returnNull = () => null;
|
||||
|
||||
/**
|
||||
* Map of order attribution field names to sbjs.get property accessors.
|
||||
*/
|
||||
wc_order_attribution.fields = {
|
||||
// main fields.
|
||||
type: 'current.typ',
|
||||
url: 'current_add.rf',
|
||||
|
||||
// utm fields.
|
||||
utm_campaign: 'current.cmp',
|
||||
utm_source: 'current.src',
|
||||
utm_medium: 'current.mdm',
|
||||
utm_content: 'current.cnt',
|
||||
utm_id: 'current.id',
|
||||
utm_term: 'current.trm',
|
||||
|
||||
// additional fields.
|
||||
session_entry: 'current_add.ep',
|
||||
session_start_time: 'current_add.fd',
|
||||
session_pages: 'session.pgs',
|
||||
session_count: 'udata.vst',
|
||||
user_agent: 'udata.uag',
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the order attribution data.
|
||||
*
|
||||
|
|
|
@ -120,8 +120,8 @@ class OrderAttributionBlocksController implements RegisterHooksInterface {
|
|||
*/
|
||||
private function get_schema_callback() {
|
||||
return function() {
|
||||
$schema = array();
|
||||
$fields = $this->order_attribution_controller->get_fields();
|
||||
$schema = array();
|
||||
$field_names = $this->order_attribution_controller->get_field_names();
|
||||
|
||||
$validate_callback = function( $value ) {
|
||||
if ( ! is_string( $value ) && null !== $value ) {
|
||||
|
@ -142,12 +142,12 @@ class OrderAttributionBlocksController implements RegisterHooksInterface {
|
|||
return sanitize_text_field( $value );
|
||||
};
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$schema[ $field ] = array(
|
||||
foreach ( $field_names as $field_name ) {
|
||||
$schema[ $field_name ] = array(
|
||||
'description' => sprintf(
|
||||
/* translators: %s is the field name */
|
||||
__( 'Order attribution field: %s', 'woocommerce' ),
|
||||
esc_html( $field )
|
||||
esc_html( $field_name )
|
||||
),
|
||||
'type' => array( 'string', 'null' ),
|
||||
'context' => array(),
|
||||
|
|
|
@ -27,7 +27,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
|
||||
use ScriptDebug;
|
||||
use OrderAttributionMeta {
|
||||
get_prefixed_field as public;
|
||||
get_prefixed_field_name as public;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,13 +111,13 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
}
|
||||
);
|
||||
|
||||
// Include our hidden fields on order notes and registration form.
|
||||
$source_form_fields = function() {
|
||||
$this->source_form_fields();
|
||||
// Include our hidden `<input>` elements on order notes and registration form.
|
||||
$source_form_elements = function() {
|
||||
$this->source_form_elements();
|
||||
};
|
||||
|
||||
add_action( 'woocommerce_after_order_notes', $source_form_fields );
|
||||
add_action( 'woocommerce_register_form', $source_form_fields );
|
||||
add_action( 'woocommerce_after_order_notes', $source_form_elements );
|
||||
add_action( 'woocommerce_register_form', $source_form_elements );
|
||||
|
||||
// Update order based on submitted fields.
|
||||
add_action(
|
||||
|
@ -125,7 +125,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
function( $order ) {
|
||||
// Nonce check is handled by WooCommerce before woocommerce_checkout_order_created hook.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$params = $this->get_unprefixed_fields( $_POST );
|
||||
$params = $this->get_unprefixed_field_values( $_POST );
|
||||
/**
|
||||
* Run an action to save order attribution data.
|
||||
*
|
||||
|
@ -188,18 +188,18 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
*/
|
||||
private function maybe_set_admin_source( WC_Order $order ) {
|
||||
if ( function_exists( 'is_admin' ) && is_admin() ) {
|
||||
$order->add_meta_data( $this->get_meta_prefixed_field( 'type' ), 'admin' );
|
||||
$order->add_meta_data( $this->get_meta_prefixed_field_name( 'source_type' ), 'admin' );
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the fields.
|
||||
* Get all of the field names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_fields(): array {
|
||||
return $this->fields;
|
||||
public function get_field_names(): array {
|
||||
return $this->field_names;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,6 +271,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
'prefix' => $this->field_prefix,
|
||||
'allowTracking' => 'yes' === $allow_tracking,
|
||||
),
|
||||
'fields' => $this->fields,
|
||||
);
|
||||
|
||||
wp_localize_script( 'wc-order-attribution', 'wc_order_attribution', $namespace );
|
||||
|
@ -323,8 +324,8 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
* @return void
|
||||
*/
|
||||
private function output_origin_column( WC_Order $order ) {
|
||||
$source_type = $order->get_meta( $this->get_meta_prefixed_field( 'type' ) );
|
||||
$source = $order->get_meta( $this->get_meta_prefixed_field( 'utm_source' ) );
|
||||
$source_type = $order->get_meta( $this->get_meta_prefixed_field_name( 'source_type' ) );
|
||||
$source = $order->get_meta( $this->get_meta_prefixed_field_name( 'utm_source' ) );
|
||||
$origin = $this->get_origin_label( $source_type, $source );
|
||||
if ( empty( $origin ) ) {
|
||||
$origin = __( 'Unknown', 'woocommerce' );
|
||||
|
@ -333,11 +334,12 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add attribution hidden input fields for checkout & customer register froms.
|
||||
* Add `<input type="hidden">` elements for source fields.
|
||||
* Used for checkout & customer register froms.
|
||||
*/
|
||||
private function source_form_fields() {
|
||||
foreach ( $this->fields as $field ) {
|
||||
printf( '<input type="hidden" name="%s" value="" />', esc_attr( $this->get_prefixed_field( $field ) ) );
|
||||
private function source_form_elements() {
|
||||
foreach ( $this->field_names as $field_name ) {
|
||||
printf( '<input type="hidden" name="%s" value="" />', esc_attr( $this->get_prefixed_field_name( $field_name ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,8 +353,8 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
private function set_customer_source_data( WC_Customer $customer ) {
|
||||
// Nonce check is handled before user_register hook.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
foreach ( $this->get_source_values( $this->get_unprefixed_fields( $_POST ) ) as $key => $value ) {
|
||||
$customer->add_meta_data( $this->get_meta_prefixed_field( $key ), $value );
|
||||
foreach ( $this->get_source_values( $this->get_unprefixed_field_values( $_POST ) ) as $key => $value ) {
|
||||
$customer->add_meta_data( $this->get_meta_prefixed_field_name( $key ), $value );
|
||||
}
|
||||
|
||||
$customer->save_meta_data();
|
||||
|
@ -372,7 +374,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
return;
|
||||
}
|
||||
foreach ( $source_data as $key => $value ) {
|
||||
$order->add_meta_data( $this->get_meta_prefixed_field( $key ), $value );
|
||||
$order->add_meta_data( $this->get_meta_prefixed_field_name( $key ), $value );
|
||||
}
|
||||
|
||||
$order->save_meta_data();
|
||||
|
@ -414,7 +416,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
*/
|
||||
private function send_order_tracks( array $source_data, WC_Order $order ) {
|
||||
$origin_label = $this->get_origin_label(
|
||||
$source_data['type'] ?? '',
|
||||
$source_data['source_type'] ?? '',
|
||||
$source_data['utm_source'] ?? '',
|
||||
false
|
||||
);
|
||||
|
@ -422,7 +424,7 @@ class OrderAttributionController implements RegisterHooksInterface {
|
|||
$customer_info = $this->get_customer_history( $customer_identifier );
|
||||
$tracks_data = array(
|
||||
'order_id' => $order->get_id(),
|
||||
'type' => $source_data['type'] ?? '',
|
||||
'source_type' => $source_data['source_type'] ?? '',
|
||||
'medium' => $source_data['utm_medium'] ?? '',
|
||||
'source' => $source_data['utm_source'] ?? '',
|
||||
'device_type' => strtolower( $source_data['device_type'] ?? 'unknown' ),
|
||||
|
|
|
@ -19,31 +19,43 @@ use WP_Post;
|
|||
*/
|
||||
trait OrderAttributionMeta {
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* The default fields and their sourcebuster accesors,
|
||||
* to show in the source data metabox.
|
||||
*
|
||||
* @var string[]
|
||||
* */
|
||||
private $default_fields = array(
|
||||
// main fields.
|
||||
'type',
|
||||
'url',
|
||||
'source_type' => 'current.typ',
|
||||
'referrer' => 'current_add.rf',
|
||||
|
||||
// utm fields.
|
||||
'utm_campaign',
|
||||
'utm_source',
|
||||
'utm_medium',
|
||||
'utm_content',
|
||||
'utm_id',
|
||||
'utm_term',
|
||||
'utm_campaign' => 'current.cmp',
|
||||
'utm_source' => 'current.src',
|
||||
'utm_medium' => 'current.mdm',
|
||||
'utm_content' => 'current.cnt',
|
||||
'utm_id' => 'current.id',
|
||||
'utm_term' => 'current.trm',
|
||||
|
||||
// additional fields.
|
||||
'session_entry',
|
||||
'session_start_time',
|
||||
'session_pages',
|
||||
'session_count',
|
||||
'user_agent',
|
||||
'session_entry' => 'current_add.ep',
|
||||
'session_start_time' => 'current_add.fd',
|
||||
'session_pages' => 'session.pgs',
|
||||
'session_count' => 'udata.vst',
|
||||
'user_agent' => 'udata.uag',
|
||||
);
|
||||
|
||||
/** @var array */
|
||||
private $fields = array();
|
||||
|
||||
/**
|
||||
* Cached `array_keys( $fields )`.
|
||||
*
|
||||
* @var array
|
||||
* */
|
||||
private $field_names = array();
|
||||
|
||||
/** @var string */
|
||||
private $field_prefix = '';
|
||||
|
||||
|
@ -67,7 +79,7 @@ trait OrderAttributionMeta {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the meta fields and the field prefix.
|
||||
* Set the fields and the field prefix.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -79,7 +91,8 @@ trait OrderAttributionMeta {
|
|||
*
|
||||
* @param string[] $fields The fields to show.
|
||||
*/
|
||||
$this->fields = (array) apply_filters( 'wc_order_attribution_tracking_fields', $this->default_fields );
|
||||
$this->fields = (array) apply_filters( 'wc_order_attribution_tracking_fields', $this->default_fields );
|
||||
$this->field_names = array_keys( $this->fields );
|
||||
$this->set_field_prefix();
|
||||
}
|
||||
|
||||
|
@ -119,11 +132,11 @@ trait OrderAttributionMeta {
|
|||
*/
|
||||
private function filter_meta_data( array $meta ): array {
|
||||
$return = array();
|
||||
$prefix = $this->get_meta_prefixed_field( '' );
|
||||
$prefix = $this->get_meta_prefixed_field_name( '' );
|
||||
|
||||
foreach ( $meta as $item ) {
|
||||
if ( str_starts_with( $item->key, $prefix ) ) {
|
||||
$return[ $this->unprefix_meta_field( $item->key ) ] = $item->value;
|
||||
$return[ $this->unprefix_meta_field_name( $item->key ) ] = $item->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +146,7 @@ trait OrderAttributionMeta {
|
|||
}
|
||||
|
||||
// Determine the origin based on source type and referrer.
|
||||
$source_type = $return['type'] ?? '';
|
||||
$source_type = $return['source_type'] ?? '';
|
||||
$source = $return['utm_source'] ?? '';
|
||||
$return['origin'] = $this->get_origin_label( $source_type, $source, true );
|
||||
|
||||
|
@ -143,50 +156,34 @@ trait OrderAttributionMeta {
|
|||
/**
|
||||
* Get the field name with the appropriate prefix.
|
||||
*
|
||||
* @param string $field Field name.
|
||||
* @param string $name Field name.
|
||||
*
|
||||
* @return string The prefixed field name.
|
||||
*/
|
||||
private function get_prefixed_field( $field ): string {
|
||||
return "{$this->field_prefix}{$field}";
|
||||
private function get_prefixed_field_name( $name ): string {
|
||||
return "{$this->field_prefix}{$name}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field name with the meta prefix.
|
||||
*
|
||||
* @param string $field The field name.
|
||||
* @param string $name The field name.
|
||||
*
|
||||
* @return string The prefixed field name.
|
||||
*/
|
||||
private function get_meta_prefixed_field( string $field ): string {
|
||||
// Map some of the fields to the correct meta name.
|
||||
if ( 'type' === $field ) {
|
||||
$field = 'source_type';
|
||||
} elseif ( 'url' === $field ) {
|
||||
$field = 'referrer';
|
||||
}
|
||||
|
||||
return "_{$this->get_prefixed_field( $field )}";
|
||||
private function get_meta_prefixed_field_name( string $name ): string {
|
||||
return "_{$this->get_prefixed_field_name( $name )}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the meta prefix from the field name.
|
||||
*
|
||||
* @param string $field The prefixed field.
|
||||
* @param string $name The prefixed fieldname .
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function unprefix_meta_field( string $field ): string {
|
||||
$return = str_replace( "_{$this->field_prefix}", '', $field );
|
||||
|
||||
// Map some of the fields to the correct meta name.
|
||||
if ( 'source_type' === $return ) {
|
||||
$return = 'type';
|
||||
} elseif ( 'referrer' === $return ) {
|
||||
$return = 'url';
|
||||
}
|
||||
|
||||
return $return;
|
||||
private function unprefix_meta_field_name( string $name ): string {
|
||||
return str_replace( "_{$this->field_prefix}", '', $name );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,19 +215,19 @@ trait OrderAttributionMeta {
|
|||
|
||||
|
||||
/**
|
||||
* Map posted, prefixed values to fields.
|
||||
* Map posted, prefixed values to field values.
|
||||
* Used for the classic forms.
|
||||
*
|
||||
* @param array $raw_values The raw values from the POST form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_unprefixed_fields( array $raw_values = array() ): array {
|
||||
private function get_unprefixed_field_values( array $raw_values = array() ): array {
|
||||
$values = array();
|
||||
|
||||
// Look through each field in POST data.
|
||||
foreach ( $this->fields as $field ) {
|
||||
$values[ $field ] = $raw_values[ $this->get_prefixed_field( $field ) ] ?? '(none)';
|
||||
foreach ( $this->field_names as $field_name ) {
|
||||
$values[ $field_name ] = $raw_values[ $this->get_prefixed_field_name( $field_name ) ] ?? '(none)';
|
||||
}
|
||||
|
||||
return $values;
|
||||
|
@ -247,13 +244,13 @@ trait OrderAttributionMeta {
|
|||
$values = array();
|
||||
|
||||
// Look through each field in given data.
|
||||
foreach ( $this->fields as $field ) {
|
||||
$value = sanitize_text_field( wp_unslash( $raw_values[ $field ] ) );
|
||||
foreach ( $this->field_names as $field_name ) {
|
||||
$value = sanitize_text_field( wp_unslash( $raw_values[ $field_name ] ) );
|
||||
if ( '(none)' === $value ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$values[ $field ] = $value;
|
||||
$values[ $field_name ] = $value;
|
||||
}
|
||||
|
||||
// Set the device type if possible using the user agent.
|
||||
|
@ -361,13 +358,13 @@ trait OrderAttributionMeta {
|
|||
/**
|
||||
* Get the description for the order attribution field.
|
||||
*
|
||||
* @param string $field The field name.
|
||||
* @param string $field_name The field name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_field_description( string $field ): string {
|
||||
private function get_field_description( string $field_name ): string {
|
||||
/* translators: %s is the field name */
|
||||
$description = sprintf( __( 'Order attribution field: %s', 'woocommerce' ), $field );
|
||||
$description = sprintf( __( 'Order attribution field: %s', 'woocommerce' ), $field_name );
|
||||
|
||||
/**
|
||||
* Filter the description for the order attribution field.
|
||||
|
@ -375,9 +372,9 @@ trait OrderAttributionMeta {
|
|||
* @since 8.5.0
|
||||
*
|
||||
* @param string $description The description for the order attribution field.
|
||||
* @param string $field The field name.
|
||||
* @param string $field_name The field name.
|
||||
*/
|
||||
return (string) apply_filters( 'wc_order_attribution_field_description', $description, $field );
|
||||
return (string) apply_filters( 'wc_order_attribution_field_description', $description, $field_name );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* @see Automattic\WooCommerce\Internal\Orders\OrderAttributionController
|
||||
* @package WooCommerce\Templates
|
||||
* @version 8.4.0
|
||||
* @version 8.6.0
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
@ -47,10 +47,10 @@ defined( 'ABSPATH' ) || exit;
|
|||
</div>
|
||||
|
||||
<div class="woocommerce-order-attribution-details-container closed">
|
||||
<?php if ( array_key_exists( 'type', $meta ) ) : ?>
|
||||
<?php if ( array_key_exists( 'source_type', $meta ) ) : ?>
|
||||
<h4><?php esc_html_e( 'Source type', 'woocommerce' ); ?></h4>
|
||||
<span class="order-attribution-source_type">
|
||||
<?php echo esc_html( $meta['type'] ); ?>
|
||||
<?php echo esc_html( $meta['source_type'] ); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
|
|
|
@ -125,8 +125,8 @@ class OrderAttributionTest extends WP_UnitTestCase {
|
|||
$this->assertEquals( '/', $args['meta']['utm_content'] ?? '' );
|
||||
$this->assertEquals( 'referral', $args['meta']['utm_medium'] ?? '' );
|
||||
$this->assertEquals( 'woocommerce.com', $args['meta']['utm_source'] ?? '' );
|
||||
$this->assertEquals( 'https://woocommerce.com/', $args['meta']['url'] ?? '' );
|
||||
$this->assertEquals( 'referral', $args['meta']['type'] ?? '' );
|
||||
$this->assertEquals( 'https://woocommerce.com/', $args['meta']['referrer'] ?? '' );
|
||||
$this->assertEquals( 'referral', $args['meta']['source_type'] ?? '' );
|
||||
|
||||
$this->assertTrue( $args['has_more_details'] );
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@ class OrderAttributionControllerTest extends WP_UnitTestCase {
|
|||
*
|
||||
* @var OrderAttributionController
|
||||
*/
|
||||
protected OrderAttributionController $attribution_fields_class;
|
||||
protected OrderAttributionController $attribution_class;
|
||||
|
||||
/**
|
||||
* Sets up the fixture, for example, open a network connection.
|
||||
|
@ -35,7 +35,7 @@ class OrderAttributionControllerTest extends WP_UnitTestCase {
|
|||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->attribution_fields_class = new OrderAttributionController();
|
||||
$this->attribution_class = new OrderAttributionController();
|
||||
|
||||
/** @var MockableLegacyProxy $legacy_proxy */
|
||||
$legacy_proxy = wc_get_container()->get( LegacyProxy::class );
|
||||
|
@ -55,7 +55,7 @@ class OrderAttributionControllerTest extends WP_UnitTestCase {
|
|||
->onlyMethods( array( 'log' ) )
|
||||
->getMock();
|
||||
|
||||
$this->attribution_fields_class->init( $legacy_proxy, $feature_mock, $wp_consent_mock, $logger_mock );
|
||||
$this->attribution_class->init( $legacy_proxy, $feature_mock, $wp_consent_mock, $logger_mock );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,8 +98,8 @@ class OrderAttributionControllerTest extends WP_UnitTestCase {
|
|||
function( $order ) {
|
||||
$this->output_origin_column( $order );
|
||||
},
|
||||
$this->attribution_fields_class,
|
||||
$this->attribution_fields_class
|
||||
$this->attribution_class,
|
||||
$this->attribution_class
|
||||
);
|
||||
|
||||
foreach ( $test_cases as $test_case ) {
|
||||
|
|
Loading…
Reference in New Issue