PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 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 All 506 releases
jetpack / modules / woocommerce-analytics / classes / class-jetpack-woocommerce-analytics-universal.php

class-jetpack-woocommerce-analytics-universal.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at modules/woocommerce-analytics/classes/class-jetpack-woocommerce-analytics-universal.php

555 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Jetpack_WooCommerce_Analytics_Universal
4 *
5 * @deprecated 13.3
6 *
7 * @package automattic/jetpack
8 * @author Automattic
9 */
10
11 /**
12 * Bail if accessed directly
13 */
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit( 0 );
16 }
17
18 /**
19 * Class Jetpack_WooCommerce_Analytics_Universal
20 * Filters and Actions added to Store pages to perform analytics
21 *
22 * @deprecated 13.3
23 */
24 class Jetpack_WooCommerce_Analytics_Universal {
25
26 /**
27 * Trait to handle common analytics functions.
28 */
29 use Jetpack_WooCommerce_Analytics_Trait;
30
31 /**
32 * Jetpack_WooCommerce_Analytics_Universal constructor.
33 *
34 * @deprecated 13.3
35 */
36 public function __construct() {
37 $this->find_cart_checkout_content_sources();
38 $this->additional_blocks_on_cart_page = $this->get_additional_blocks_on_page( 'cart' );
39 $this->additional_blocks_on_checkout_page = $this->get_additional_blocks_on_page( 'checkout' );
40
41 // add to carts from non-product pages or lists -- search, store etc.
42 add_action( 'wp_head', array( $this, 'loop_session_events' ), 2 );
43
44 // Capture cart events.
45 add_action( 'woocommerce_add_to_cart', array( $this, 'capture_add_to_cart' ), 10, 6 );
46
47 add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) );
48 add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) );
49 add_action( 'wcct_before_cart_widget', array( $this, 'remove_from_cart' ) );
50 add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
51
52 // Checkout.
53 // Send events after checkout template (shortcode).
54 add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
55 // Send events after checkout block.
56 add_action( 'woocommerce_blocks_enqueue_checkout_block_scripts_after', array( $this, 'checkout_process' ) );
57
58 // order confirmed.
59 add_action( 'woocommerce_thankyou', array( $this, 'order_process' ), 10, 1 );
60 add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart_via_quantity' ), 10, 1 );
61
62 add_filter( 'woocommerce_checkout_posted_data', array( $this, 'save_checkout_post_data' ), 10, 1 );
63
64 add_action( 'woocommerce_created_customer', array( $this, 'capture_created_customer' ), 10, 2 );
65 }
66
67 /**
68 * On product lists or other non-product pages, add an event listener to "Add to Cart" button click
69 *
70 * @deprecated 13.3
71 */
72 public function loop_session_events() {
73 // Check for previous events queued in session data.
74 if ( is_object( WC()->session ) ) {
75 $data = WC()->session->get( 'wca_session_data' );
76 if ( ! empty( $data ) ) {
77 foreach ( $data as $data_instance ) {
78 $this->record_event(
79 $data_instance['event'],
80 array(
81 'pq' => $data_instance['quantity'],
82 ),
83 $data_instance['product_id']
84 );
85 }
86 // Clear data, now that these events have been recorded.
87 WC()->session->set( 'wca_session_data', '' );
88 }
89 }
90 }
91
92 /**
93 * On the cart page, add an event listener for removal of product click
94 *
95 * @deprecated 13.3
96 */
97 public function remove_from_cart() {
98 $common_props = wp_json_encode(
99 array_merge(
100 array(
101 '_en' => 'woocommerceanalytics_remove_from_cart',
102 ),
103 $this->get_common_properties()
104 ),
105 JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
106 );
107
108 // We listen at div.woocommerce because the cart 'form' contents get forcibly
109 // updated and subsequent removals from cart would then not have this click
110 // handler attached.
111 wc_enqueue_js(
112 "jQuery( 'div.woocommerce' ).on( 'click', 'a.remove', function() {
113 var productID = jQuery( this ).data( 'product_id' );
114 var quantity = jQuery( this ).parent().parent().find( '.qty' ).val()
115 var common_props = $common_props;
116 common_props.pi = productID;
117 common_props.pq = quantity ? quantity : '1';
118 _wca.push( common_props );
119 } );"
120 );
121 }
122
123 /**
124 * Adds the product ID to the remove product link (for use by remove_from_cart above) if not present
125 *
126 * @deprecated 13.3
127 *
128 * @param string $url Full HTML a tag of the link to remove an item from the cart.
129 * @param string $key Unique Key ID for a cart item.
130 *
131 * @return string
132 */
133 public function remove_from_cart_attributes( $url, $key ) {
134 if ( str_contains( $url, 'data-product_id' ) ) {
135 return $url;
136 }
137
138 $item = WC()->cart->get_cart_item( $key );
139 $product = $item['data'];
140
141 $new_attributes = sprintf(
142 '" data-product_id="%s">',
143 esc_attr( $product->get_id() )
144 );
145
146 $url = str_replace( '">', $new_attributes, $url );
147 return $url;
148 }
149
150 /**
151 * Get the selected shipping option for a cart item. If the name cannot be found in the options table, the method's
152 * ID will be used.
153 *
154 * @deprecated 13.3
155 *
156 * @param string $cart_item_key the cart item key.
157 *
158 * @return mixed|bool
159 */
160 public function get_shipping_option_for_item( $cart_item_key ) {
161 $packages = wc()->shipping()->get_packages();
162 $selected_options = wc()->session->get( 'chosen_shipping_methods' );
163
164 if ( ! is_array( $packages ) || ! is_array( $selected_options ) ) {
165 return false;
166 }
167
168 foreach ( $packages as $package_id => $package ) {
169
170 if ( ! isset( $package['contents'] ) || ! is_array( $package['contents'] ) ) {
171 return false;
172 }
173
174 foreach ( $package['contents'] as $package_item ) {
175 if ( ! isset( $package_item['key'] ) || $package_item['key'] !== $cart_item_key || ! isset( $selected_options[ $package_id ] ) ) {
176 continue;
177 }
178 $selected_rate_id = $selected_options[ $package_id ];
179 $method_key_id = sanitize_text_field( str_replace( ':', '_', $selected_rate_id ) );
180 $option_name = 'woocommerce_' . $method_key_id . '_settings';
181 $option_value = get_option( $option_name );
182 $title = '';
183 if ( is_array( $option_value ) && isset( $option_value['title'] ) ) {
184 $title = $option_value['title'];
185 }
186 if ( ! $title ) {
187 return $selected_rate_id;
188 }
189 return $title;
190 }
191 }
192
193 return false;
194 }
195
196 /**
197 * On the Checkout page, trigger an event for each product in the cart
198 *
199 * @deprecated 13.3
200 */
201 public function checkout_process() {
202 global $post;
203 $checkout_page_id = wc_get_page_id( 'checkout' );
204 $cart = WC()->cart->get_cart();
205
206 $enabled_payment_options = array_filter(
207 WC()->payment_gateways->get_available_payment_gateways(),
208 function ( $payment_gateway ) {
209 if ( ! $payment_gateway instanceof WC_Payment_Gateway ) {
210 return false;
211 }
212
213 return $payment_gateway->is_available();
214 }
215 );
216
217 $enabled_payment_options = array_keys( $enabled_payment_options );
218
219 $is_in_checkout_page = $checkout_page_id === $post->ID ? 'Yes' : 'No';
220 $session = WC()->session;
221 if ( is_object( $session ) ) {
222 $session->set( 'checkout_page_used', true );
223 $session->save_data();
224 }
225
226 foreach ( $cart as $cart_item_key => $cart_item ) {
227 /**
228 * This filter is already documented in woocommerce/templates/cart/cart.php
229 */
230 $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
231
232 if ( ! $product || ! $product instanceof WC_Product ) {
233 continue;
234 }
235
236 $data = $this->get_cart_checkout_shared_data();
237
238 $data['from_checkout'] = $is_in_checkout_page;
239
240 if ( ! empty( $data['products'] ) ) {
241 unset( $data['products'] );
242 }
243
244 if ( ! empty( $data['shipping_options_count'] ) ) {
245 unset( $data['shipping_options_count'] );
246 }
247
248 $data['pq'] = $cart_item['quantity'];
249
250 $properties = $this->process_event_properties(
251 'woocommerceanalytics_product_checkout',
252 $data,
253 $product->get_id()
254 );
255
256 wc_enqueue_js(
257 "
258 var cartItem_{$cart_item_key}_logged = false;
259 var properties = {$properties};
260 // Check if jQuery is available
261 if ( typeof jQuery !== 'undefined' ) {
262 // This is only triggered on the checkout shortcode.
263 jQuery( document.body ).on( 'init_checkout', function () {
264 if ( true === cartItem_{$cart_item_key}_logged ) {
265 return;
266 }
267 wp.hooks.addAction( 'wcpay.payment-request.availability', 'wcpay', function ( args ) {
268 properties.express_checkout = args.paymentRequestType;
269 } );
270 properties.checkout_page_contains_checkout_block = '0';
271 properties.checkout_page_contains_checkout_shortcode = '1';
272
273 _wca.push( properties );
274 cartItem_{$cart_item_key}_logged = true;
275
276 } );
277 }
278
279 if (
280 typeof wp !== 'undefined' &&
281 typeof wp.data !== 'undefined' &&
282 typeof wp.data.subscribe !== 'undefined'
283 ) {
284 wp.data.subscribe( function () {
285 if ( true === cartItem_{$cart_item_key}_logged ) {
286 return;
287 }
288
289 const checkoutDataStore = wp.data.select( 'wc/store/checkout' );
290 // Ensures we're not in Cart, but in Checkout page.
291 if (
292 typeof checkoutDataStore !== 'undefined' &&
293 checkoutDataStore.getOrderId() !== 0
294 ) {
295 properties.express_checkout = Object.keys( wc.wcBlocksRegistry.getExpressPaymentMethods() );
296 properties.checkout_page_contains_checkout_block = '1';
297 properties.checkout_page_contains_checkout_shortcode = '0';
298
299 _wca.push( properties );
300 cartItem_{$cart_item_key}_logged = true;
301 }
302 } );
303 }
304 "
305 );
306
307 }
308 }
309
310 /**
311 * After the checkout process, fire an event for each item in the order
312 *
313 * @deprecated 13.3
314 *
315 * @param string $order_id Order Id.
316 */
317 public function order_process( $order_id ) {
318 $order = wc_get_order( $order_id );
319
320 if (
321 ! $order
322 || ! $order instanceof WC_Order
323 ) {
324 return;
325 }
326
327 $payment_option = $order->get_payment_method();
328
329 if ( is_object( WC()->session ) ) {
330 $create_account = true === WC()->session->get( 'wc_checkout_createaccount_used' ) ? 'Yes' : 'No';
331 $checkout_page_used = true === WC()->session->get( 'checkout_page_used' ) ? 'Yes' : 'No';
332
333 } else {
334 $create_account = 'No';
335 $checkout_page_used = 'No';
336 }
337
338 $guest_checkout = $order->get_user() ? 'No' : 'Yes';
339
340 $express_checkout = 'null';
341 // When the payment option is woocommerce_payment
342 // See if Google Pay or Apple Pay was used.
343 if ( 'woocommerce_payments' === $payment_option ) {
344 $payment_option_title = $order->get_payment_method_title();
345 if ( 'Google Pay (WooCommerce Payments)' === $payment_option_title ) {
346 $express_checkout = array( 'google_pay' );
347 } elseif ( 'Apple Pay (WooCommerce Payments)' === $payment_option_title ) {
348 $express_checkout = array( 'apple_pay' );
349 }
350 }
351
352 $checkout_page_contains_checkout_block = '0';
353 $checkout_page_contains_checkout_shortcode = '0';
354
355 $order_source = $order->get_created_via();
356 if ( 'store-api' === $order_source ) {
357 $checkout_page_contains_checkout_block = '1';
358 } elseif ( 'checkout' === $order_source ) {
359 $checkout_page_contains_checkout_shortcode = '1';
360 }
361
362 // loop through products in the order and queue a purchase event.
363 foreach ( $order->get_items() as $order_item ) {
364 // @phan-suppress-next-line PhanUndeclaredMethod -- Checked before being called. See also https://github.com/phan/phan/issues/1204.
365 $product_id = is_callable( array( $order_item, 'get_product_id' ) ) ? $order_item->get_product_id() : -1;
366
367 $order_items = $order->get_items();
368 $order_items_count = 0;
369 if ( is_array( $order_items ) ) {
370 $order_items_count = count( $order_items );
371 }
372 $order_coupons = $order->get_coupons();
373 $order_coupons_count = 0;
374 if ( is_array( $order_coupons ) ) {
375 $order_coupons_count = count( $order_coupons );
376 }
377 $this->record_event(
378 'woocommerceanalytics_product_purchase',
379 array(
380 'oi' => $order->get_order_number(),
381 'pq' => $order_item->get_quantity(),
382 'payment_option' => $payment_option,
383 'create_account' => $create_account,
384 'guest_checkout' => $guest_checkout,
385 'express_checkout' => $express_checkout,
386 'products_count' => $order_items_count,
387 'coupon_used' => $order_coupons_count,
388 'order_value' => $order->get_total(),
389 'from_checkout' => $checkout_page_used,
390 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block,
391 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode,
392 ),
393 $product_id
394 );
395 }
396 }
397
398 /**
399 * Listen for clicks on the "Update Cart" button to know if an item has been removed by
400 * updating its quantity to zero
401 *
402 * @deprecated 13.3
403 */
404 public function remove_from_cart_via_quantity() {
405 $common_props = wp_json_encode(
406 array_merge(
407 array(
408 '_en' => 'woocommerceanalytics_remove_from_cart',
409 ),
410 $this->get_common_properties()
411 ),
412 JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
413 );
414
415 wc_enqueue_js(
416 "
417 jQuery( 'button[name=update_cart]' ).on( 'click', function() {
418 var cartItems = jQuery( '.cart_item' );
419 cartItems.each( function( item ) {
420 var qty = jQuery( this ).find( 'input.qty' );
421 if ( qty && qty.val() === '0' ) {
422 var productID = jQuery( this ).find( '.product-remove a' ).data( 'product_id' );
423 var common_props = $common_props;
424 common_props.pi = productID;
425 _wca.push( common_props );
426 }
427 } );
428 } );"
429 );
430 }
431
432 /**
433 * Gets the inner blocks of a block.
434 *
435 * @deprecated 13.3
436 *
437 * @param array $inner_blocks The inner blocks.
438 *
439 * @return array
440 */
441 private function get_inner_blocks( $inner_blocks ) {
442 $block_names = array();
443 if ( ! empty( $inner_blocks['blockName'] ) ) {
444 $block_names[] = $inner_blocks['blockName'];
445 }
446 if ( isset( $inner_blocks['innerBlocks'] ) && is_array( $inner_blocks['innerBlocks'] ) ) {
447 $block_names = array_merge( $block_names, $this->get_inner_blocks( $inner_blocks['innerBlocks'] ) );
448 }
449 return $block_names;
450 }
451
452 /**
453 * Track adding items to the cart.
454 *
455 * @deprecated 13.3
456 *
457 * @param string $cart_item_key Cart item key.
458 * @param int $product_id Product added to cart.
459 * @param int $quantity Quantity added to cart.
460 * @param int $variation_id Product variation.
461 * @param array $variation Variation attributes..
462 * @param array $cart_item_data Other cart data.
463 */
464 public function capture_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
465 $referer_postid = isset( $_SERVER['HTTP_REFERER'] ) ? url_to_postid( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : 0;
466 // if the referring post is not a product OR the product being added is not the same as post.
467 // (eg. related product list on single product page) then include a product view event.
468 $product_by_referer_postid = wc_get_product( $referer_postid );
469 if ( ! $product_by_referer_postid instanceof WC_Product || (int) $product_id !== $referer_postid ) {
470 $this->capture_event_in_session_data( $product_id, $quantity, 'woocommerceanalytics_product_view' );
471 }
472 // add cart event to the session data.
473 $this->capture_event_in_session_data( $product_id, $quantity, 'woocommerceanalytics_add_to_cart' );
474 }
475
476 /**
477 * Track in-session data.
478 *
479 * @deprecated 13.3
480 *
481 * @param int $product_id Product ID.
482 * @param int $quantity Quantity.
483 * @param string $event Fired event.
484 */
485 public function capture_event_in_session_data( $product_id, $quantity, $event ) {
486
487 $product = wc_get_product( $product_id );
488 if ( ! $product instanceof WC_Product ) {
489 return;
490 }
491
492 $quantity = ( 0 === $quantity ) ? 1 : $quantity;
493
494 // check for existing data.
495 if ( is_object( WC()->session ) ) {
496 $data = WC()->session->get( 'wca_session_data' );
497 if ( empty( $data ) || ! is_array( $data ) ) {
498 $data = array();
499 }
500 } else {
501 $data = array();
502 }
503
504 // extract new event data.
505 $new_data = array(
506 'event' => $event,
507 'product_id' => (string) $product_id,
508 'quantity' => (string) $quantity,
509 );
510
511 // append new data.
512 $data[] = $new_data;
513
514 WC()->session->set( 'wca_session_data', $data );
515 }
516
517 /**
518 * Save createaccount post data to be used in $this->order_process.
519 *
520 * @deprecated 13.3
521 *
522 * @param array $data post data from the checkout page.
523 *
524 * @return array
525 */
526 public function save_checkout_post_data( array $data ) {
527 $session = WC()->session;
528 if ( is_object( $session ) ) {
529 if ( isset( $data['createaccount'] ) && ! empty( $data['createaccount'] ) ) {
530 $session->set( 'wc_checkout_createaccount_used', true );
531 $session->save_data();
532 }
533 }
534 return $data;
535 }
536
537 /**
538 * Capture the create account event. Similar to save_checkout_post_data but works with Store API.
539 *
540 * @deprecated 13.3
541 *
542 * @param int $customer_id Customer ID.
543 * @param array $new_customer_data New customer data.
544 */
545 public function capture_created_customer( $customer_id, $new_customer_data ) {
546 $session = WC()->session;
547 if ( is_object( $session ) ) {
548 if ( str_contains( $new_customer_data['source'], 'store-api' ) ) {
549 $session->set( 'wc_checkout_createaccount_used', true );
550 $session->save_data();
551 }
552 }
553 }
554 }
555