Completed
Push — update/connect-button-text ( 07ca48...14adb7 )
by
unknown
97:53 queued 87:56
created

loop_add_to_cart()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 9

Duplication

Lines 27
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 0
dl 27
loc 27
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Jetpack_WooCommerce_Analytics_Universal
4
 *
5
 * @package Jetpack
6
 * @author Automattic
7
 */
8
9
/**
10
 * Bail if accessed directly
11
 */
12
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
/**
17
 * Class Jetpack_WooCommerce_Analytics_Universal
18
 * Filters and Actions added to Store pages to perform analytics
19
 */
20
class Jetpack_WooCommerce_Analytics_Universal {
21
	/**
22
	 * Jetpack_WooCommerce_Analytics_Universal constructor.
23
	 */
24
	public function __construct() {
25
		// loading _wca
26
		add_action( 'wp_head', array( $this, 'wp_head_top' ), 1 );
27
28
		// loading s.js
29
		add_action( 'wp_head', array( $this, 'wp_head_bottom' ), 999999 );
30
31
		// single product page view
32
		add_action( 'woocommerce_after_single_product', array( $this, 'product_detail' ) );
33
34
		// add to cart on single product page
35
		add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'add_to_cart' ) );
36
37
		// add to carts from list views (search, store etc.)
38
		add_action( 'wp_footer', array( $this, 'loop_add_to_cart' ) );
39
40
41
		add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) );
42
		add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) );
43
		add_action( 'wcct_before_cart_widget', array( $this, 'remove_from_cart' ) );
44
		add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
45
46
		// cart checkout
47
		add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
48
49
		// order confirmed
50
		add_action( 'woocommerce_thankyou', array( $this, 'order_process' ), 10, 1 );
51
		add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart_via_quantity' ), 10, 1 );
52
	}
53
54
	/**
55
	 * Make _wca available to queue events
56
	 */
57
	public function wp_head_top() {
58
		if ( is_cart() || is_checkout() || is_checkout_pay_page() || is_order_received_page() || is_add_payment_method_page() ) {
59
			$prevent_referrer_code = "<script>window._wca_prevent_referrer = true;</script>";
60
			echo "$prevent_referrer_code\r\n";
61
		}
62
		$wca_code = "<script>window._wca = window._wca || [];</script>";
63
		echo "$wca_code\r\n";
64
	}
65
66
67
	/**
68
	 * Place script to call s.js, Store Analytics
69
	 */
70
	public function wp_head_bottom() {
71
		$filename = 's-' . gmdate( 'YW' ) . '.js';
72
		$async_code = "<script async src='https://stats.wp.com/" . $filename . "'></script>";
73
		echo "$async_code\r\n";
74
	}
75
76
	/**
77
	 * On a product page, add a click event listener to "Add to Cart" button click
78
	 */
79 View Code Duplication
	public function add_to_cart() {
80
81
		if ( ! is_single() ) {
82
			return;
83
		}
84
85
		$blogid = Jetpack::get_option( 'id' );
86
		global $product;
87
88
		wc_enqueue_js(
89
			"jQuery( '" . esc_js( '.single_add_to_cart_button' ) . "' ).click( function() {
90
				_wca.push( {
91
					'_en': 'woocommerceanalytics_add_to_cart',
92
					'blog_id': " . esc_js( $blogid ) . ",
93
					'pi': '" . esc_js( $product->get_id() ) . "',
94
					'pn' : '" . esc_js( $product->get_title() ) . "',
95
					'pq': jQuery( 'input.qty' ).val() ? jQuery( 'input.qty' ).val() : '1',
96
					'ui': '" . esc_js( $this->get_user_id() ) . "',
97
				} );
98
			} );"
99
		);
100
	}
101
102
	/**
103
	 * On product lists or other non-product pages, add an event listener to "Add to Cart" button click
104
	 */
