PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.9.4
Jetpack – WP Security, Backup, Speed, & Growth v8.9.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / modules / google-analytics / classes / wp-google-analytics-universal.php
wp-google-analytics-universal.php
419 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Jetpack_Google_Analytics_Universal hooks and and enqueues support for analytics.js
5 * https://developers.google.com/analytics/devguides/collection/analyticsjs/
6 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce
7 *
8 * @author allendav
9 */
10
11 /**
12 * Bail if accessed directly
13 */
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 class Jetpack_Google_Analytics_Universal {
19 public function __construct() {
20 add_filter( 'jetpack_wga_universal_commands', array( $this, 'maybe_anonymize_ip' ) );
21 add_filter( 'jetpack_wga_universal_commands', array( $this, 'maybe_track_purchases' ) );
22
23 add_action( 'wp_head', array( $this, 'wp_head' ), 999999 );
24
25 add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'add_to_cart' ) );
26 add_action( 'wp_footer', array( $this, 'loop_add_to_cart' ) );
27 add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) );
28 add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) );
29 add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
30 add_action( 'woocommerce_after_shop_loop_item', array( $this, 'listing_impression' ) );
31 add_action( 'woocommerce_after_shop_loop_item', array( $this, 'listing_click' ) );
32 add_action( 'woocommerce_after_single_product', array( $this, 'product_detail' ) );
33 add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
34
35 // we need to send a pageview command last - so we use priority 24 to add
36 // this command's JavaScript just before wc_print_js is called (pri 25)
37 add_action( 'wp_footer', array( $this, 'send_pageview_in_footer' ), 24 );
38 }
39
40 public function wp_head() {
41 $tracking_code = Jetpack_Google_Analytics_Options::get_tracking_code();
42 if ( empty( $tracking_code ) ) {
43 echo "<!-- No tracking ID configured for Jetpack Google Analytics -->\r\n";
44 return;
45 }
46
47 // If we're in the admin_area, return without inserting code.
48 if ( is_admin() ) {
49 return;
50 }
51
52 if ( Jetpack_AMP_Support::is_amp_request() ) {
53 // For Reader mode — legacy.
54 add_filter( 'amp_post_template_analytics', 'Jetpack_Google_Analytics::amp_analytics_entries', 1000 );
55 // For Standard and Transitional modes.
56 add_filter( 'amp_analytics_entries', 'Jetpack_Google_Analytics::amp_analytics_entries', 1000 );
57 return;
58 }
59
60 /**
61 * Allow for additional elements to be added to the universal Google Analytics queue (ga) array
62 *
63 * @since 5.6.0
64 *
65 * @param array $custom_vars Array of universal Google Analytics queue elements
66 */
67 $universal_commands = apply_filters( 'jetpack_wga_universal_commands', array() );
68
69 $async_code = "
70 <!-- Jetpack Google Analytics -->
71 <script>
72 window.ga = window.ga || function(){ ( ga.q = ga.q || [] ).push( arguments ) }; ga.l=+new Date;
73 ga( 'create', '%tracking_id%', 'auto' );
74 ga( 'require', 'ec' );
75 %universal_commands%
76 </script>
77 <script async src='https://www.google-analytics.com/analytics.js'></script>
78 <!-- End Jetpack Google Analytics -->
79 ";
80 $async_code = str_replace( '%tracking_id%', $tracking_code, $async_code );
81
82 $universal_commands_string = implode( "\r\n", $universal_commands );
83 $async_code = str_replace( '%universal_commands%', $universal_commands_string, $async_code );
84
85 echo "$async_code\r\n";
86 }
87
88 public function maybe_anonymize_ip( $command_array ) {
89 if ( Jetpack_Google_Analytics_Options::anonymize_ip_is_enabled() ) {
90 array_push( $command_array, "ga( 'set', 'anonymizeIp', true );" );
91 }
92
93 return $command_array;
94 }
95
96 public function maybe_track_purchases( $command_array ) {
97 global $wp;
98
99 if ( ! Jetpack_Google_Analytics_Options::track_purchases_is_enabled() ) {
100 return $command_array;
101 }
102
103 if ( ! class_exists( 'WooCommerce' ) ) {
104 return $command_array;
105 }
106
107 $minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( WC_VERSION, '3.0', '>=' );
108 if ( ! $minimum_woocommerce_active ) {
109 return $command_array;
110 }
111
112 if ( ! is_order_received_page() ) {
113 return $command_array;
114 }
115
116 $order_id = isset( $wp->query_vars['order-received'] ) ? $wp->query_vars['order-received'] : 0;
117 if ( 0 == $order_id ) {
118 return $command_array;
119 }
120
121 // A 1 indicates we've already tracked this order - don't do it again
122 if ( 1 == get_post_meta( $order_id, '_ga_tracked', true ) ) {
123 return $command_array;
124 }
125
126 $order = new WC_Order( $order_id );
127 $order_currency = $order->get_currency();
128 $command = "ga( 'set', '&cu', '" . esc_js( $order_currency ) . "' );";
129 array_push( $command_array, $command );
130
131 // Order items
132 if ( $order->get_items() ) {
133 foreach ( $order->get_items() as $item ) {
134 $product = $order->get_product_from_item( $item );
135 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
136
137 $item_details = array(
138 'id' => $product_sku_or_id,
139 'name' => $item['name'],
140 'category' => Jetpack_Google_Analytics_Utils::get_product_categories_concatenated( $product ),
141 'price' => $order->get_item_total( $item ),
142 'quantity' => $item['qty'],
143 );
144 $command = "ga( 'ec:addProduct', " . wp_json_encode( $item_details ) . " );";
145 array_push( $command_array, $command );
146 }
147 }
148
149 // Order summary
150 $summary = array(
151 'id' => $order->get_order_number(),
152 'affiliation' => get_bloginfo( 'name' ),
153 'revenue' => $order->get_total(),
154 'tax' => $order->get_total_tax(),
155 'shipping' => $order->get_total_shipping()
156 );
157 $command = "ga( 'ec:setAction', 'purchase', " . wp_json_encode( $summary ) . " );";
158 array_push( $command_array, $command );
159
160 update_post_meta( $order_id, '_ga_tracked', 1 );
161
162 return $command_array;
163 }
164
165 public function add_to_cart() {
166 if ( ! Jetpack_Google_Analytics_Options::track_add_to_cart_is_enabled() ) {
167 return;
168 }
169
170 if ( ! is_single() ) {
171 return;
172 }
173
174 global $product;
175
176 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
177 $selector = ".single_add_to_cart_button";
178
179 wc_enqueue_js(
180 "$( '" . esc_js( $selector ) . "' ).click( function() {
181 var productDetails = {
182 'id': '" . esc_js( $product_sku_or_id ) . "',
183 'name' : '" . esc_js( $product->get_title() ) . "',
184 'quantity': $( 'input.qty' ).val() ? $( 'input.qty' ).val() : '1',
185 };
186 ga( 'ec:addProduct', productDetails );
187 ga( 'ec:setAction', 'add' );
188 ga( 'send', 'event', 'UX', 'click', 'add to cart' );
189 } );"
190 );
191 }
192
193 public function loop_add_to_cart() {
194 if ( ! Jetpack_Google_Analytics_Options::track_add_to_cart_is_enabled() ) {
195 return;
196 }
197
198 if ( ! class_exists( 'WooCommerce' ) ) {
199 return;
200 }
201
202 $minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( WC_VERSION, '3.0', '>=' );
203 if ( ! $minimum_woocommerce_active ) {
204 return;
205 }
206
207 $selector = ".add_to_cart_button:not(.product_type_variable, .product_type_grouped)";
208
209 wc_enqueue_js(
210 "$( '" . esc_js( $selector ) . "' ).click( function() {
211 var productSku = $( this ).data( 'product_sku' );
212 var productID = $( this ).data( 'product_id' );
213 var productDetails = {
214 'id': productSku ? productSku : '#' + productID,
215 'quantity': $( this ).data( 'quantity' ),
216 };
217 ga( 'ec:addProduct', productDetails );
218 ga( 'ec:setAction', 'add' );
219 ga( 'send', 'event', 'UX', 'click', 'add to cart' );
220 } );"
221 );
222 }
223
224 public function remove_from_cart() {
225 if ( ! Jetpack_Google_Analytics_Options::enhanced_ecommerce_tracking_is_enabled() ) {
226 return;
227 }
228
229 if ( ! Jetpack_Google_Analytics_Options::track_remove_from_cart_is_enabled() ) {
230 return;
231 }
232
233 // We listen at div.woocommerce because the cart 'form' contents get forcibly
234 // updated and subsequent removals from cart would then not have this click
235 // handler attached
236 wc_enqueue_js(
237 "$( 'div.woocommerce' ).on( 'click', 'a.remove', function() {
238 var productSku = $( this ).data( 'product_sku' );
239 var productID = $( this ).data( 'product_id' );
240 var quantity = $( this ).parent().parent().find( '.qty' ).val()
241 var productDetails = {
242 'id': productSku ? productSku : '#' + productID,
243 'quantity': quantity ? quantity : '1',
244 };
245 ga( 'ec:addProduct', productDetails );
246 ga( 'ec:setAction', 'remove' );
247 ga( 'send', 'event', 'UX', 'click', 'remove from cart' );
248 } );"
249 );
250 }
251
252 /**
253 * Adds the product ID and SKU to the remove product link (for use by remove_from_cart above) if not present
254 *
255 * @param string $url Full HTML a tag of the link to remove an item from the cart.
256 * @param string $key Unique Key ID for a cart item.
257 */
258 public function remove_from_cart_attributes( $url, $key ) {
259 if ( false !== strpos( $url, 'data-product_id' ) ) {
260 return $url;
261 }
262
263 $item = WC()->cart->get_cart_item( $key );
264 $product = $item['data'];
265
266 $new_attributes = sprintf(
267 '" data-product_id="%1$s" data-product_sku="%2$s">',
268 esc_attr( $product->get_id() ),
269 esc_attr( $product->get_sku() )
270 );
271
272 $url = str_replace( '">', $new_attributes, $url );
273 return $url;
274 }
275
276 public function listing_impression() {
277 if ( ! Jetpack_Google_Analytics_Options::enhanced_ecommerce_tracking_is_enabled() ) {
278 return;
279 }
280
281 if ( ! Jetpack_Google_Analytics_Options::track_product_impressions_is_enabled() ) {
282 return;
283 }
284
285 if ( isset( $_GET['s'] ) ) {
286 $list = "Search Results";
287 } else {
288 $list = "Product List";
289 }
290
291 global $product, $woocommerce_loop;
292 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
293
294 $item_details = array(
295 'id' => $product_sku_or_id,
296 'name' => $product->get_title(),
297 'category' => Jetpack_Google_Analytics_Utils::get_product_categories_concatenated( $product ),
298 'list' => $list,
299 'position' => $woocommerce_loop['loop']
300 );
301 wc_enqueue_js( "ga( 'ec:addImpression', " . wp_json_encode( $item_details ) . " );" );
302 }
303
304 public function listing_click() {
305 if ( ! Jetpack_Google_Analytics_Options::enhanced_ecommerce_tracking_is_enabled() ) {
306 return;
307 }
308
309 if ( ! Jetpack_Google_Analytics_Options::track_product_clicks_is_enabled() ) {
310 return;
311 }
312
313 if ( isset( $_GET['s'] ) ) {
314 $list = "Search Results";
315 } else {
316 $list = "Product List";
317 }
318
319 global $product, $woocommerce_loop;
320 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
321
322 $selector = ".products .post-" . esc_js( $product->get_id() ) . " a";
323
324 $item_details = array(
325 'id' => $product_sku_or_id,
326 'name' => $product->get_title(),
327 'category' => Jetpack_Google_Analytics_Utils::get_product_categories_concatenated( $product ),
328 'position' => $woocommerce_loop['loop']
329 );
330
331 wc_enqueue_js(
332 "$( '" . esc_js( $selector ) . "' ).click( function() {
333 if ( true === $( this ).hasClass( 'add_to_cart_button' ) ) {
334 return;
335 }
336
337 ga( 'ec:addProduct', " . wp_json_encode( $item_details ) . " );
338 ga( 'ec:setAction', 'click', { list: '" . esc_js( $list ) . "' } );
339 ga( 'send', 'event', 'UX', 'click', { list: '" . esc_js( $list ) . "' } );
340 } );"
341 );
342 }
343
344 public function product_detail() {
345 if ( ! Jetpack_Google_Analytics_Options::enhanced_ecommerce_tracking_is_enabled() ) {
346 return;
347 }
348
349 if ( ! Jetpack_Google_Analytics_Options::track_product_detail_view_is_enabled() ) {
350 return;
351 }
352
353 global $product;
354 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
355
356 $item_details = array(
357 'id' => $product_sku_or_id,
358 'name' => $product->get_title(),
359 'category' => Jetpack_Google_Analytics_Utils::get_product_categories_concatenated( $product ),
360 'price' => $product->get_price()
361 );
362 wc_enqueue_js(
363 "ga( 'ec:addProduct', " . wp_json_encode( $item_details ) . " );" .
364 "ga( 'ec:setAction', 'detail' );"
365 );
366 }
367
368 public function checkout_process() {
369 if ( ! Jetpack_Google_Analytics_Options::enhanced_ecommerce_tracking_is_enabled() ) {
370 return;
371 }
372
373 if ( ! Jetpack_Google_Analytics_Options::track_checkout_started_is_enabled() ) {
374 return;
375 }
376
377 $universal_commands = array();
378 $cart = WC()->cart->get_cart();
379
380 foreach ( $cart as $cart_item_key => $cart_item ) {
381 /**
382 * This filter is already documented in woocommerce/templates/cart/cart.php
383 */
384 $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
385 $product_sku_or_id = Jetpack_Google_Analytics_Utils::get_product_sku_or_id( $product );
386
387 $item_details = array(
388 'id' => $product_sku_or_id,
389 'name' => $product->get_title(),
390 'category' => Jetpack_Google_Analytics_Utils::get_product_categories_concatenated( $product ),
391 'price' => $product->get_price(),
392 'quantity' => $cart_item[ 'quantity' ]
393 );
394
395 array_push( $universal_commands, "ga( 'ec:addProduct', " . wp_json_encode( $item_details ) . " );" );
396 }
397
398 array_push( $universal_commands, "ga( 'ec:setAction','checkout' );" );
399
400 wc_enqueue_js( implode( "\r\n", $universal_commands ) );
401 }
402
403 public function send_pageview_in_footer() {
404 if ( ! Jetpack_Google_Analytics_Options::has_tracking_code() ) {
405 return;
406 }
407
408 if ( is_admin() ) {
409 return;
410 }
411
412 if ( ! class_exists( 'WooCommerce' ) ) {
413 return;
414 }
415
416 wc_enqueue_js( "ga( 'send', 'pageview' );" );
417 }
418 }
419