Skip to content

Commit

Permalink
try a new module with woocommerce tracking, ported from recent google…
Browse files Browse the repository at this point in the history
… analytics enhancements
  • Loading branch information
Robert Elliott committed Dec 5, 2017
1 parent 576a76c commit eb2a1af
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 1 deletion.
4 changes: 3 additions & 1 deletion class.jetpack.php
Original file line number Diff line number Diff line change
Expand Up @@ -1440,7 +1440,9 @@ public static function get_active_plan() {
if ( empty( $plan ) || ( isset( $plan['product_slug'] ) && 'jetpack_free' === $plan['product_slug'] ) ) {
$plan = wp_parse_args( $plan, array(
'product_slug' => 'jetpack_free',
'supports' => array(),
'supports' => array(
'woocommerce-analytics',
),
'class' => 'free',
) );
}
Expand Down
15 changes: 15 additions & 0 deletions modules/module-info.php
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,18 @@ function jetpack_google_analytics_more_info() {
, 'jetpack' );
}
add_action( 'jetpack_module_more_info_google-analytics', 'jetpack_google_analytics_more_info' );

/**
* WooCommerce Analytics
*/
function jetpack_woocommerce_analytics_more_link() {
echo 'https://jetpack.com/support/';
}
add_action( 'jetpack_learn_more_button_woocommerce-analytics', 'jetpack_woocommerce_analytics_more_link' );

function jetpack_woocommerce_analytics_more_info() {
esc_html_e(
'Enhanced analytics for WooCommerce and Jetpack users.'
, 'jetpack' );
}
add_action( 'jetpack_module_more_info_woocommerce-analytics', 'jetpack_woocommerce_analytics_more_info' );
14 changes: 14 additions & 0 deletions modules/woocommerce-analytics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

/**
* Module Name: WooCommerce Analytics
* Module Description: Set up WooCommerce analytics
* First Introduced: 4.5
* Sort Order: 38
* Requires Connection: Yes
* Auto Activate: Yes
* Feature: Engagement
* Additional Search Queries: webmaster, analytics, console
*/

include dirname( __FILE__ ) . "/woocommerce-analytics/wp-woocommerce-analytics.php";
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<?php

/**
* Jetpack_WooCommerce_Analytics_Universal
*
* @author greenafrican
*/

/**
* Bail if accessed directly
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Jetpack_WooCommerce_Analytics_Universal {
public function __construct() {
add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'add_to_cart' ) );
add_action( 'wp_head', array( $this, 'wp_head' ), 999999 );
add_action( 'wp_footer', array( $this, 'loop_add_to_cart' ) );
add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) );
add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) );
add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
add_action( 'woocommerce_after_single_product', array( $this, 'product_detail' ) );
// add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
}

public function wp_head() {

// If we're in the admin_area, return without inserting code.
if ( is_admin() ) {
return;
}

if ( ! class_exists( 'WooCommerce' ) ) {
return;
}

$async_code = "<script async src='https://stats.wp.com/s.js'></script>";

echo "$async_code\r\n";
}

public function add_to_cart() {

if ( ! is_single() ) {
return;
}

$blogid = Jetpack::get_option( 'id' );
global $product;

$product_sku_or_id = Jetpack_WooCommerce_Analytics_Utils::get_product_sku_or_id( $product );
$selector = ".single_add_to_cart_button";

wc_enqueue_js(
"window._wca = window._wca || [];
jQuery( '" . esc_js( $selector ) . "' ).click( function() {
_wca.track( {
'_en': 'woocommerce_analytics_add_to_cart',
'blog_id': " . esc_js( $blogid ) . ",
'pi': '" . esc_js( $product_sku_or_id ) . "',
'pn' : '" . esc_js( $product->get_title() ) . "',
'pq': jQuery( 'input.qty' ).val() ? jQuery( 'input.qty' ).val() : '1',
} );
} );"
);
}

public function loop_add_to_cart() {

if ( ! class_exists( 'WooCommerce' ) ) {
return;
}

$minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( WC_VERSION, '3.0', '>=' );
if ( ! $minimum_woocommerce_active ) {
return;
}
$blogid = Jetpack::get_option( 'id' );
$selector = ".add_to_cart_button:not(.product_type_variable, .product_type_grouped)";

wc_enqueue_js(
"jQuery( '" . esc_js( $selector ) . "' ).click( function() {
var productSku = jQuery( this ).data( 'product_sku' );
var productID = jQuery( this ).data( 'product_id' );
var productDetails = {
'id': productSku ? productSku : productID,
'quantity': jQuery( this ).data( 'quantity' ),
};
_wca.track( {
'_en': 'woocommerce_analytics_product_view',
'blog_id': '" . esc_js( $blogid ) . "',
'pi': productDetails.id,
} );
_wca.track( {
'_en': 'woocommerce_analytics_add_to_cart',
'blog_id': " . esc_js( $blogid ) . ",
'pi': productDetails.id,
'pq': productDetails.quantity,
} );
} );"
);
}

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 productSku = jQuery( this ).data( 'product_sku' );
var productID = jQuery( this ).data( 'product_id' );
var quantity = jQuery( this ).parent().parent().find( '.qty' ).val()
var productDetails = {
'id': productSku ? productSku : productID,
'quantity': quantity ? quantity : '1',
};
_wca.track( {
'_en': 'woocommerce_analytics_remove_from_cart',
'blog_id': '" . esc_js( $blogid ) . "',
'pi': productDetails.id,
'pq': productDetails.quantity,
} );
} );"
);
}

/**
* Adds the product ID and SKU to the remove product link (for use by remove_from_cart above) if not present
*/
public function remove_from_cart_attributes( $url, $key ) {
if ( false !== strpos( $url, 'data-product_id' ) ) {
return $url;
}

$item = WC()->cart->get_cart_item( $key );
$product = $item[ 'data' ];

$new_attributes = sprintf( 'href="%s" data-product_id="%s" data-product_sku="%s"',
esc_attr( $url ),
esc_attr( $product->get_id() ),
esc_attr( $product->get_sku() )
);
$url = str_replace( 'href=', $new_attributes );
return $url;
}