105 View Code Duplication
	public function loop_add_to_cart() {
106
		$blogid   = Jetpack::get_option( 'id' );
107
		$selector = '.add_to_cart_button:not(.product_type_variable, .product_type_grouped)';
108
109
		wc_enqueue_js(
110
			"jQuery( '" . esc_js( $selector ) . "' ).click( function() {
111
				var productID = jQuery( this ).data( 'product_id' );
112
				var productDetails = {
113
					'id': productID,
114
					'quantity': jQuery( this ).data( 'quantity' ),
115
				};
116
				_wca.push( {
117
					'_en': 'woocommerceanalytics_product_view',
118
					'blog_id': '" . esc_js( $blogid ) . "',
119
					'pi': productDetails.id,
120
					'ui': '" . esc_js( $this->get_user_id() ) . "',
121
				} );
122
				_wca.push( {
123
					'_en': 'woocommerceanalytics_add_to_cart',
124
					'blog_id': " . esc_js( $blogid ) . ",
125
					'pi': productDetails.id,
126
					'pq': productDetails.quantity,
127
					'ui': '" . esc_js( $this->get_user_id() ) . "',
128
				} );
129
			} );"
130
		);
131
	}
132
133
	/**
134
	 * On the cart page, add an event listener for removal of product click
135
	 */
136 View Code Duplication
	public function remove_from_cart() {
137
138
		// We listen at div.woocommerce because the cart 'form' contents get forcibly
139
		// updated and subsequent removals from cart would then not have this click
140
		// handler attached.
141
		$blogid = Jetpack::get_option( 'id' );
142
		wc_enqueue_js(
143
			"jQuery( 'div.woocommerce' ).on( 'click', 'a.remove', function() {
144
				var productID = jQuery( this ).data( 'product_id' );
145
				var quantity = jQuery( this ).parent().parent().find( '.qty' ).val()
146
				var productDetails = {
147
					'id': productID,
148
					'quantity': quantity ? quantity : '1',
149
				};
150
				_wca.push( {
151
					'_en': 'woocommerceanalytics_remove_from_cart',
152
					'blog_id': '" . esc_js( $blogid ) . "',
153
					'pi': productDetails.id,
154
					'pq': productDetails.quantity,
155
					'ui': '" . esc_js( $this->get_user_id() ) . "',
156
				} );
157
			} );"
158
		);
159
	}
160
161
	/**
162
	 * Adds the product ID to the remove product link (for use by remove_from_cart above) if not present
163
	 *
164
	 * @param string $url url.
165
	 * @param string $key key.
166
	 * @return mixed.
0 ignored issues
show
Documentation introduced by
The doc-type mixed. could not be parsed: Unknown type name "mixed." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
167
	 */
168 View Code Duplication
	public function remove_from_cart_attributes( $url, $key ) {
169
		if ( false !== strpos( $url, 'data-product_id' ) ) {
170
			return $url;
171
		}
172
173
		$item    = WC()->cart->get_cart_item( $key );
174
		$product = $item['data'];
175
176
		$new_attributes = sprintf(
177
			'href="%s" data-product_id="%s" data-product_sku="%s"',
178
			esc_attr( $url ),
179
			esc_attr( $product->get_id() ),
180
			esc_attr( $product->get_sku() )
181
		);
182
		$url = str_replace( 'href=', $new_attributes );
183
		return $url;
184
	}
185
186
	/**
187
	 * Gather relevant product information
188
	 *
189
	 * @param array $product product
190
	 * @return array
191
	 */
192
	public function get_item_details( $product ) {
193
		return array(
194
			'id'       => $product->get_id(),
0 ignored issues
show
Bug introduced by
The method get_id cannot be called on $product (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
195
			'name'     => $product->get_title(),
0 ignored issues
show
Bug introduced by
The method get_title cannot be called on $product (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
196
			'category' => Jetpack_WooCommerce_Analytics_Utils::get_product_categories_concatenated( $product ),
197
			'price'    => $product->get_price(),
0 ignored issues
show
Bug introduced by
The method get_price cannot be called on $product (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
198
		);
199
	}
200
201
	/**
202
	 * Track a product page view
203
	 */
204
	public function product_detail() {
205
206
		global $product;
207
		$blogid = Jetpack::get_option( 'id' );
208
209
		$item_details = $this->get_item_details( $product );
210
211
		wc_enqueue_js(
212
			"_wca.push( {
213
				'_en': 'woocommerceanalytics_product_view',
214
				'blog_id': '" . esc_js( $blogid ) . "',
215
				'pi': '" . esc_js( $item_details['id'] ) . "',
216
				'pn': '" . esc_js( $item_details['name'] ) . "',
217
				'pc': '" . esc_js( $item_details['category'] ) . "',
218
				'pp': '" . esc_js( $item_details['price'] ) . "',
219
				'ui': '" . esc_js( $this->get_user_id() ) . "',
220
			} );"
221
		);
222
	}
223
224
	/**
225
	 * On the Checkout page, trigger an event for each product in the cart
226
	 */
227
	public function checkout_process() {
228
229
		$universal_commands = array();
230
		$cart               = WC()->cart->get_cart();
231
		$blogid             = Jetpack::get_option( 'id' );
232
233
		foreach ( $cart as $cart_item_key => $cart_item ) {
234
			/**
235
			* This filter is already documented in woocommerce/templates/cart/cart.php
236
			*/
237
			$product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
238
239
			$item_details = $this->get_item_details( $product );
240
241
			$universal_commands[] = "_wca.push( {
242
				'_en': 'woocommerceanalytics_product_checkout',
243
				'blog_id': '" . esc_js( $blogid ) . "',
244
				'pi': '" . esc_js( $item_details['id'] ) . "',
245
				'pn': '" . esc_js( $item_details['name'] ) . "',
246
				'pc': '" . esc_js( $item_details['category'] ) . "',
247
				'pp': '" . esc_js( $item_details['price'] ) . "',
248
				'pq': '" . esc_js( $cart_item['quantity'] ) . "',
249
				'ui': '" . esc_js( $this->get_user_id() ) . "',
250
			} );";
251
		}
