PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
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 / jetpack_vendor / automattic / woocommerce-analytics / src / class-woo-analytics-trait.php
jetpack / jetpack_vendor / automattic / woocommerce-analytics / src Last commit date
API 8 months ago mu-plugin 5 months ago class-consent-manager.php 8 months ago class-features.php 5 months ago class-my-account.php 8 months ago class-pixel-builder.php 4 months ago class-universal.php 3 weeks ago class-wc-analytics-tracking.php 3 weeks ago class-woo-analytics-trait.php 3 weeks ago class-woocommerce-analytics.php 3 weeks ago
class-woo-analytics-trait.php
732 lines
1 <?php
2 /**
3 * Woo_Analytics_Trait
4 *
5 * @package automattic/woocommerce-analytics
6 */
7
8 namespace Automattic\Woocommerce_Analytics;
9
10 use Automattic\Block_Scanner;
11 use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
12 use WC_Order_Item;
13 use WC_Order_Item_Product;
14 use WC_Payment_Gateway;
15 use WC_Product;
16
17 /**
18 * Common functionality for WooCommerce Analytics classes.
19 */
20 trait Woo_Analytics_Trait {
21 /**
22 * Saves whether the cart/checkout templates are in use based on WC Blocks version.
23 *
24 * @var bool true if the templates are in use.
25 */
26 protected $cart_checkout_templates_in_use;
27
28 /**
29 * The content of the cart page or where the cart page is ultimately derived from if using a template.
30 *
31 * @var string
32 */
33 protected $cart_content_source = '';
34
35 /**
36 * The content of the checkout page or where the cart page is ultimately derived from if using a template.
37 *
38 * @var string
39 */
40 protected $checkout_content_source = '';
41
42 /**
43 * Tracks any additional blocks loaded on the Cart page.
44 *
45 * @var array
46 */
47 protected $additional_blocks_on_cart_page;
48
49 /**
50 * Tracks any additional blocks loaded on the Checkout page.
51 *
52 * @var array
53 */
54 protected $additional_blocks_on_checkout_page;
55
56 /**
57 * Locks Add to Cart Events Tracking in the current request avoiding duplications.
58 * i.e. If update_cart and add_to_cart actions happens in the same request.
59 *
60 * @var bool If true. Cart events are locked for the current request.
61 */
62 protected $lock_add_to_cart_events = false;
63
64 /**
65 * Format Cart Items or Order Items to an array
66 *
67 * @param array|WC_Order_Item[] $items Cart Items or Order Items.
68 */
69 protected function format_items_to_json( $items ) {
70 $products = array();
71
72 foreach ( $items as $item ) {
73 if ( $item instanceof WC_Order_Item_Product ) {
74 $product = wc_get_product( $item->get_product_id() );
75 } else {
76 $product = $item['data'];
77 }
78
79 if ( ! $product || ! $product instanceof WC_Product ) {
80 continue;
81 }
82
83 $data = $this->get_product_details( $product );
84
85 if ( $item instanceof WC_Order_Item_Product ) {
86 $data['pq'] = $item->get_quantity();
87 } else {
88 $data['pq'] = $item['quantity'];
89 }
90 $products[] = $data;
91 }
92
93 return wp_json_encode( $products, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG );
94 }
95
96 /**
97 * Get Cart/Checkout page view shared data
98 */
99 protected function get_cart_checkout_shared_data() {
100 $cart = WC()->cart;
101
102 $guest_checkout = ucfirst( get_option( 'woocommerce_enable_guest_checkout', 'No' ) );
103 $create_account = ucfirst( get_option( 'woocommerce_enable_signup_and_login_from_checkout', 'No' ) );
104 $delayed_account_creation = ucfirst( get_option( 'woocommerce_enable_delayed_account_creation', 'Yes' ) );
105
106 $coupons = $cart->get_coupons();
107 $coupon_used = 0;
108 if ( is_countable( $coupons ) ) {
109 $coupon_used = count( $coupons ) ? 1 : 0;
110 }
111
112 $enabled_payment_options = array_filter(
113 WC()->payment_gateways->get_available_payment_gateways(),
114 function ( $payment_gateway ) {
115 if ( ! $payment_gateway instanceof WC_Payment_Gateway ) {
116 return false;
117 }
118
119 return $payment_gateway->is_available();
120 }
121 );
122
123 $enabled_payment_options = array_keys( $enabled_payment_options );
124 $shared_data = array(
125 'products' => $this->format_items_to_json( $cart->get_cart() ),
126 'create_account' => $create_account,
127 'guest_checkout' => $guest_checkout,
128 'delayed_account_creation' => $delayed_account_creation,
129 'express_checkout' => 'null', // TODO: not solved yet.
130 'shipping_options_count' => 'null', // TODO: not solved yet.
131 'coupon_used' => $coupon_used,
132 'payment_options' => $enabled_payment_options,
133 );
134
135 return $shared_data;
136 }
137
138 /**
139 * Gets the content of the cart/checkout page or where the cart/checkout page is ultimately derived from if using a template.
140 * This method sets the class properties $checkout_content_source and $cart_content_source.
141 *
142 * @return void Does not return, but sets class properties.
143 */
144 public function find_cart_checkout_content_sources() {
145
146 /**
147 * The steps we take to find the content are:
148 * 1. Check the transient, if that contains content and is not expired, return that.
149 * 2. Check if the cart/checkout templates are in use. If *not in use*, get the content from the pages and
150 * return it, there is no need to dig further.
151 * 3. If the templates *are* in use, check if the `page-content-wrapper` block is in use. If so, get the content
152 * from the pages (same as step 2) and return it.
153 * 4. If the templates are in use but `page-content-wrapper` is not, then get the content directly from the
154 * template and return it.
155 * 5. At the end of each step, assign the found content to the relevant class properties and save them in a
156 * transient with a 1-day lifespan. This will prevent us from having to do this work on every page load.
157 */
158
159 $cart_checkout_content_cache_transient_name = 'jetpack_woocommerce_analytics_cart_checkout_content_sources';
160
161 $transient_value = get_transient( $cart_checkout_content_cache_transient_name );
162
163 if (
164 false !== $transient_value &&
165 ! empty( $transient_value['checkout_content_source'] ) &&
166 ! empty( $transient_value['cart_content_source'] )
167 ) {
168 $this->cart_content_source = $transient_value['cart_content_source'];
169 $this->checkout_content_source = $transient_value['checkout_content_source'];
170 return;
171 }
172
173 $this->cart_checkout_templates_in_use = wp_is_block_theme()
174 && class_exists( '\Automattic\WooCommerce\Blocks\Package' )
175 // @phan-suppress-current-line UnusedPluginSuppression @phan-suppress-next-line PhanUndeclaredClassMethod -- If the class exists (as of WooCommerce 8.5.0), the method exists. See also: https://github.com/phan/phan/issues/1204
176 && version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '10.6.0', '>=' );
177
178 // Cart/Checkout *pages* are in use if the templates are not in use. Return their content and do nothing else.
179 if ( ! $this->cart_checkout_templates_in_use ) {
180 $cart_page = get_post( wc_get_page_id( 'cart' ) );
181 $checkout_page = get_post( wc_get_page_id( 'checkout' ) );
182
183 if ( $cart_page && isset( $cart_page->post_content ) ) {
184 $this->cart_content_source = $cart_page->post_content;
185 }
186
187 if ( $checkout_page && isset( $checkout_page->post_content ) ) {
188 $this->checkout_content_source = $checkout_page->post_content;
189 }
190
191 set_transient(
192 $cart_checkout_content_cache_transient_name,
193 array(
194 'cart_content_source' => $this->cart_content_source,
195 'checkout_content_source' => $this->checkout_content_source,
196 ),
197 DAY_IN_SECONDS
198 );
199 return;
200 }
201
202 // We are in a Block theme - so we need to find out if the templates are being used.
203 if ( function_exists( 'get_block_template' ) ) {
204 $checkout_template = get_block_template( 'woocommerce/woocommerce//page-checkout' );
205 $cart_template = get_block_template( 'woocommerce/woocommerce//page-cart' );
206
207 if ( ! $checkout_template ) {
208 $checkout_template = get_block_template( 'woocommerce/woocommerce//checkout' );
209 }
210
211 if ( ! $cart_template ) {
212 $cart_template = get_block_template( 'woocommerce/woocommerce//cart' );
213 }
214 }
215
216 if ( ! empty( $checkout_template->content ) ) {
217 // Checkout template is in use, but we need to see if the page-content-wrapper is in use, or if the template is being used directly.
218 $this->checkout_content_source = $checkout_template->content;
219 $is_using_page_content = str_contains( $checkout_template->content, '<!-- wp:woocommerce/page-content-wrapper {"page":"checkout"}' );
220
221 if ( $is_using_page_content ) {
222 // The page-content-wrapper is in use, so we need to get the page content.
223 $checkout_page = get_post( wc_get_page_id( 'checkout' ) );
224
225 if ( $checkout_page && isset( $checkout_page->post_content ) ) {
226 $this->checkout_content_source = $checkout_page->post_content;
227 }
228 }
229 }
230
231 if ( ! empty( $cart_template->content ) ) {
232 // Cart template is in use, but we need to see if the page-content-wrapper is in use, or if the template is being used directly.
233 $this->cart_content_source = $cart_template->content;
234 $is_using_page_content = str_contains( $cart_template->content, '<!-- wp:woocommerce/page-content-wrapper {"page":"cart"}' );
235
236 if ( $is_using_page_content ) {
237 // The page-content-wrapper is in use, so we need to get the page content.
238 $cart_page = get_post( wc_get_page_id( 'cart' ) );
239
240 if ( $cart_page && isset( $cart_page->post_content ) ) {
241 $this->cart_content_source = $cart_page->post_content;
242 }
243 }
244 }
245
246 set_transient(
247 $cart_checkout_content_cache_transient_name,
248 array(
249 'cart_content_source' => $this->cart_content_source,
250 'checkout_content_source' => $this->checkout_content_source,
251 ),
252 DAY_IN_SECONDS
253 );
254 }
255
256 /**
257 * Request-scoped — not for page output, see `get_page_common_properties()`.
258 *
259 * Default event properties for events the server fires itself on an uncached
260 * request. See `WC_Analytics_Tracking::get_common_properties()`.
261 *
262 * @return array Array of standard event props.
263 */
264 public function get_common_properties() {
265 $common_properties = WC_Analytics_Tracking::get_common_properties();
266 /**
267 * Allow defining custom event properties in WooCommerce Analytics.
268 *
269 * @module woocommerce-analytics
270 *
271 * @since 12.5
272 *
273 * @param array $properties Array of event props to be filtered.
274 */
275 $properties = apply_filters(
276 'jetpack_woocommerce_analytics_event_props',
277 $common_properties
278 );
279
280 return $properties;
281 }
282
283 /**
284 * Default event properties for the client, excluding anything derived from
285 * the current request.
286 *
287 * Used for the properties embedded in front-end page HTML, which is cached
288 * and replayed to later visitors. See
289 * `WC_Analytics_Tracking::get_page_common_properties()`.
290 *
291 * @since 0.16.7
292 *
293 * @return array Array of standard event props.
294 */
295 public function get_page_common_properties() {
296 $common_properties = WC_Analytics_Tracking::get_page_common_properties();
297
298 /** This filter is documented in src/class-woo-analytics-trait.php */
299 return apply_filters(
300 'jetpack_woocommerce_analytics_event_props',
301 $common_properties
302 );
303 }
304
305 /**
306 * Enqueue an event with optional product and custom properties.
307 *
308 * @param string $event_name The name of the event to record.
309 * @param array $properties Optional array of (key => value) event properties.
310 * @param integer|null $product_id The id of the product relating to the event.
311 *
312 * @return void
313 */
314 public function enqueue_event( $event_name, $properties = array(), $product_id = null ) {
315 // Only set product details if we have a product id.
316 if ( $product_id ) {
317 $product = wc_get_product( $product_id );
318 if ( ! $product instanceof WC_Product ) {
319 return;
320 }
321 $product_details = $this->get_product_details( $product );
322 }
323
324 $event_properties = array_merge( $product_details ?? array(), $properties );
325
326 WC_Analytics_Tracking::add_event_to_queue( $event_name, $event_properties );
327 }
328
329 /**
330 * Gather relevant product information
331 *
332 * @param \WC_Product $product product.
333 * @return array
334 */
335 public function get_product_details( $product ) {
336 return array(
337 'pi' => $product->get_id(),
338 'pn' => $product->get_title(),
339 'pc' => $this->get_product_categories_concatenated( $product ),
340 'pp' => $product->get_price(),
341 'pt' => $product->get_type(),
342 );
343 }
344
345 /**
346 * Gets product categories or varation attributes as a formatted concatenated string
347 *
348 * @param object $product WC_Product.
349 * @return string
350 */
351 public function get_product_categories_concatenated( $product ) {
352
353 if ( ! $product instanceof WC_Product ) {
354 return '';
355 }
356
357 $variation_data = $product->is_type( 'variation' ) ? wc_get_product_variation_attributes( $product->get_id() ) : '';
358 if ( is_array( $variation_data ) && ! empty( $variation_data ) ) {
359 $line = wc_get_formatted_variation( $variation_data, true );
360 } else {
361 $out = array();
362 $categories = get_the_terms( $product->get_id(), 'product_cat' );
363 if ( is_array( $categories ) ) {
364 foreach ( $categories as $category ) {
365 $out[] = $category->name;
366 }
367 }
368 $line = implode( '/', $out );
369 }
370 return $line;
371 }
372
373 /**
374 * Get the current user id
375 *
376 * @return string|null
377 */
378 public function get_user_id() {
379 if ( is_user_logged_in() ) {
380 $blogid = Jetpack_Connection::get_site_id();
381 $userid = get_current_user_id();
382 return $blogid . ':' . $userid;
383 }
384 return null;
385 }
386
387 /**
388 * Gets the IDs of additional blocks on the Cart/Checkout pages or templates.
389 *
390 * @param string $cart_or_checkout Whether to get blocks on the cart or checkout page.
391 * @return array All inner blocks on the page.
392 */
393 public function get_additional_blocks_on_page( $cart_or_checkout = 'cart' ) {
394 $additional_blocks_on_page_transient_name = 'jetpack_woocommerce_analytics_additional_blocks_on_' . $cart_or_checkout . '_page';
395 $additional_blocks_on_page = get_transient( $additional_blocks_on_page_transient_name );
396
397 if ( false !== $additional_blocks_on_page ) {
398 return $additional_blocks_on_page;
399 }
400
401 $content = $this->cart_content_source;
402
403 if ( 'checkout' === $cart_or_checkout ) {
404 $content = $this->checkout_content_source;
405 }
406
407 $blocks_to_ignore = array(
408 'woocommerce/classic-shortcode',
409 'core/shortcode',
410 'checkout' === $cart_or_checkout ? 'woocommerce/checkout' : 'woocommerce/cart',
411 );
412
413 $scanner = Block_Scanner::create( $content );
414 if ( ! $scanner ) {
415 return array();
416 }
417
418 $found_blocks = array();
419 $ignored_block_depth = 0; // Count how many ignored blocks we're nested inside.
420
421 while ( $scanner->next_delimiter() ) {
422 $type = $scanner->get_delimiter_type();
423 $block_type = $scanner->get_block_type();
424 $is_ignored_block = in_array( $block_type, $blocks_to_ignore, true );
425
426 switch ( $type ) {
427 case Block_Scanner::OPENER:
428 // If this is an ignored block, increase our nesting depth.
429 if ( $is_ignored_block ) {
430 ++$ignored_block_depth;
431 }
432
433 // Only collect blocks that are not inside any ignored block.
434 if ( 0 === $ignored_block_depth ) {
435 $found_blocks[] = $block_type;
436 }
437 break;
438
439 case Block_Scanner::CLOSER:
440 // If this closes an ignored block, decrease our nesting depth.
441 if ( $is_ignored_block && $ignored_block_depth > 0 ) {
442 --$ignored_block_depth;
443 }
444 break;
445
446 case Block_Scanner::VOID:
447 // Void blocks: only collect if we're not inside an ignored block and this isn't an ignored block itself.
448 if ( 0 === $ignored_block_depth && ! $is_ignored_block ) {
449 $found_blocks[] = $block_type;
450 }
451 break;
452 }
453 }
454
455 set_transient( $additional_blocks_on_page_transient_name, $found_blocks, DAY_IN_SECONDS );
456 return $found_blocks;
457 }
458
459 /**
460 * Gets an array containing the block or shortcode use properties for the Cart page.
461 *
462 * @return array An array containing the block or shortcode use properties for the Cart page.
463 */
464 public function get_cart_page_block_usage() {
465 $new_info = array();
466
467 $content = $this->cart_content_source;
468 $block_presence = str_contains( $content, '<!-- wp:woocommerce/cart' );
469 $shortcode_presence = str_contains( $content, '[woocommerce_cart]' );
470 $classic_shortcode_presence = str_contains( $content, '<!-- wp:woocommerce/classic-shortcode {"shortcode":"cart"}' );
471
472 $new_info['cart_page_contains_cart_block'] = $block_presence ? '1' : '0';
473 $new_info['cart_page_contains_cart_shortcode'] = $shortcode_presence || $classic_shortcode_presence ? '1' : '0';
474 return $new_info;
475 }
476
477 /**
478 * Gets an array containing the block or shortcode use properties for the Checkout page.
479 *
480 * @return array An array containing the block or shortcode use properties for the Checkout page.
481 */
482 public function get_checkout_page_block_usage() {
483 $new_info = array();
484
485 $content = $this->checkout_content_source;
486 $block_presence = str_contains( $content, '<!-- wp:woocommerce/checkout' );
487 $shortcode_presence = str_contains( $content, '[woocommerce_checkout]' );
488 $classic_shortcode_presence = str_contains( $content, '<!-- wp:woocommerce/classic-shortcode {"shortcode":"checkout"}' );
489
490 $new_info['checkout_page_contains_checkout_block'] = $block_presence ? '1' : '0';
491 $new_info['checkout_page_contains_checkout_shortcode'] = $shortcode_presence || $classic_shortcode_presence ? '1' : '0';
492 return $new_info;
493 }
494
495 /**
496 * Get info about the cart & checkout pages, in particular
497 * whether the store is using shortcodes or Gutenberg blocks.
498 * This info is cached in a transient.
499 *
500 * Note: similar code is in a WooCommerce core PR:
501 * https://github.com/woocommerce/woocommerce/pull/25932
502 *
503 * @return array
504 */
505 public function get_cart_checkout_info() {
506 $info = array_merge(
507 $this->get_cart_page_block_usage(),
508 $this->get_checkout_page_block_usage()
509 );
510 return $info;
511 }
512
513 /**
514 * Search a specific post for text content.
515 *
516 * Note: similar code is in a WooCommerce core PR:
517 * https://github.com/woocommerce/woocommerce/pull/25932
518 *
519 * @param integer $post_id The id of the post to search.
520 * @param string $text The text to search for.
521 * @return integer 1 if post contains $text (otherwise 0).
522 */
523 public function post_contains_text( $post_id, $text ) {
524 global $wpdb;
525
526 // Search for the text anywhere in the post.
527 $wildcarded = "%{$text}%";
528
529 // No better way to search post content without having filters expanding blocks.
530 // This is already cached up in the parent function.
531 $result = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
532 $wpdb->prepare(
533 "
534 SELECT COUNT( * ) FROM {$wpdb->prefix}posts
535 WHERE ID=%d
536 AND {$wpdb->prefix}posts.post_content LIKE %s
537 ",
538 array( $post_id, $wildcarded )
539 )
540 );
541
542 return ( '0' !== $result ) ? 1 : 0;
543 }
544
545 /**
546 * Get the cart total
547 *
548 * @return float
549 */
550 public function get_cart_total() {
551 $cart = WC()->cart;
552 if ( $cart === null ) {
553 return 0;
554 }
555 return $cart->get_total( 'tracking' );
556 }
557
558 /**
559 * Get the cart subtotal
560 *
561 * @return float
562 */
563 public function get_cart_subtotal() {
564 $cart = WC()->cart;
565 if ( $cart === null ) {
566 return 0;
567 }
568 return $cart->get_subtotal();
569 }
570
571 /**
572 * Get the cart shipping total
573 *
574 * @return float
575 */
576 public function get_cart_shipping_total() {
577 $cart = WC()->cart;
578 if ( $cart === null ) {
579 return 0;
580 }
581 return $cart->get_shipping_total();
582 }
583
584 /**
585 * Get the cart taxes
586 *
587 * @return float
588 */
589 public function get_cart_taxes() {
590 $cart = WC()->cart;
591 if ( $cart === null ) {
592 return 0;
593 }
594 return $cart->get_taxes_total();
595 }
596
597 /**
598 * Get the cart discount total
599 *
600 * @return float
601 */
602 public function get_total_discounts() {
603 $cart = WC()->cart;
604 if ( $cart === null ) {
605 return 0;
606 }
607 return $cart->get_discount_total();
608 }
609
610 /**
611 * Get number of items in the cart
612 *
613 * @return int
614 */
615 public function get_cart_items_count() {
616 $cart = WC()->cart;
617 if ( $cart === null ) {
618 return 0;
619 }
620 return $cart->get_cart_contents_count();
621 }
622
623 /**
624 * Retrieves the breadcrumb trail as an array of page titles.
625 *
626 * This function attempts to generate a hierarchical breadcrumb trail for the current page or post.
627 * - For the front page, it returns "Home".
628 * - For WooCommerce product, category, or tag pages, it uses the WooCommerce breadcrumb generator and prepends the shop page title if needed.
629 * - For regular pages, it builds the breadcrumb from the page's ancestors, ordered from top-level to current.
630 * - For all other cases, it returns the current page's title.
631 *
632 * Titles are capped before being returned; see `cap_page_string()`.
633 *
634 * @return array The breadcrumb trail as an array of titles.
635 */
636 private function get_breadcrumb_titles() {
637 return array_map( array( $this, 'cap_page_string' ), $this->build_breadcrumb_titles() );
638 }
639
640 /**
641 * Limit the length of a caller-influenced string bound for the page output.
642 *
643 * Breadcrumb titles come from `post_title`, which core does not bound, so this
644 * is their only limit. The search term is already blanked by
645 * `WP_Query::parse_query()` above 1600 bytes; capping it further keeps the
646 * search pixel URL, which carries the term twice, from being rejected outright.
647 *
648 * Truncated values keep an ellipsis so they stay distinguishable downstream
649 * from a value that genuinely ended at the limit.
650 *
651 * @since 0.16.7
652 *
653 * @param string $value The value bound for the page output.
654 * @return string The value, truncated if it exceeded the limit.
655 */
656 private function cap_page_string( $value ) {
657 $max_length = 200;
658
659 $value = (string) $value;
660
661 if ( mb_strlen( $value ) <= $max_length ) {
662 return $value;
663 }
664
665 return mb_substr( $value, 0, $max_length - 1 ) . '';
666 }
667
668 /**
669 * Build the uncapped breadcrumb trail. See `get_breadcrumb_titles()`.
670 *
671 * @return array The breadcrumb trail as an array of titles.
672 */
673 private function build_breadcrumb_titles() {
674 if ( is_front_page() ) {
675 return array( __( 'Home', 'woocommerce-analytics' ) );
676 }
677
678 if ( class_exists( '\WC_Breadcrumb' ) ) {
679 $breadcrumb = new \WC_Breadcrumb();
680 $crumbs = $breadcrumb->generate();
681 $titles = wp_list_pluck( $crumbs, 0 );
682
683 if ( is_product() || is_product_category() || is_product_tag() ) {
684 $titles = $this->prepend_shop_page_title( $titles );
685 }
686
687 if ( ! empty( $titles ) ) {
688 return $titles;
689 }
690 }
691
692 // If it's a page, get the hierarchical title.
693 if ( is_page() ) {
694 $titles = array();
695 $page_id = get_queried_object_id();
696 $ancestors = get_post_ancestors( $page_id );
697 // Reverse the ancestors to get the top-level first.
698 $ancestors = array_reverse( $ancestors );
699
700 foreach ( $ancestors as $ancestor ) {
701 $titles[] = get_the_title( $ancestor );
702 }
703 $titles[] = get_the_title( $page_id );
704
705 return $titles;
706 }
707
708 return array( get_the_title() );
709 }
710
711 /**
712 * Prepend the shop page title if it's not already present.
713 *
714 * @param array $titles The titles to prepend the shop page title to.
715 * @return array The titles with the shop page title prepended.
716 */
717 private function prepend_shop_page_title( array $titles ) {
718 $shop_page_id = wc_get_page_id( 'shop' );
719 if ( ! $shop_page_id ) {
720 return $titles;
721 }
722
723 $shop_page_title = get_the_title( $shop_page_id );
724
725 if ( ! $shop_page_title || ( ! empty( $titles ) && $titles[0] === $shop_page_title ) ) {
726 return $titles;
727 }
728
729 return array_merge( array( $shop_page_title ), $titles );
730 }
731 }
732