public function product_detail() {

global $product;
$blogid = Jetpack::get_option( 'id' );
$product_sku_or_id = Jetpack_WooCommerce_Analytics_Utils::get_product_sku_or_id( $product );

$item_details = array(
'id' => $product_sku_or_id,
'name' => $product->get_title(),
'category' => Jetpack_WooCommerce_Analytics_Utils::get_product_categories_concatenated( $product ),
'price' => $product->get_price()
);
wc_enqueue_js(
"_wca.track( {
'_en': 'woocommerce_analytics_product_view',
'blog_id': '" . esc_js( $blogid ) . "',
'pi': '" . esc_js( $item_details['id'] ) . "',
'pn': '" . esc_js( $item_details['name'] ) . "',
'pc': '" . esc_js( $item_details['category'] ) . "',
'pp': '" . esc_js( $item_details['price'] ) . "'
} );"
);
}

// public function checkout_process() {
//
// $universal_commands = array();
// $cart = WC()->cart->get_cart();
//
// foreach ( $cart as $cart_item_key => $cart_item ) {
// /**
// * This filter is already documented in woocommerce/templates/cart/cart.php
// */
// $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
// $product_sku_or_id = Jetpack_WooCommerce_Analytics_Utils::get_product_sku_or_id( $product );
//
// $item_details = array(
// 'id' => $product_sku_or_id,
// 'name' => $product->get_title(),
// 'category' => Jetpack_WooCommerce_Analytics_Utils::get_product_categories_concatenated( $product ),
// 'price' => $product->get_price(),
// 'quantity' => $cart_item[ 'quantity' ]
// );
//
// array_push( $universal_commands, "ga( 'ec:addProduct', " . wp_json_encode( $item_details ) . " );" );
// }
//
// array_push( $universal_commands, "ga( 'ec:setAction','checkout' );" );
//
// wc_enqueue_js( implode( "\r\n", $universal_commands ) );
// }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/**
* Jetpack_WooCommerce_Analytics_Options provides a single interface to module options
*
* @author greenafrican
*/

/**
* Bail if accessed directly
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Jetpack_WooCommerce_Analytics_Utils {
/**
* Gets product categories or varation attributes as a formatted concatenated string
* @param WC_Product
* @return string
*/
public static function get_product_categories_concatenated( $product ) {
if ( ! class_exists( 'WooCommerce' ) ) {
return '';
}

if ( ! $product ) {
return '';
}

$variation_data = $product->is_type( 'variation' ) ? wc_get_product_variation_attributes( $product->get_id() ) : '';
if ( is_array( $variation_data ) && ! empty( $variation_data ) ) {
$line = wc_get_formatted_variation( $variation_data, true );
} else {
$out = array();
$categories = get_the_terms( $product->get_id(), 'product_cat' );
if ( $categories ) {
foreach ( $categories as $category ) {
$out[] = $category->name;
}
}
$line = join( "/", $out );
}
return $line;
}

/**
* Gets a product's SKU with fallback to just ID. IDs are prepended with a hash symbol.
* @param WC_Product
* @return string
*/
public static function get_product_sku_or_id( $product ) {
if ( ! class_exists( 'WooCommerce' ) ) {
return '';
}

if ( ! $product ) {
return '';
}

return $product->get_sku() ? $product->get_sku() : $product->get_id();
}
}
48 changes: 48 additions & 0 deletions modules/woocommerce-analytics/wp-woocommerce-analytics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* Jetpack_WooCommerce_Analytics is ported from the Jetpack_Google_Analytics code.
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

require_once( plugin_basename( 'classes/wp-woocommerce-analytics-utils.php' ) );
require_once( plugin_basename( 'classes/wp-woocommerce-analytics-universal.php' ) );

class Jetpack_WooCommerce_Analytics {

/**
* @var Jetpack_WooCommerce_Analytics - Static property to hold our singleton instance
*/
static $instance = false;

/**
* @var Static property to hold concrete analytics impl that does the work (universal or legacy)
*/
static $analytics = false;

/**
* This is our constructor, which is private to force the use of get_instance()
*
* @return void
*/
private function __construct() {
$analytics = new Jetpack_WooCommerce_Analytics_Universal();
}

/**
* Function to instantiate our class and make it a singleton
*/
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self;
}

return self::$instance;
}
}

global $jetpack_woocommerce_analytics;
$jetpack_woocommerce_analytics = Jetpack_WooCommerce_Analytics::get_instance();

0 comments on commit eb2a1af

Please sign in to comment.