PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.1.3
Jetpack – WP Security, Backup, Speed, & Growth v12.1.3
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 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / woocommerce-analytics / classes / class-jetpack-woocommerce-analytics-universal.php
jetpack / modules / woocommerce-analytics / classes Last commit date
class-jetpack-woocommerce-analytics-universal.php 4 years ago
class-jetpack-woocommerce-analytics-universal.php
629 lines
1 <?php
2 /**
3 * Jetpack_WooCommerce_Analytics_Universal
4 *
5 * @package automattic/jetpack
6 * @author Automattic
7 */
8
9 use Automattic\Jetpack\Assets;
10
11 /**
12 * Bail if accessed directly
13 */
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 /**
19 * Class Jetpack_WooCommerce_Analytics_Universal
20 * Filters and Actions added to Store pages to perform analytics
21 */
22 class Jetpack_WooCommerce_Analytics_Universal {
23 /**
24 * Jetpack_WooCommerce_Analytics_Universal constructor.
25 */
26 public function __construct() {
27 // loading _wca.
28 add_action( 'wp_head', array( $this, 'wp_head_top' ), 1 );
29
30 // add to carts from non-product pages or lists -- search, store etc.
31 add_action( 'wp_head', array( $this, 'loop_session_events' ), 2 );
32
33 // loading s.js.
34 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_tracking_script' ) );
35
36 // Capture cart events.
37 add_action( 'woocommerce_add_to_cart', array( $this, 'capture_add_to_cart' ), 10, 6 );
38
39 // single product page view.
40 add_action( 'woocommerce_after_single_product', array( $this, 'capture_product_view' ) );
41
42 add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) );
43 add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) );
44 add_action( 'wcct_before_cart_widget', array( $this, 'remove_from_cart' ) );
45 add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
46
47 // Checkout.
48 // Send events after checkout template (shortcode).
49 add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
50 // Send events after checkout block.
51 add_action( 'woocommerce_blocks_enqueue_checkout_block_scripts_after', array( $this, 'checkout_process' ) );
52
53 // order confirmed.
54 add_action( 'woocommerce_thankyou', array( $this, 'order_process' ), 10, 1 );
55 add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart_via_quantity' ), 10, 1 );
56
57 add_filter( 'woocommerce_checkout_posted_data', array( $this, 'save_checkout_post_data' ), 10, 1 );
58 }
59
60 /**
61 * Make _wca available to queue events
62 */
63 public function wp_head_top() {
64 if ( is_cart() || is_checkout() || is_checkout_pay_page() || is_order_received_page() || is_add_payment_method_page() ) {
65 echo '<script>window._wca_prevent_referrer = true;</script>' . "\r\n";
66 }
67 echo '<script>window._wca = window._wca || [];</script>' . "\r\n";
68 }
69
70 /**
71 * Place script to call s.js, Store Analytics.
72 */
73 public function enqueue_tracking_script() {
74 $filename = sprintf(
75 'https://stats.wp.com/s-%d.js',
76 gmdate( 'YW' )
77 );
78
79 Assets::enqueue_async_script( 'woocommerce-analytics', esc_url( $filename ), esc_url( $filename ), array(), null, false );
80 }
81
82 /**
83 * Default event properties which should be included with all events.
84 *
85 * @return array Array of standard event props.
86 */
87 public function get_common_properties() {
88 $site_info = array(
89 'blog_id' => Jetpack::get_option( 'id' ),
90 'ui' => $this->get_user_id(),
91 'url' => home_url(),
92 'woo_version' => WC()->version,
93 );
94 $cart_checkout_info = self::get_cart_checkout_info();
95 return array_merge( $site_info, $cart_checkout_info );
96 }
97
98 /**
99 * Render tracks event properties as string of JavaScript object props.
100 *
101 * @param array $properties Array of key/value pairs.
102 * @return string String of the form "key1: value1, key2: value2, " (etc).
103 */
104 private function render_properties_as_js( $properties ) {
105 $js_args_string = '';
106 foreach ( $properties as $key => $value ) {
107 if ( is_array( $value ) ) {
108 $js_args_string = $js_args_string . "'$key': " . wp_json_encode( $value ) . ',';
109 } else {
110 $js_args_string = $js_args_string . "'$key': '" . esc_js( $value ) . "', ";
111 }
112 }
113 return $js_args_string;
114 }
115
116 /**
117 * Record an event with optional custom properties.
118 *
119 * @param string $event_name The name of the event to record.
120 * @param integer $product_id The id of the product relating to the event.
121 * @param array $properties Optional array of (key => value) event properties.
122 *
123 * @return string|void
124 */
125 public function record_event( $event_name, $product_id, $properties = array() ) {
126 $js = $this->process_event_properties( $event_name, $product_id, $properties );
127 wc_enqueue_js( "_wca.push({$js});" );
128 }
129
130 /**
131 * Compose event properties.
132 *
133 * @param string $event_name The name of the event to record.
134 * @param integer $product_id The id of the product relating to the event.
135 * @param array $properties Optional array of (key => value) event properties.
136 *
137 * @return string|void
138 */
139 public function process_event_properties( $event_name, $product_id, $properties = array() ) {
140 $product = wc_get_product( $product_id );
141 if ( ! $product instanceof WC_Product ) {
142 return;
143 }
144 $product_details = $this->get_product_details( $product );
145
146 $all_props = array_merge(
147 $properties,
148 $this->get_common_properties()
149 );
150
151 $js = "{
152 '_en': '" . esc_js( $event_name ) . "',
153 'pi': '" . esc_js( $product_id ) . "',
154 'pn': '" . esc_js( $product_details['name'] ) . "',
155 'pc': '" . esc_js( $product_details['category'] ) . "',
156 'pp': '" . esc_js( $product_details['price'] ) . "',
157 'pt': '" . esc_js( $product_details['type'] ) . "'," .
158 $this->render_properties_as_js( $all_props ) . '
159 }';
160
161 return $js;
162 }
163
164 /**
165 * On product lists or other non-product pages, add an event listener to "Add to Cart" button click
166 */
167 public function loop_session_events() {
168 // Check for previous events queued in session data.
169 if ( is_object( WC()->session ) ) {
170 $data = WC()->session->get( 'wca_session_data' );
171 if ( ! empty( $data ) ) {
172 foreach ( $data as $data_instance ) {
173 $this->record_event(
174 $data_instance['event'],
175 $data_instance['product_id'],
176 array(
177 'pq' => $data_instance['quantity'],
178 )
179 );
180 }
181 // Clear data, now that these events have been recorded.
182 WC()->session->set( 'wca_session_data', '' );
183 }
184 }
185 }
186
187 /**
188 * On the cart page, add an event listener for removal of product click
189 */
190 public function remove_from_cart() {
191 $common_props = $this->render_properties_as_js(
192 $this->get_common_properties()
193 );
194
195 // We listen at div.woocommerce because the cart 'form' contents get forcibly
196 // updated and subsequent removals from cart would then not have this click
197 // handler attached.
198 wc_enqueue_js(
199 "jQuery( 'div.woocommerce' ).on( 'click', 'a.remove', function() {
200 var productID = jQuery( this ).data( 'product_id' );
201 var quantity = jQuery( this ).parent().parent().find( '.qty' ).val()
202 var productDetails = {
203 'id': productID,
204 'quantity': quantity ? quantity : '1',
205 };
206 _wca.push( {
207 '_en': 'woocommerceanalytics_remove_from_cart',
208 'pi': productDetails.id,
209 'pq': productDetails.quantity, " .
210 $common_props . '
211 } );
212 } );'
213 );
214 }
215
216 /**
217 * Adds the product ID to the remove product link (for use by remove_from_cart above) if not present
218 *
219 * @param string $url Full HTML a tag of the link to remove an item from the cart.
220 * @param string $key Unique Key ID for a cart item.
221 *
222 * @return mixed.
223 */
224 public function remove_from_cart_attributes( $url, $key ) {
225 if ( false !== strpos( $url, 'data-product_id' ) ) {
226 return $url;
227 }
228
229 $item = WC()->cart->get_cart_item( $key );
230 $product = $item['data'];
231
232 $new_attributes = sprintf(
233 '" data-product_id="%s">',
234 esc_attr( $product->get_id() )
235 );
236
237 $url = str_replace( '">', $new_attributes, $url );
238 return $url;
239 }
240
241 /**
242 * Gather relevant product information
243 *
244 * @param array $product product.
245 * @return array
246 */
247 public function get_product_details( $product ) {
248 return array(
249 'id' => $product->get_id(),
250 'name' => $product->get_title(),
251 'category' => $this->get_product_categories_concatenated( $product ),
252 'price' => $product->get_price(),
253 'type' => $product->get_type(),
254 );
255 }
256
257 /**
258 * Track a product page view
259 */
260 public function capture_product_view() {
261 global $product;
262 $this->record_event(
263 'woocommerceanalytics_product_view',
264 $product->get_id()
265 );
266 }
267
268 /**
269 * On the Checkout page, trigger an event for each product in the cart
270 */
271 public function checkout_process() {
272 $cart = WC()->cart->get_cart();
273
274 $guest_checkout = ucfirst( get_option( 'woocommerce_enable_guest_checkout', 'No' ) );
275 $create_account = ucfirst( get_option( 'woocommerce_enable_signup_and_login_from_checkout', 'No' ) );
276
277 $enabled_payment_options = array_filter(
278 WC()->payment_gateways->get_available_payment_gateways(),
279 function ( $payment_gateway ) {
280 if ( ! $payment_gateway instanceof WC_Payment_Gateway ) {
281 return false;
282 }
283
284 return $payment_gateway->is_available();
285 }
286 );
287
288 $enabled_payment_options = array_keys( $enabled_payment_options );
289 $include_express_payment = false;
290
291 $wcpay_version = get_option( 'woocommerce_woocommerce_payments_version' );
292 $has_required_wcpay_version = version_compare( $wcpay_version, '2.9.0', '>=' );
293 // Check express payment availablity only if WC Pay is enabled and express checkout (payment request) is enabled.
294 if ( in_array( 'woocommerce_payments', $enabled_payment_options, true ) && $has_required_wcpay_version ) {
295 $wcpay_settings = get_option( 'woocommerce_woocommerce_payments_settings', array() );
296 if ( array_key_exists( 'payment_request', $wcpay_settings ) && 'yes' === $wcpay_settings['payment_request'] ) {
297 $include_express_payment = true;
298 }
299 }
300
301 foreach ( $cart as $cart_item_key => $cart_item ) {
302 /**
303 * This filter is already documented in woocommerce/templates/cart/cart.php
304 */
305 $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
306
307 if ( ! $product ) {
308 continue;
309 }
310
311 if ( true === $include_express_payment ) {
312 $properties = $this->process_event_properties(
313 'woocommerceanalytics_product_checkout',
314 $product->get_id(),
315 array(
316 'pq' => $cart_item['quantity'],
317 'payment_options' => $enabled_payment_options,
318 'device' => wp_is_mobile() ? 'mobile' : 'desktop',
319 'guest_checkout' => 'Yes' === $guest_checkout ? 'Yes' : 'No',
320 'create_account' => 'Yes' === $create_account ? 'Yes' : 'No',
321 'express_checkout' => 'null',
322 )
323 );
324 wc_enqueue_js(
325 "
326 // wcpay.payment-request.availability event gets fired twice.
327 // make sure we push only one event.
328 var cartItem_{$cart_item_key}_logged = false;
329 wp.hooks.addAction('wcpay.payment-request.availability', 'wcpay', function(args) {
330 if ( true === cartItem_{$cart_item_key}_logged ) {
331 return;
332 }
333 var properties = {$properties};
334 properties.express_checkout = args.paymentRequestType;
335 _wca.push(properties);
336 cartItem_{$cart_item_key}_logged = true;
337 });
338 "
339 );
340 } else {
341 $this->record_event(
342 'woocommerceanalytics_product_checkout',
343 $product->get_id(),
344 array(
345 'pq' => $cart_item['quantity'],
346 'payment_options' => $enabled_payment_options,
347 'device' => wp_is_mobile() ? 'mobile' : 'desktop',
348 'guest_checkout' => 'Yes' === $guest_checkout ? 'Yes' : 'No',
349 'create_account' => 'Yes' === $create_account ? 'Yes' : 'No',
350 'express_checkout' => 'null',
351 )
352 );
353 }
354 }
355 }
356
357 /**
358 * After the checkout process, fire an event for each item in the order
359 *
360 * @param string $order_id Order Id.
361 */
362 public function order_process( $order_id ) {
363 $order = wc_get_order( $order_id );
364
365 $payment_option = $order->get_payment_method();
366
367 if ( is_object( WC()->session ) ) {
368 $create_account = true === WC()->session->get( 'wc_checkout_createaccount_used' ) ? 'Y' : 'N';
369 } else {
370 $create_account = 'N';
371 }
372
373 $guest_checkout = $order->get_user() ? 'N' : 'Y';
374
375 $express_checkout = 'null';
376 // When the payment option is woocommerce_payment
377 // See if Google Pay or Apple Pay was used.
378 if ( 'woocommerce_payments' === $payment_option ) {
379 $payment_option_title = $order->get_payment_method_title();
380 if ( 'Google Pay (WooCommerce Payments)' === $payment_option_title ) {
381 $express_checkout = array( 'google_pay' );
382 } elseif ( 'Apple Pay (WooCommerce Payments)' === $payment_option_title ) {
383 $express_checkout = array( 'apple_pay' );
384 }
385 }
386
387 // loop through products in the order and queue a purchase event.
388 foreach ( $order->get_items() as $order_item ) {
389 $product_id = is_callable( array( $order_item, 'get_product_id' ) ) ? $order_item->get_product_id() : -1;
390
391 $this->record_event(
392 'woocommerceanalytics_product_purchase',
393 $product_id,
394 array(
395 'oi' => $order->get_order_number(),
396 'pq' => $order_item->get_quantity(),
397 'device' => wp_is_mobile() ? 'mobile' : 'desktop',
398 'payment_option' => $payment_option,
399 'create_account' => $create_account,
400 'guest_checkout' => $guest_checkout,
401 'express_checkout' => $express_checkout,
402 )
403 );
404 }
405 }
406
407 /**
408 * Listen for clicks on the "Update Cart" button to know if an item has been removed by
409 * updating its quantity to zero
410 */
411 public function remove_from_cart_via_quantity() {
412 $common_props = $this->render_properties_as_js(
413 $this->get_common_properties()
414 );
415
416 wc_enqueue_js(
417 "
418 jQuery( 'button[name=update_cart]' ).on( 'click', function() {
419 var cartItems = jQuery( '.cart_item' );
420 cartItems.each( function( item ) {
421 var qty = jQuery( this ).find( 'input.qty' );
422 if ( qty && qty.val() === '0' ) {
423 var productID = jQuery( this ).find( '.product-remove a' ).data( 'product_id' );
424 _wca.push( {
425 '_en': 'woocommerceanalytics_remove_from_cart',
426 'pi': productID, " .
427 $common_props . '
428 } );
429 }
430 } );
431 } );'
432 );
433 }
434
435 /**
436 * Get the current user id
437 *
438 * @return int
439 */
440 public function get_user_id() {
441 if ( is_user_logged_in() ) {
442 $blogid = Jetpack::get_option( 'id' );
443 $userid = get_current_user_id();
444 return $blogid . ':' . $userid;
445 }
446 return 'null';
447 }
448
449 /**
450 * Track adding items to the cart.
451 *
452 * @param string $cart_item_key Cart item key.
453 * @param int $product_id Product added to cart.
454 * @param int $quantity Quantity added to cart.
455 * @param int $variation_id Product variation.
456 * @param array $variation Variation attributes..
457 * @param array $cart_item_data Other cart data.
458 */
459 public function capture_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
460 $referer_postid = isset( $_SERVER['HTTP_REFERER'] ) ? url_to_postid( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : 0;
461 // if the referring post is not a product OR the product being added is not the same as post.
462 // (eg. related product list on single product page) then include a product view event.
463 $product_by_referer_postid = wc_get_product( $referer_postid );
464 if ( ! $product_by_referer_postid instanceof WC_Product || (int) $product_id !== $referer_postid ) {
465 $this->capture_event_in_session_data( $product_id, $quantity, 'woocommerceanalytics_product_view' );
466 }
467 // add cart event to the session data.
468 $this->capture_event_in_session_data( $product_id, $quantity, 'woocommerceanalytics_add_to_cart' );
469 }
470
471 /**
472 * Track in-session data.
473 *
474 * @param int $product_id Product ID.
475 * @param int $quantity Quantity.
476 * @param string $event Fired event.
477 */
478 public function capture_event_in_session_data( $product_id, $quantity, $event ) {
479
480 $product = wc_get_product( $product_id );
481 if ( ! $product instanceof WC_Product ) {
482 return;
483 }
484
485 $quantity = ( 0 === $quantity ) ? 1 : $quantity;
486
487 // check for existing data.
488 if ( is_object( WC()->session ) ) {
489 $data = WC()->session->get( 'wca_session_data' );
490 if ( empty( $data ) || ! is_array( $data ) ) {
491 $data = array();
492 }
493 } else {
494 $data = array();
495 }
496
497 // extract new event data.
498 $new_data = array(
499 'event' => $event,
500 'product_id' => (string) $product_id,
501 'quantity' => (string) $quantity,
502 );
503
504 // append new data.
505 $data[] = $new_data;
506
507 WC()->session->set( 'wca_session_data', $data );
508 }
509
510 /**
511 * Gets product categories or varation attributes as a formatted concatenated string
512 *
513 * @param object $product WC_Product.
514 * @return string
515 */
516 public function get_product_categories_concatenated( $product ) {
517
518 if ( ! $product instanceof WC_Product ) {
519 return '';
520 }
521
522 $variation_data = $product->is_type( 'variation' ) ? wc_get_product_variation_attributes( $product->get_id() ) : '';
523 if ( is_array( $variation_data ) && ! empty( $variation_data ) ) {
524 $line = wc_get_formatted_variation( $variation_data, true );
525 } else {
526 $out = array();
527 $categories = get_the_terms( $product->get_id(), 'product_cat' );
528 if ( $categories ) {
529 foreach ( $categories as $category ) {
530 $out[] = $category->name;
531 }
532 }
533 $line = join( '/', $out );
534 }
535 return $line;
536 }
537
538 /**
539 * Search a specific post for text content.
540 *
541 * Note: similar code is in a WooCommerce core PR:
542 * https://github.com/woocommerce/woocommerce/pull/25932
543 *
544 * @param integer $post_id The id of the post to search.
545 * @param string $text The text to search for.
546 * @return integer 1 if post contains $text (otherwise 0).
547 */
548 public static function post_contains_text( $post_id, $text ) {
549 global $wpdb;
550
551 // Search for the text anywhere in the post.
552 $wildcarded = "%{$text}%";
553
554 $result = $wpdb->get_var(
555 $wpdb->prepare(
556 "
557 SELECT COUNT( * ) FROM {$wpdb->prefix}posts
558 WHERE ID=%d
559 AND {$wpdb->prefix}posts.post_content LIKE %s
560 ",
561 array( $post_id, $wildcarded )
562 )
563 );
564
565 return ( '0' !== $result ) ? 1 : 0;
566 }
567
568 /**
569 * Get info about the cart & checkout pages, in particular
570 * whether the store is using shortcodes or Gutenberg blocks.
571 * This info is cached in a transient.
572 *
573 * Note: similar code is in a WooCommerce core PR:
574 * https://github.com/woocommerce/woocommerce/pull/25932
575 *
576 * @return array
577 */
578 public static function get_cart_checkout_info() {
579 $transient_name = 'jetpack_woocommerce_analytics_cart_checkout_info_cache';
580
581 $info = get_transient( $transient_name );
582 if ( false === $info ) {
583 $cart_page_id = wc_get_page_id( 'cart' );
584 $checkout_page_id = wc_get_page_id( 'checkout' );
585
586 $info = array(
587 'cart_page_contains_cart_block' => self::post_contains_text(
588 $cart_page_id,
589 '<!-- wp:woocommerce/cart'
590 ),
591 'cart_page_contains_cart_shortcode' => self::post_contains_text(
592 $cart_page_id,
593 '[woocommerce_cart]'
594 ),
595 'checkout_page_contains_checkout_block' => self::post_contains_text(
596 $checkout_page_id,
597 '<!-- wp:woocommerce/checkout'
598 ),
599 'checkout_page_contains_checkout_shortcode' => self::post_contains_text(
600 $checkout_page_id,
601 '[woocommerce_checkout]'
602 ),
603 );
604
605 set_transient( $transient_name, $info, DAY_IN_SECONDS );
606 }
607
608 return $info;
609 }
610
611 /**
612 * Save createaccount post data to be used in $this->order_process.
613 *
614 * @param array $data post data from the checkout page.
615 *
616 * @return array
617 */
618 public function save_checkout_post_data( array $data ) {
619 $session = WC()->session;
620 if ( is_object( $session ) ) {
621 if ( isset( $data['createaccount'] ) && ! empty( $data['createaccount'] ) ) {
622 $session->set( 'wc_checkout_createaccount_used', true );
623 $session->save_data();
624 }
625 }
626 return $data;
627 }
628 }
629