252
253
		wc_enqueue_js( implode( "\r\n", $universal_commands ) );
254
	}
255
256
	/**
257
	 * After the checkout process, fire an event for each item in the order
258
	 *
259
	 * @param string $order_id Order Id.
260
	 */
261
	public function order_process( $order_id ) {
262
		$order              = wc_get_order( $order_id );
263
		$universal_commands = array();
264
		$blogid             = Jetpack::get_option( 'id' );
265
266
		// loop through products in the order and queue a purchase event.
267
		foreach ( $order->get_items() as $order_item_id => $order_item ) {
268
			$product = $order->get_product_from_item( $order_item );
269
270
			$item_details = $this->get_item_details( $product );
271
272
			$universal_commands[] = "_wca.push( {
273
				'_en': 'woocommerceanalytics_product_purchase',
274
				'blog_id': '" . esc_js( $blogid ) . "',
275
				'pi': '" . esc_js( $item_details['id'] ) . "',
276
				'pn': '" . esc_js( $item_details['name'] ) . "',
277
				'pc': '" . esc_js( $item_details['category'] ) . "',
278
				'pp': '" . esc_js( $item_details['price'] ) . "',
279
				'pq': '" . esc_js( $order_item->get_quantity() ) . "',
280
				'oi': '" . esc_js( $order->get_order_number() ) . "',
281
				'ui': '" . esc_js( $this->get_user_id() ) . "',
282
			} );";
283
		}
284
285
		wc_enqueue_js( implode( "\r\n", $universal_commands ) );
286
	}
287
288
	/**
289
	 * Listen for clicks on the "Update Cart" button to know if an item has been removed by
290
	 * updating its quantity to zero
291
	 */
292 View Code Duplication
	public function remove_from_cart_via_quantity() {
293
		$blogid = Jetpack::get_option( 'id' );
294
295
		wc_enqueue_js( "
296
			jQuery( 'button[name=update_cart]' ).on( 'click', function() {
297
				var cartItems = jQuery( '.cart_item' );
298
				cartItems.each( function( item ) {
299
					var qty = jQuery( this ).find( 'input.qty' );
300
					if ( qty && qty.val() === '0' ) {
301
						var productID = jQuery( this ).find( '.product-remove a' ).data( 'product_id' );
302
						_wca.push( {
303
							'_en': 'woocommerceanalytics_remove_from_cart',
304
							'blog_id': '" . esc_js( $blogid ) . "',
305
							'pi': productID,
306
							'ui': '" . esc_js( $this->get_user_id() ) . "',
307
						} );
308
					}
309
				} );
310
			} );
311
		" );
312
	}
313
314
	/**
315
	 * Get the current user id
316
	 *
317
	 * @return int
318
	 */
319
	public function get_user_id() {
320
		if ( is_user_logged_in() ) {
321
			$blogid = Jetpack::get_option( 'id' );
322
			$userid = get_current_user_id();
323
			return $blogid . ":" . $userid;
324
		}
325
		return 'null';
326
	}
327
328
}
329