From 37f89bf242057a60d5fb809de00fe6e09e56874d Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Wed, 18 Mar 2020 16:20:06 +1300 Subject: [PATCH 1/6] add extra properties to woocommerceanalytics events: - url - store/site home url - woo_version - version of woo plugin installed on the site --- .../classes/wp-woocommerce-analytics-universal.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index f736d0e5f57be..e724045fa58f8 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -103,6 +103,8 @@ public function loop_session_events() { 'pq': '" . esc_js( $data_instance['quantity'] ) . "', 'pt': '" . esc_js( $product_details['type'] ) . "', 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } );" ); } @@ -135,6 +137,8 @@ public function remove_from_cart() { 'pi': productDetails.id, 'pq': productDetails.quantity, 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } ); } );" ); @@ -200,6 +204,8 @@ public function capture_product_view() { 'pp': '" . esc_js( $product_details['price'] ) . "', 'pt': '" . esc_js( $product_details['type'] ) . "', 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } );" ); } @@ -235,6 +241,8 @@ public function checkout_process() { 'pq': '" . esc_js( $cart_item['quantity'] ) . "', 'pt': '" . esc_js( $product_details['type'] ) . "', 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } );"; } @@ -268,6 +276,8 @@ public function order_process( $order_id ) { 'pt': '" . esc_js( $product_details['type'] ) . "', 'oi': '" . esc_js( $order->get_order_number() ) . "', 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } );"; } @@ -294,6 +304,8 @@ public function remove_from_cart_via_quantity() { 'blog_id': '" . esc_js( $blogid ) . "', 'pi': productID, 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', } ); } } ); From 7766ae8a8eb9a8886b0e4a0f6503aa6c1a3a76a8 Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Thu, 19 Mar 2020 09:27:34 +1300 Subject: [PATCH 2/6] factor out common record_event from loop_session_events: - prep work for having a single record event where we can add all common properties - also provides a PHP interface for logging events with custom params + tidy comments --- .../wp-woocommerce-analytics-universal.php | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index e724045fa58f8..0ae6794c7ef62 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -77,38 +77,62 @@ public function enqueue_tracking_script() { } /** - * On product lists or other non-product pages, add an event listener to "Add to Cart" button click + * Record an event with optional custom properties. + * + * @param string $event_name The name of the event to record. + * @param integer $product_id The id of the product relating to the event. + * @param array $properties Array of key => value event properties. */ - public function loop_session_events() { + public function record_event( $event_name, $product_id, $properties ) { $blogid = Jetpack::get_option( 'id' ); - // check for previous add-to-cart cart events + $product = wc_get_product( $product_id ); + if ( ! $product instanceof WC_Product ) { + return; + } + $product_details = $this->get_product_details( $product ); + + $extra_properties = ''; + foreach ( $properties as $key => $value ) { + $extra_properties = $extra_properties . "'$key': '" . esc_js( $value ) . "', "; + } + + wc_enqueue_js( + "_wca.push( { + '_en': '" . esc_js( $event_name ) . "', + 'blog_id': '" . esc_js( $blogid ) . "', + 'pi': '" . esc_js( $product_id ) . "', + 'pn': '" . esc_js( $product_details['name'] ) . "', + 'pc': '" . esc_js( $product_details['category'] ) . "', + 'pp': '" . esc_js( $product_details['price'] ) . "', + 'pt': '" . esc_js( $product_details['type'] ) . "', + 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "', + " . $extra_properties . ' + } );' + ); + } + + + /** + * On product lists or other non-product pages, add an event listener to "Add to Cart" button click + */ + public function loop_session_events() { + // Check for previous events queued in session data. if ( is_object( WC()->session ) ) { $data = WC()->session->get( 'wca_session_data' ); if ( ! empty( $data ) ) { foreach ( $data as $data_instance ) { - $product = wc_get_product( $data_instance['product_id'] ); - if ( ! $product instanceof WC_Product ) { - continue; - } - $product_details = $this->get_product_details( $product ); - wc_enqueue_js( - "_wca.push( { - '_en': '" . esc_js( $data_instance['event'] ) . "', - 'blog_id': '" . esc_js( $blogid ) . "', - 'pi': '" . esc_js( $data_instance['product_id'] ) . "', - 'pn': '" . esc_js( $product_details['name'] ) . "', - 'pc': '" . esc_js( $product_details['category'] ) . "', - 'pp': '" . esc_js( $product_details['price'] ) . "', - 'pq': '" . esc_js( $data_instance['quantity'] ) . "', - 'pt': '" . esc_js( $product_details['type'] ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', - } );" + $this->record_event( + $data_instance['event'], + $data_instance['product_id'], + array( + 'pq' => $data_instance['quantity'], + ) ); } - // clear data + // Clear data, now that these events have been recorded. WC()->session->set( 'wca_session_data', '' ); } } From 0e123001bfa30e60c195d91e5cb65917f71ad516 Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Thu, 19 Mar 2020 10:01:57 +1300 Subject: [PATCH 3/6] include common props js-triggered events via new util func --- .../wp-woocommerce-analytics-universal.php | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index 0ae6794c7ef62..b03ce55bbea93 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -76,6 +76,18 @@ public function enqueue_tracking_script() { wp_enqueue_script( 'woocommerce-analytics', esc_url( $filename ), array(), null, false ); } + /** + * Render common default properties as a js args string. + */ + public function get_common_properties() { + $blogid = Jetpack::get_option( 'id' ); + + return "'blog_id': '" . esc_js( $blogid ) . "', + 'ui': '" . esc_js( $this->get_user_id() ) . "', + 'url': '" . esc_js( home_url() ) . "', + 'woo_version': '" . esc_js( WC()->version ) . "',"; + } + /** * Record an event with optional custom properties. * @@ -84,8 +96,6 @@ public function enqueue_tracking_script() { * @param array $properties Array of key => value event properties. */ public function record_event( $event_name, $product_id, $properties ) { - $blogid = Jetpack::get_option( 'id' ); - $product = wc_get_product( $product_id ); if ( ! $product instanceof WC_Product ) { return; @@ -100,21 +110,16 @@ public function record_event( $event_name, $product_id, $properties ) { wc_enqueue_js( "_wca.push( { '_en': '" . esc_js( $event_name ) . "', - 'blog_id': '" . esc_js( $blogid ) . "', 'pi': '" . esc_js( $product_id ) . "', 'pn': '" . esc_js( $product_details['name'] ) . "', 'pc': '" . esc_js( $product_details['category'] ) . "', 'pp': '" . esc_js( $product_details['price'] ) . "', - 'pt': '" . esc_js( $product_details['type'] ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', - " . $extra_properties . ' + 'pt': '" . esc_js( $product_details['type'] ) . "'," . + $extra_properties . $this->get_common_properties() . ' } );' ); } - /** * On product lists or other non-product pages, add an event listener to "Add to Cart" button click */ @@ -146,7 +151,6 @@ public function remove_from_cart() { // We listen at div.woocommerce because the cart 'form' contents get forcibly // updated and subsequent removals from cart would then not have this click // handler attached. - $blogid = Jetpack::get_option( 'id' ); wc_enqueue_js( "jQuery( 'div.woocommerce' ).on( 'click', 'a.remove', function() { var productID = jQuery( this ).data( 'product_id' ); @@ -157,14 +161,11 @@ public function remove_from_cart() { }; _wca.push( { '_en': 'woocommerceanalytics_remove_from_cart', - 'blog_id': '" . esc_js( $blogid ) . "', 'pi': productDetails.id, - 'pq': productDetails.quantity, - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', + 'pq': productDetails.quantity, " . + $this->get_common_properties() . ' } ); - } );" + } );' ); } @@ -326,15 +327,12 @@ public function remove_from_cart_via_quantity() { _wca.push( { '_en': 'woocommerceanalytics_remove_from_cart', 'blog_id': '" . esc_js( $blogid ) . "', - 'pi': productID, - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', + 'pi': productID, " . + $this->get_common_properties() . ' } ); } } ); - } ); - " + } );' ); } From 32b04096cf542594824f622b4c08b91a00443dd1 Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Thu, 19 Mar 2020 10:22:44 +1300 Subject: [PATCH 4/6] use record_event for all remaining woo events --- .../wp-woocommerce-analytics-universal.php | 79 +++++-------------- 1 file changed, 20 insertions(+), 59 deletions(-) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index b03ce55bbea93..c0eb78e32ccb4 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -214,24 +214,10 @@ public function get_product_details( $product ) { * Track a product page view */ public function capture_product_view() { - global $product; - $blogid = Jetpack::get_option( 'id' ); - $product_details = $this->get_product_details( $product ); - - wc_enqueue_js( - "_wca.push( { - '_en': 'woocommerceanalytics_product_view', - 'blog_id': '" . esc_js( $blogid ) . "', - 'pi': '" . esc_js( $product_details['id'] ) . "', - 'pn': '" . esc_js( $product_details['name'] ) . "', - 'pc': '" . esc_js( $product_details['category'] ) . "', - 'pp': '" . esc_js( $product_details['price'] ) . "', - 'pt': '" . esc_js( $product_details['type'] ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', - } );" + $this->record_event( + 'woocommerceanalytics_product_view', + $product->get_id() ); } @@ -239,10 +225,7 @@ public function capture_product_view() { * On the Checkout page, trigger an event for each product in the cart */ public function checkout_process() { - - $universal_commands = array(); - $cart = WC()->cart->get_cart(); - $blogid = Jetpack::get_option( 'id' ); + $cart = WC()->cart->get_cart(); foreach ( $cart as $cart_item_key => $cart_item ) { /** @@ -254,24 +237,14 @@ public function checkout_process() { continue; } - $product_details = $this->get_product_details( $product ); - - $universal_commands[] = "_wca.push( { - '_en': 'woocommerceanalytics_product_checkout', - 'blog_id': '" . esc_js( $blogid ) . "', - 'pi': '" . esc_js( $product_details['id'] ) . "', - 'pn': '" . esc_js( $product_details['name'] ) . "', - 'pc': '" . esc_js( $product_details['category'] ) . "', - 'pp': '" . esc_js( $product_details['price'] ) . "', - 'pq': '" . esc_js( $cart_item['quantity'] ) . "', - 'pt': '" . esc_js( $product_details['type'] ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', - } );"; + $this->record_event( + 'woocommerceanalytics_product_checkout', + $product->get_id(), + array( + 'pq' => $cart_item['quantity'], + ) + ); } - - wc_enqueue_js( implode( "\r\n", $universal_commands ) ); } /** @@ -280,33 +253,21 @@ public function checkout_process() { * @param string $order_id Order Id. */ public function order_process( $order_id ) { - $order = wc_get_order( $order_id ); - $universal_commands = array(); - $blogid = Jetpack::get_option( 'id' ); + $order = wc_get_order( $order_id ); // loop through products in the order and queue a purchase event. foreach ( $order->get_items() as $order_item_id => $order_item ) { $product = $order->get_product_from_item( $order_item ); - $product_details = $this->get_product_details( $product ); - - $universal_commands[] = "_wca.push( { - '_en': 'woocommerceanalytics_product_purchase', - 'blog_id': '" . esc_js( $blogid ) . "', - 'pi': '" . esc_js( $product_details['id'] ) . "', - 'pn': '" . esc_js( $product_details['name'] ) . "', - 'pc': '" . esc_js( $product_details['category'] ) . "', - 'pp': '" . esc_js( $product_details['price'] ) . "', - 'pq': '" . esc_js( $order_item->get_quantity() ) . "', - 'pt': '" . esc_js( $product_details['type'] ) . "', - 'oi': '" . esc_js( $order->get_order_number() ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "', - } );"; + $this->record_event( + 'woocommerceanalytics_product_purchase', + $product->get_id(), + array( + 'oi' => $order->get_order_number(), + 'pq' => $order_item->get_quantity(), + ) + ); } - - wc_enqueue_js( implode( "\r\n", $universal_commands ) ); } /** From 220eddb2c6e635847ba52c5415d1518763b794a2 Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Thu, 19 Mar 2020 10:29:19 +1300 Subject: [PATCH 5/6] fix issue with product_view event - handle no extra props --- .../classes/wp-woocommerce-analytics-universal.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index c0eb78e32ccb4..0687d93accac4 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -93,9 +93,9 @@ public function get_common_properties() { * * @param string $event_name The name of the event to record. * @param integer $product_id The id of the product relating to the event. - * @param array $properties Array of key => value event properties. + * @param array $properties Optional array of (key => value) event properties. */ - public function record_event( $event_name, $product_id, $properties ) { + public function record_event( $event_name, $product_id, $properties = array() ) { $product = wc_get_product( $product_id ); if ( ! $product instanceof WC_Product ) { return; From 40e0171e7cfe408417133a5dc1d5613e1ae1febc Mon Sep 17 00:00:00 2001 From: Rua Haszard Date: Mon, 23 Mar 2020 12:12:00 +1300 Subject: [PATCH 6/6] refactor: split definition of common props from render as js object keys --- .../wp-woocommerce-analytics-universal.php | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php index 0687d93accac4..c01f634b5c399 100644 --- a/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php +++ b/modules/woocommerce-analytics/classes/wp-woocommerce-analytics-universal.php @@ -77,15 +77,31 @@ public function enqueue_tracking_script() { } /** - * Render common default properties as a js args string. + * Default event properties which should be included with all events. + * + * @return array Array of standard event props. */ public function get_common_properties() { - $blogid = Jetpack::get_option( 'id' ); + return array( + 'blog_id' => Jetpack::get_option( 'id' ), + 'ui' => $this->get_user_id(), + 'url' => home_url(), + 'woo_version' => WC()->version, + ); + } - return "'blog_id': '" . esc_js( $blogid ) . "', - 'ui': '" . esc_js( $this->get_user_id() ) . "', - 'url': '" . esc_js( home_url() ) . "', - 'woo_version': '" . esc_js( WC()->version ) . "',"; + /** + * Render tracks event properties as string of JavaScript object props. + * + * @param array $properties Array of key/value pairs. + * @return string String of the form "key1: value1, key2: value2, " (etc). + */ + private function render_properties_as_js( $properties ) { + $js_args_string = ''; + foreach ( $properties as $key => $value ) { + $js_args_string = $js_args_string . "'$key': '" . esc_js( $value ) . "', "; + } + return $js_args_string; } /** @@ -102,10 +118,10 @@ public function record_event( $event_name, $product_id, $properties = array() ) } $product_details = $this->get_product_details( $product ); - $extra_properties = ''; - foreach ( $properties as $key => $value ) { - $extra_properties = $extra_properties . "'$key': '" . esc_js( $value ) . "', "; - } + $all_props = array_merge( + $properties, + $this->get_common_properties() + ); wc_enqueue_js( "_wca.push( { @@ -115,7 +131,7 @@ public function record_event( $event_name, $product_id, $properties = array() ) 'pc': '" . esc_js( $product_details['category'] ) . "', 'pp': '" . esc_js( $product_details['price'] ) . "', 'pt': '" . esc_js( $product_details['type'] ) . "'," . - $extra_properties . $this->get_common_properties() . ' + $this->render_properties_as_js( $all_props ) . ' } );' ); } @@ -147,6 +163,9 @@ public function loop_session_events() { * On the cart page, add an event listener for removal of product click */ public function remove_from_cart() { + $common_props = $this->render_properties_as_js( + $this->get_common_properties() + ); // We listen at div.woocommerce because the cart 'form' contents get forcibly // updated and subsequent removals from cart would then not have this click @@ -163,7 +182,7 @@ public function remove_from_cart() { '_en': 'woocommerceanalytics_remove_from_cart', 'pi': productDetails.id, 'pq': productDetails.quantity, " . - $this->get_common_properties() . ' + $common_props . ' } ); } );' ); @@ -275,7 +294,9 @@ public function order_process( $order_id ) { * updating its quantity to zero */ public function remove_from_cart_via_quantity() { - $blogid = Jetpack::get_option( 'id' ); + $common_props = $this->render_properties_as_js( + $this->get_common_properties() + ); wc_enqueue_js( " @@ -287,9 +308,8 @@ public function remove_from_cart_via_quantity() { var productID = jQuery( this ).find( '.product-remove a' ).data( 'product_id' ); _wca.push( { '_en': 'woocommerceanalytics_remove_from_cart', - 'blog_id': '" . esc_js( $blogid ) . "', 'pi': productID, " . - $this->get_common_properties() . ' + $common_props . ' } ); } } );