PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.1
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.1
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / inc / compatibility / class-merchant-ohio-theme.php
inc/compatibility
class-merchant-astra-theme.php 1.1 KB 1 month ago class-merchant-avada-theme.php 918 B 1 month ago class-merchant-blocksy-theme.php 4.8 KB 1 month ago class-merchant-botiga-theme.php 4.4 KB 1 month ago class-merchant-breakdance-builder.php 12.8 KB 1 month ago class-merchant-bricks-builder.php 1.5 KB 1 month ago class-merchant-divi-theme.php 5.9 KB 1 month ago class-merchant-elementor-builder.php 7.3 KB 1 month ago class-merchant-flatsome-theme.php 948 B 1 month ago class-merchant-kadence-theme.php 938 B 1 month ago class-merchant-oceanwp-theme.php 1.5 KB 1 month ago class-merchant-ohio-theme.php 26.1 KB 1 month ago class-merchant-twenty-twenty-four-theme.php 1.1 KB 1 month ago class-merchant-woo-multi-currency.php 4.7 KB 1 month ago class-merchant-woo-payments-plugin.php 1.6 KB 1 month ago

class-merchant-ohio-theme.php in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 2.3.1, at inc/compatibility/class-merchant-ohio-theme.php

702 lines 26.1 KB Raw Download Zip
1 <?php
2 /**
3 * Ohio Theme compatibility layer
4 *
5 * @package Merchant
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Ohio Theme compatibility layer
14 */
15 if ( ! class_exists( 'Merchant_Ohio_Theme' ) ) {
16 class Merchant_Ohio_Theme {
17
18 /**
19 * Constructor.
20 */
21 public function __construct() {
22 if ( ( is_admin() && ! wp_doing_ajax() ) || ! merchant_is_ohio_active() ) {
23 return;
24 }
25
26 // Custom CSS.
27 add_filter( 'merchant_custom_css', array( $this, 'frontend_custom_css' ) );
28
29 $ajax_cart = ! class_exists( 'OhioOptions' ) || OhioOptions::get( 'woocommerce_product_ajax_cart', true );
30
31 // Custom scripts
32 add_action( 'wp_footer', function() {
33 ?>
34 <script>
35 jQuery( document ).ready( function ( $ ) {
36
37 /**
38 * Ohio theme using their own AJAX scripts and not using 'added_to_cart' event which is required.
39 * So adding `added_to_cart` event here
40 */
41 const originalAjax = $.ajax;
42 $.ajax = function( options ) {
43 if (
44 options.url === wc_cart_fragments_params.ajax_url
45 && ( options.data.action === 'ohio_ajax_add_to_cart_woo' || options.data.action === 'ohio_ajax_add_to_cart_woo_single' ) ) {
46 const originalSuccess = options.success;
47 options.success = function(response) {
48 // Call the original Ohio success handler
49 originalSuccess.apply( this, arguments );
50 if ( response && ! response.error ) {
51 // Ensure fragments and cart_hash are present
52 const fragments = response.fragments || {};
53 const cart_hash = response.cart_hash || '';
54 const $button = $( '.data_button_ajax.loading' ); // Find the button that triggered it
55
56 // Trigger added_to_cart with the response data
57 $( document.body ).trigger( 'added_to_cart', [ fragments, cart_hash, $button ] );
58 }
59 };
60 }
61 return originalAjax.apply( this, arguments );
62 };
63
64 // Add loading class to show loading indicator
65 $( '.merchant-sticky-add-to-cart-wrapper button.button' ).addClass( 'data_button_ajax' );
66 $( 'body' ).on( 'adding_to_cart', function( event, $button, data ) {
67 if ( $button.closest( '.merchant-quick-view-content' ).length || $button.closest( '.merchant-sticky-add-to-cart-wrapper' ).length ) {
68 $button.addClass( 'btn-loading' );
69 }
70 } );
71
72 // Quick View, Wishlist, Product Labels, Product Video/Audio Position
73 $( '.merchant-product-labels, .merchant-quick-view-button, .merchant-wishlist-button, .merchant-product-video, .merchant-product-audio' ).each( function() {
74 const $product = $( this ).closest( 'li.product' );
75 const $thumbnail = $product.find( '.product-item-thumbnail' );
76
77 if ( $thumbnail.length ) {
78 $( this )
79 .appendTo( $thumbnail )
80 .css( { 'visibility': 'visible' } );
81 } else {
82 $( this )
83 .appendTo( $product )
84 .css( { 'visibility': 'visible' } );
85 }
86 } );
87 } );
88 </script>
89 <?php
90 } );
91
92 // Product labels
93 if ( Merchant_Modules::is_module_active( Merchant_Product_Labels::MODULE_ID ) ) {
94 $Product_Labels = Merchant_Modules::get_module( Merchant_Product_Labels::MODULE_ID );
95
96 // Archives/Loop
97 remove_action( 'woocommerce_before_shop_loop_item', array( $Product_Labels, 'loop_product_output' ) );
98 add_action( 'woocommerce_after_shop_loop_item_title', array( $Product_Labels, 'loop_product_output' ) );
99
100 // Single Product
101 remove_action( 'woocommerce_product_thumbnails', array( $Product_Labels, 'single_product_output' ) );
102 add_action( 'woocommerce_before_single_product_summary', array( $Product_Labels, 'single_product_output' ) );
103 }
104
105 // Quick View
106 if ( Merchant_Modules::is_module_active( Merchant_Quick_View::MODULE_ID ) && $ajax_cart ) {
107 $Quick_View = Merchant_Modules::get_module( Merchant_Quick_View::MODULE_ID );
108 remove_action( 'woocommerce_after_shop_loop_item', array( $Quick_View, 'quick_view_button' ) );
109 add_action( 'woocommerce_after_shop_loop_item_title', array( $Quick_View, 'quick_view_button' ) );
110 }
111
112 // Buy Now
113 if ( Merchant_Modules::is_module_active( Merchant_Buy_Now::MODULE_ID ) && $ajax_cart ) {
114 $Buy_Now = Merchant_Modules::get_module( Merchant_Buy_Now::MODULE_ID );
115 $button_renderer = $Buy_Now->get_button_renderer();
116
117 if ( $button_renderer ) {
118 remove_action( 'woocommerce_after_shop_loop_item', array( $button_renderer, 'shop_archive_button' ) );
119 add_action( 'woocommerce_after_shop_loop_item_title', array( $button_renderer, 'shop_archive_button' ) );
120 }
121 }
122
123 if ( merchant_is_pro_active() ) {
124 // FBT
125 if ( Merchant_Modules::is_module_active( Merchant_Frequently_Bought_Together::MODULE_ID ) ) {
126 add_filter( 'merchant_frequently_bought_together_woo_hook', function( $hook ) {
127 return 'woocommerce_product_after_tabs';
128 } );
129 }
130
131 // Recently Viewed Products
132 if ( Merchant_Modules::is_module_active( Merchant_Recently_Viewed_Products::MODULE_ID ) ) {
133 add_filter( 'merchant_recently_viewed_products_woo_hook', function( $hook ) {
134 return 'woocommerce_product_after_tabs';
135 } );
136
137 add_filter( 'merchant_recently_viewed_products_wrapper_classes', function( $classes ) {
138 $classes[] = 'woo-products';
139
140 return $classes;
141 } );
142 }
143
144 // Wishlist
145 if ( Merchant_Modules::is_module_active( Merchant_Wishlist::MODULE_ID ) ) {
146 add_filter( 'merchant_wishlist_archive_woo_hook', function( $hook ) {
147 return 'woocommerce_after_shop_loop_item_title';
148 } );
149 }
150
151 // Advanced Reviews
152 if ( Merchant_Modules::is_module_active( Merchant_Advanced_Reviews::MODULE_ID ) ) {
153 add_filter( 'merchant_advanced_review_woo_hook', function( $hook ) {
154 return 'woocommerce_product_after_tabs';
155 } );
156 }
157
158 // Side Cart
159 if ( Merchant_Modules::is_module_active( Merchant_Side_Cart::MODULE_ID ) ) {
160 // remove_filter( 'woocommerce_cart_item_name', 'ohio_add_cart_product_category', 99 );
161 }
162
163 // Product Video/Audio
164 if ( Merchant_Modules::is_module_active( Merchant_Product_Video::MODULE_ID ) || Merchant_Modules::is_module_active( Merchant_Product_Audio::MODULE_ID ) ) {
165 // Archive
166 add_filter( 'merchant_product_video_before_woo_hook', function() {
167 return 'woocommerce_after_shop_loop_item_title';
168 } );
169
170 add_filter( 'merchant_product_audio_before_woo_hook', function() {
171 return 'woocommerce_after_shop_loop_item_title';
172 } );
173
174 /*
175 * Single Product
176 *
177 * `YITH_Featured_Audio_Video_Init` function is needed in Ohio theme for firing `woocommerce_single_product_image_thumbnail_html` action
178 * That's why just defining it here to make sure it exists.
179 * No need any implementation
180 *
181 */
182 if ( ! function_exists( 'YITH_Featured_Audio_Video_Init' ) ) {
183 function YITH_Featured_Audio_Video_Init() {}
184 }
185 }
186
187 if ( Merchant_Modules::is_module_active( Merchant_Complementary_Products::MODULE_ID ) ) {
188 // Complementary products
189 add_action( 'woocommerce_ajax_added_to_cart', array( $this, 'complementary_products_support' ) );
190 }
191
192 // Size Chart
193 if ( Merchant_Modules::is_module_active( Merchant_Size_Chart::MODULE_ID ) ) {
194 add_filter( 'merchant_size_chart_skipped_placement_fires', array( $this, 'size_chart_skipped_placement_fires' ), 10, 3 );
195 }
196 }
197 }
198
199 /**
200 * Ignore Ohio's extra add to cart button hook on variable products.
201 *
202 * Ohio's variable.php fires woocommerce_before_add_to_cart_button above the
203 * variation block, then variation-add-to-cart-button.php fires it again where
204 * WooCommerce core does. Skipping the first one keeps the size chart next to
205 * the add to cart button, which is the position the setting offers.
206 *
207 * @param int $skipped Number of fires to ignore.
208 * @param string $hook_name The hook the size chart renders on.
209 * @param WC_Product $product The current product.
210 *
211 * @return int
212 */
213 public function size_chart_skipped_placement_fires( $skipped, $hook_name, $product ) {
214 if ( 'woocommerce_before_add_to_cart_button' !== $hook_name ) {
215 return $skipped;
216 }
217
218 if ( ! $product || ! $product->is_type( 'variable' ) ) {
219 return $skipped;
220 }
221
222 return 1;
223 }
224
225 /**
226 * Frontend custom CSS.
227 *
228 * @param string $css The custom CSS.
229 * @return string $css The custom CSS.
230 */
231 public function frontend_custom_css( $css ) {
232 $css .= Merchant_Side_Cart::get_module_custom_css();
233
234 $css .= '
235 body .cart_item .product-name .variation,
236 body .cart_item .product-total .variation {
237 display: block;
238 }
239 body .cart_item .product-name .variation dd,
240 body .cart_item .product-total .variation dd {
241 margin-inline: 0;
242 }
243 body .cart_item .product-name .variation p,
244 body .cart_item .product-total .variation p {
245 margin: 0;
246 }
247 body.wp-theme-ohio .merchant-cart-offers .merchant-cart-offers-container{
248 position: static;
249 }
250 ';
251
252 // Product labels
253 if ( Merchant_Modules::is_module_active( Merchant_Product_Labels::MODULE_ID ) ) {
254 $css .= '
255 .product-item {
256 position: relative;
257 }
258 li.product .merchant-product-labels {
259 visibility: hidden;
260 }
261 ';
262 }
263
264 // Size Chart
265 if ( Merchant_Modules::is_module_active( Merchant_Size_Chart::MODULE_ID ) ) {
266 // The add to cart button positions land inside a flex row here, where the
267 // button\'s own bottom margin knocks it out of line with the button.
268 $css .= '
269 .woo-product-details .variations_button .merchant-product-size-chart {
270 margin: 0 1rem 0 0;
271 }
272 ';
273 }
274
275 // Quick View
276 if ( Merchant_Modules::is_module_active( Merchant_Quick_View::MODULE_ID ) ) {
277 $css .= '
278 li.product .merchant-quick-view-button {
279 visibility: hidden;
280 }
281 ';
282 }
283
284 // Buy Now
285 if ( Merchant_Modules::is_module_active( Merchant_Buy_Now::MODULE_ID ) ) {
286 $css .= '
287 .data_button_ajax.btn-loading.loading:before {
288 position: absolute;
289 }
290 ';
291 }
292
293 if ( merchant_is_ohio_active() ) {
294 // Wishlist
295 if ( Merchant_Modules::is_module_active( Merchant_Wishlist::MODULE_ID ) ) {
296 $css .= '
297 li.product .merchant-wishlist-button {
298 visibility: hidden;
299 }
300 ';
301 }
302
303 // Side Cart
304 if ( Merchant_Modules::is_module_active( Merchant_Side_Cart::MODULE_ID ) ) {
305 $css .= '
306 .merchant-side-cart-item .woo-product-name,
307 .merchant-side-cart-item .woo-category {
308 display: none !important;
309 }
310 .merchant-side-cart-widget .product_list_widget .merchant-quantity-wrap span.merchant-cart-item-name a {
311 display: block !important;
312 }
313 ';
314 }
315
316 // Advanced Review
317 if ( Merchant_Modules::is_module_active( Merchant_Advanced_Reviews::MODULE_ID ) ) {
318 $css .= '
319 .merchant-adv-reviews-modal-photo-slider .star-rating:before,
320 .merchant-adv-reviews .star-rating:before {
321 content: "�
322
323
324
325
326 " !important;
327 }
328 ';
329 }
330
331 // Product Video/Audio
332 if ( Merchant_Modules::is_module_active( Merchant_Product_Video::MODULE_ID ) || Merchant_Modules::is_module_active( Merchant_Product_Audio::MODULE_ID ) ) {
333 $css .= '
334 li.product:has(.merchant-product-video) .product-item-thumbnail .image-holder,
335 li.product:has(.merchant-product-audio) .product-item-thumbnail .image-holder {
336 display: none;
337 }
338 li.product .merchant-product-video,
339 li.product .merchant-product-audio {
340 visibility: hidden;
341 }
342 ';
343 }
344 }
345
346 return $css;
347 }
348
349 /**
350 * Get the variation ID from attributes for a variable product.
351 *
352 * @param int $product_id The variable product ID.
353 * @param array $variation_data The selected variation attributes.
354 *
355 * @return int The variation ID or 0 if not found.
356 */
357 private function get_variation_id_from_attributes( $product_id, $variation_data ) {
358 // Get the product object.
359 $product = wc_get_product( $product_id );
360 if ( ! $product || $product->get_type() !== 'variable' ) {
361 return 0;
362 }
363
364 // Ensure variation data keys are prefixed correctly.
365 $formatted_variation_data = array();
366 foreach ( $variation_data as $key => $value ) {
367 // If the key doesn't start with 'attribute_', assume it's the attribute name and prefix it.
368 $formatted_key = strpos( $key, 'attribute_' ) === 0 ? $key : 'attribute_' . sanitize_title( $key );
369 $formatted_variation_data[ $formatted_key ] = sanitize_text_field( $value );
370 }
371
372 // Get available variations for comparison (optional debugging).
373 $available_variations = $product->get_available_variations();
374 $data_store = WC_Data_Store::load( 'product' );
375 $variation_id = $data_store->find_matching_product_variation( $product, $formatted_variation_data );
376
377 /**
378 * Filter the variation ID found for a complementary product.
379 *
380 * @param int $variation_id The found variation ID (0 if not found).
381 * @param int $product_id The variable product ID.
382 * @param array $formatted_variation_data The formatted variation attributes.
383 * @param array $available_variations The available variations for the product.
384 *
385 * @since 2.2.1
386 */
387 return apply_filters( 'merchant_ohio_complementary_products_variation_id',
388 (int) $variation_id,
389 $product_id,
390 $formatted_variation_data,
391 $available_variations
392 );
393 }
394
395 /**
396 * Add support for complementary products module with Ohio theme custom add to cart action.
397 *
398 * @param int $cart_product_id The ID of the product added to cart.
399 *
400 * @return void
401 * @throws Exception When the cart fails to add the product.
402 */
403 public function complementary_products_support( $cart_product_id ) {
404 if ( ! $this->should_handle_complementary_products() ) {
405 return;
406 }
407
408 $request = $this->get_complementary_request_data();
409 if ( ! $request ) {
410 return;
411 }
412
413 $cart = WC()->cart;
414 $cart_item = $this->find_parent_cart_item( $cart, $cart_product_id, $request['variation_id'] );
415
416 if ( ! $cart_item ) {
417 return;
418 }
419
420 $this->mark_parent_cart_item(
421 $cart,
422 $cart_item['key'],
423 $cart_item['data'],
424 $request['campaign_id']
425 );
426
427 $this->add_complementary_products_to_cart(
428 $cart,
429 $cart_product_id,
430 $cart_item['key'],
431 $request['campaign_id'],
432 $request['products'],
433 $request['quantity']
434 );
435 }
436
437 /**
438 * Check if complementary products logic should run.
439 *
440 * @return bool
441 */
442 private function should_handle_complementary_products() {
443 if ( ! Merchant_Modules::is_module_active( Merchant_Complementary_Products::MODULE_ID ) ) {
444 return false;
445 }
446
447 if ( ! merchant_is_ohio_active() ) {
448 return false;
449 }
450
451 if ( empty( $_POST['complementary_offer_products'] ) || '[]' === $_POST['complementary_offer_products'] ) {
452 return false;
453 }
454
455 if (
456 ! isset( $_POST['complementary_offer_products_nonce'] )
457 || ! wp_verify_nonce(
458 sanitize_text_field( wp_unslash( $_POST['complementary_offer_products_nonce'] ) ),
459 'complementary_offer_products'
460 )
461 ) {
462 return false;
463 }
464
465 return true;
466 }
467
468 /**
469 * Read and sanitize complementary products request data.
470 *
471 * We are ignoring nonce verification here because it is already verified in should_handle_complementary_products().
472 *
473 * @return array|null
474 */
475 private function get_complementary_request_data() {
476 if ( ! isset( $_POST['complementary_offer_products'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
477 return null;
478 }
479 //phpcs:ignore WordPress.Security.NonceVerification.Missing
480 $complementary_products_json = sanitize_text_field( wp_unslash( $_POST['complementary_offer_products'] ) );
481 $complementary_products = json_decode( $complementary_products_json, true );
482
483 if ( ! is_array( $complementary_products ) || empty( $complementary_products ) ) {
484 return null;
485 }
486
487 //phpcs:ignore WordPress.Security.NonceVerification.Missing
488 if ( ! isset( $_POST['complementary_offer_id'] ) ) {
489 return null;
490 }
491
492 //phpcs:ignore WordPress.Security.NonceVerification.Missing
493 $quantity = isset( $_POST['quantity'] ) ? absint( wp_unslash( $_POST['quantity'] ) ) : 1;
494
495 //phpcs:ignore WordPress.Security.NonceVerification.Missing
496 $variation_id = isset( $_POST['variation_id'] ) ? absint( wp_unslash( $_POST['variation_id'] ) ) : 0;
497
498 return array(
499 'products' => $complementary_products,
500 //phpcs:ignore WordPress.Security.NonceVerification.Missing
501 'campaign_id' => sanitize_text_field( wp_unslash( $_POST['complementary_offer_id'] ) ),
502 'quantity' => $quantity,
503 'variation_id' => $variation_id,
504 );
505 }
506
507 /**
508 * Find the parent cart item (main product) in the cart.
509 *
510 * @param WC_Cart $cart Cart instance.
511 * @param int $product_id Product ID added to cart.
512 * @param int $variation_id Variation ID (if any).
513 *
514 * @return array|null {
515 * @type string $key
516 * @type array $data
517 * }
518 */
519 private function find_parent_cart_item( WC_Cart $cart, $product_id, $variation_id ) {
520 $cart_items = $cart->get_cart();
521
522 foreach ( $cart_items as $key => $cart_item ) {
523 if ( $variation_id && isset( $cart_item['variation_id'] ) && (int) $cart_item['variation_id'] === (int) $variation_id ) {
524 return array(
525 'key' => $key,
526 'data' => $cart_item,
527 );
528 }
529
530 if ( ! $variation_id && isset( $cart_item['product_id'] ) && (int) $cart_item['product_id'] === (int) $product_id ) {
531 return array(
532 'key' => $key,
533 'data' => $cart_item,
534 );
535 }
536 }
537
538 return null;
539 }
540
541 /**
542 * Mark the parent cart item with complementary metadata.
543 *
544 * @param WC_Cart $cart Cart instance.
545 * @param string $cart_item_key Cart item key.
546 * @param array $cart_item_data Existing cart item data.
547 * @param string $campaign_id Campaign/offer ID.
548 *
549 * @return void
550 */
551 private function mark_parent_cart_item( WC_Cart $cart, $cart_item_key, array $cart_item_data, $campaign_id ) {
552 $cart_item_data['is_complementary_parent'] = true;
553 $cart_item_data['merchant_added_via'] = 'complementary_products';
554 $cart_item_data['merchant_module_id'] = Merchant_Complementary_Products::MODULE_ID;
555 $cart_item_data['complementary_offer_id'] = $campaign_id;
556
557 $cart->cart_contents[ $cart_item_key ] = array_merge( $cart->cart_contents[ $cart_item_key ], $cart_item_data );
558 }
559
560 /**
561 * Add all complementary products to the cart.
562 *
563 * @param WC_Cart $cart
564 * @param int $main_product_id
565 * @param string $parent_cart_item_key
566 * @param string $campaign_id
567 * @param array $complementary_products
568 * @param int $quantity
569 *
570 * @return void
571 */
572 private function add_complementary_products_to_cart(
573 WC_Cart $cart,
574 $main_product_id,
575 $parent_cart_item_key,
576 $campaign_id,
577 $complementary_products,
578 $quantity
579 ) {
580 foreach ( $complementary_products as $comp_product ) {
581 $this->add_single_complementary_product(
582 $cart,
583 $main_product_id,
584 $parent_cart_item_key,
585 $campaign_id,
586 $comp_product,
587 $quantity
588 );
589 }
590 }
591
592 /**
593 * Add a single complementary product to the cart.
594 *
595 * @param WC_Cart $cart
596 * @param int $main_product_id
597 * @param string $parent_cart_item_key
598 * @param string $campaign_id
599 * @param array $comp_product
600 * @param int $quantity
601 *
602 * @return void
603 */
604 private function add_single_complementary_product(
605 WC_Cart $cart,
606 $main_product_id,
607 $parent_cart_item_key,
608 $campaign_id,
609 array $comp_product,
610 $quantity
611 ) {
612 $comp_product_id = absint( $comp_product['product_id'] );
613 $comp_product_object = wc_get_product( $comp_product_id );
614
615 if ( ! $comp_product_object ) {
616 wc_add_notice(
617 sprintf(
618 /* translators: %s: Product ID */
619 __( 'Complementary product with ID "%s" does not exist.', 'merchant' ),
620 $comp_product_id
621 ),
622 'error'
623 );
624
625 return;
626 }
627
628 $product_type = sanitize_text_field( $comp_product['product_type'] );
629 $variation_data = $this->sanitize_variation_data(
630 isset( $comp_product['variation_data'] ) ? $comp_product['variation_data'] : array()
631 );
632
633 $comp_variation_id = 0;
634 if ( 'variable' === $product_type && ! empty( $variation_data ) ) {
635 $comp_variation_id = $this->get_variation_id_from_attributes( $comp_product_id, $variation_data );
636 if ( ! $comp_variation_id ) {
637 wc_add_notice(
638 sprintf(
639 /* translators: %s: Product name */
640 __( 'Invalid variation selected for complementary product "%s".', 'merchant' ),
641 $comp_product_object->get_name()
642 ),
643 'error'
644 );
645
646 return;
647 }
648 }
649
650 $comp_cart_item_data = array(
651 'complementary_main_product' => $main_product_id,
652 'complementary_parent' => $parent_cart_item_key,
653 'complementary_offer_id' => $campaign_id,
654 'merchant_added_via' => 'complementary_products',
655 'merchant_module_id' => Merchant_Complementary_Products::MODULE_ID,
656 'is_complementary' => true,
657 );
658
659 $added_to_cart = $cart->add_to_cart(
660 $comp_product_id,
661 $quantity,
662 $comp_variation_id,
663 $variation_data,
664 $comp_cart_item_data
665 );
666
667 if ( ! $added_to_cart ) {
668 wc_add_notice(
669 sprintf(
670 /* translators: %s: Product name */
671 __( 'Failed to add complementary product "%s" to the cart.', 'merchant' ),
672 $comp_product_object->get_name()
673 ),
674 'error'
675 );
676 }
677 }
678
679 /**
680 * Sanitize variation data array.
681 *
682 * @param array $variation_data
683 *
684 * @return array
685 */
686 private function sanitize_variation_data( array $variation_data ) {
687 $sanitized = array();
688
689 foreach ( $variation_data as $key => $value ) {
690 $sanitized_key = sanitize_key( $key );
691 $sanitized_value = map_deep( $value, 'sanitize_text_field' );
692 $sanitized[ $sanitized_key ] = $sanitized_value;
693 }
694
695 return $sanitized;
696 }
697 }
698
699 add_action( 'init', function() {
700 new Merchant_Ohio_Theme();
701 } );
702 }