PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.9
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.9
2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 1.8.1 All 42 releases
sellkit / includes / elementor / modules / checkout / classes / local-hooks.php

local-hooks.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster 1.7.9, at includes/elementor/modules/checkout/classes/local-hooks.php

1,184 lines 34.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Sellkit\Elementor\Modules\Checkout\Classes;
4
5 defined( 'ABSPATH' ) || exit;
6
7 use Sellkit\Elementor\Modules\Checkout\Classes\{ Helper, Global_Hooks };
8 use Elementor\Plugin as Elementor;
9 use Sellkit_Funnel;
10 use Sellkit\Global_Checkout\Checkout;
11
12 /**
13 * Local hooks.
14 * Applies before widget.
15 *
16 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
17 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
18 * @SuppressWarnings(PHPMD.TooManyMethods)
19 * @SuppressWarnings(PHPMD.NPathComplexity)
20 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
21 * @since 1.1.0
22 */
23 class Local_Hooks {
24 /**
25 * Array of added products to cart.
26 *
27 * @since 1.7.9
28 * @var array
29 */
30 public static $in_cart = [];
31
32 /**
33 * Create instance of class without construct.
34 *
35 * @since 1.1.0
36 * @return object
37 */
38 public static function instance() {
39 $class = new \ReflectionClass( __CLASS__ );
40 return $class->newInstanceWithoutConstructor();
41 }
42
43 /**
44 * Class construct.
45 *
46 * @param array $settings widget settings.
47 * @param array $widget widget object.
48 * @since 1.1.0
49 */
50 public function __construct( $settings, $widget ) {
51 $this->settings = $settings;
52 $this->widget = $widget;
53 $this->actions();
54 }
55
56 /**
57 * Actions.
58 * required actions to apply changes to woocommerce checkout shortcode.
59 *
60 * @return void
61 * @since 1.1.0
62 */
63 private function actions() {
64 // sellkit wrapper for checkout widget.
65 add_action( 'woocommerce_before_checkout_form', [ $this, 'open_multistep_wrap' ] );
66 add_action( 'woocommerce_after_checkout_form', [ $this, 'close_multistep_wrap' ], 999 );
67
68 // Add express section to widget.
69 $this->add_express_checkout();
70
71 // Replace Woocommerce templates that require huge changes.
72 add_filter( 'woocommerce_locate_template', [ $this, 'template_replace' ], 1, 3 );
73
74 // Remove coupon from it's default location. will add custom coupon form based on design at desired location.
75 remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10, 1 );
76
77 // Filter all woocommerce field right before printing them in our widget.
78 add_filter( 'woocommerce_checkout_fields', [ $this, 'woocommerce_checkout_fields' ] );
79
80 // Hide shipping fields section title.
81 add_filter( 'sellkit-checkout-disable-shipping-fields-title', [ $this, 'check_to_disable_shipping_title' ] );
82
83 // Customize shipping fields based on user desire.
84 add_action( 'sellkit_checkout_shipping_fields', [ $this, 'sellkit_checkout_shipping_field' ], 10, 2 );
85
86 // Customize billing fields based on user desire.
87 add_action( 'sellkit_checkout_billing_fields', [ $this, 'sellkit_checkout_billing_field' ], 10, 2 );
88
89 // Rename Shipping text to Shipping Method.
90 add_filter( 'woocommerce_shipping_package_name', [ $this, 'rename_shipping_text' ] );
91
92 // Remove login form from default location.
93 remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_login_form', 10 );
94
95 // Append widget settings to checkout form 2 hidden fields form_id post_id.
96 add_action( 'woocommerce_checkout_before_customer_details', [ $this, 'widget_settings' ] );
97
98 // Apply custom messages.
99 $this->custom_messages();
100
101 // Enable or Disable Sections based on user selection.
102 add_action( 'woocommerce_before_checkout_form', function() {
103 self::enable_disable_sections( $this->settings, [] );
104 } );
105
106 // Inject required scripts for google address autocomplete.
107 add_action( 'sellkit-after-checkout-content', [ $this, 'inject_google_autocomplete' ] );
108
109 // Order bump html.
110 add_action( 'woocommerce_before_checkout_form', [ $this, 'order_bump' ] );
111
112 // Bundle products.
113 $this->bundled_products();
114
115 // Trigger for upsell steps popup.
116 add_action( 'sellkit_checkout_required_hidden_fields', [ $this, 'add_trigger_field_for_upsell_steps' ] );
117
118 // Add upsell templates at the end of page.
119 add_action( 'wp_footer', [ $this, 'sellkit_funnel_display_upsell_as_popup' ] );
120 }
121
122 /**
123 * Opens a wrapper around widget checkout form.
124 *
125 * @return void
126 * @since 1.1.0
127 */
128 public function open_multistep_wrap() {
129 ?>
130 <div id="sellkit-checkout-widget-id" class="sellkit-multistep-checkout-wrap sellkit-mobile-design-checkout-widget">
131 <?php
132 }
133
134 /**
135 * Close opened wrapper around checkout form.
136 *
137 * @return void
138 * @since 1.1.0
139 */
140 public function close_multistep_wrap() {
141 echo '</div>';
142 }
143
144 /**
145 * Inject express checkout section into the checkout form based on layout.
146 *
147 * @return void
148 * @since 1.1.0
149 */
150 private function add_express_checkout() {
151 if ( ! sellkit()->has_pro ) {
152 return;
153 }
154
155 $show_express = true;
156
157 // Disable express checkout.
158 if ( 'yes' !== $this->settings['show_express_checkout'] ) {
159 $show_express = false;
160 }
161
162 $gateways = [
163 'Paypal_For_Woocommerce',
164 'Woo_Payment_Gateway',
165 'Stripe_For_Woocommerce',
166 'Klarma_Checkout_Woocommerce',
167 'Amazon_Pay_Woocommerce',
168 'Stripe_Woocommerce_Official',
169 ];
170
171 foreach ( $gateways as $gateway ) {
172 $class = 'Sellkit\Elementor\Modules\Checkout\Integrations\\' . $gateway;
173 $class = new $class();
174 $class->run();
175 }
176
177 if ( false === $show_express ) {
178 return;
179 }
180
181 if ( 'one-page' === $this->settings['layout-type'] ) {
182 add_action( 'sellkit_checkout_one_page_express_methods', [ $this, 'express_checkout_html' ] );
183 return;
184 }
185
186 add_action( 'sellkit-checkout-step-a-begins', [ $this, 'express_checkout_html' ], 20 );
187 }
188
189 /**
190 * Replace Woocommerce templates that needs to be changed, locally.
191 * this template_replace does not affect default checkout page.
192 *
193 * @param string $template template name.
194 * @param string $template_name name.
195 * @param string $template_path path of template.
196 * @return string
197 * @since 1.1.0
198 */
199 public function template_replace( $template, $template_name, $template_path ) {
200 $basename = basename( $template );
201 $our_path = sellkit()->plugin_dir() . 'includes/elementor/modules/checkout/';
202
203 switch ( $basename ) {
204 case 'form-checkout.php':
205 $template = $our_path . 'templates/form-checkout.php';
206 break;
207 case 'form-login.php':
208 $template = $our_path . 'templates/form-login.php';
209 break;
210 case 'form-shipping.php':
211 $template = $our_path . 'templates/form-shipping.php';
212 break;
213 case 'form-billing.php':
214 $template = $our_path . 'templates/form-billing.php';
215 break;
216 case 'payment.php':
217 $template = $our_path . 'templates/payment.php';
218 break;
219 case 'payment-method.php':
220 $template = $our_path . 'templates/payment-method.php';
221 break;
222 case 'review-order.php':
223 $template = $our_path . 'templates/review-order.php';
224 break;
225 case 'cart-shipping.php':
226 $template = $our_path . 'templates/cart-shipping.php';
227 break;
228 case 'cart-item-data.php':
229 $template = $our_path . 'templates/cart-item-data.php';
230 break;
231 case 'terms.php':
232 $template = $our_path . 'templates/terms.php';
233 break;
234 }
235
236 return $template;
237 }
238
239 /**
240 * Customize shipping fields parameters based on user needs through widget options.
241 *
242 * @param array $default_fields woocommerce fields.
243 * @return array
244 * @since 1.1.0
245 */
246 public function customize_shipping_field( $default_fields ) {
247 $widget_shipping_fields = $this->settings['shipping_list'];
248 $widget_field_slug = Helper::instance()->get_user_defined_fields_slug( $widget_shipping_fields, 'shipping_list_field' );
249
250 // Unset default fields.
251 foreach ( $default_fields as $key => $field ) {
252 if ( ! in_array( $key, $widget_field_slug, true ) ) {
253 unset( $default_fields[ $key ] );
254 }
255 }
256
257 return $default_fields;
258 }
259
260 /**
261 * Check to disable shipping fields section title.
262 *
263 * @since 1.2.1
264 */
265 public function check_to_disable_shipping_title() {
266 $widget_shipping_fields = $this->settings['shipping_list'];
267
268 if ( 0 === count( $widget_shipping_fields ) ) {
269 return false;
270 }
271
272 return true;
273 }
274
275 /**
276 * Print shipping field in frontend using our customized fields.
277 *
278 * @param array $fields : shipping fields.
279 * @param object $checkout : checkout object.
280 * @since 1.1.0
281 */
282 public function sellkit_checkout_shipping_field( $fields, $checkout ) {
283 $default_text = [
284 'shipping_first_name',
285 'shipping_last_name',
286 'shipping_company',
287 'shipping_address_1',
288 'shipping_address_2',
289 'shipping_postcode',
290 'shipping_city',
291 ];
292
293 $default_select = [
294 'shipping_country',
295 'shipping_state',
296 ];
297
298 foreach ( $fields as $key => $details ) {
299 if ( in_array( $key, $default_text, true ) ) {
300 $fields[ $key ]['type'] = 'text';
301 }
302
303 if ( in_array( $key, $default_select, true ) ) {
304 $fields[ $key ]['type'] = 'select';
305 }
306 }
307
308 $widget_shipping_fields = $this->settings['shipping_list'];
309 $default_fields = Helper::instance()->assign_settings_per_field( $fields, $widget_shipping_fields, 'shipping', $this->settings );
310
311 $priority = array_column( $default_fields, 'priority' );
312 array_multisort( $priority, SORT_ASC, $default_fields );
313
314 foreach ( $default_fields as $key => $details ) {
315 if ( ! array_key_exists( 'type', $details ) ) {
316 continue;
317 }
318
319 $class = $details['type'];
320 $class = '\Sellkit\Elementor\Modules\Checkout\Fields\\' . ucfirst( $class );
321 $class = new $class();
322 $field = '';
323
324 $class->final_html_structure( $field, $details, $key );
325 }
326 }
327
328 /**
329 * Customize billing fields parameters based on user needs through widget options.
330 *
331 * @param array $default_fields woocommerce fields.
332 * @return array
333 * @since 1.1.0
334 */
335 public function customize_billing_field( $default_fields ) {
336 $widget_billing_fields = $this->settings['billing_list'];
337 $widget_field_slug = Helper::instance()->get_user_defined_fields_slug( $widget_billing_fields, 'billing_list_field' );
338
339 // Unset default fields.
340 foreach ( $default_fields as $key => $field ) {
341 if ( ! in_array( $key, $widget_field_slug, true ) && 'billing_email' !== $field ) {
342 unset( $default_fields[ $key ] );
343 }
344 }
345
346 return $default_fields;
347 }
348
349 /**
350 * Print billing fields in frontend using our customized fields.
351 *
352 * @param array $fields : billing fields.
353 * @param object $checkout : checkout object.
354 * @since 1.1.0
355 */
356 public function sellkit_checkout_billing_field( $fields, $checkout ) {
357 $default_text = [
358 'billing_first_name',
359 'billing_last_name',
360 'billing_company',
361 'billing_address_1',
362 'billing_address_2',
363 'billing_postcode',
364 'billing_city',
365 ];
366
367 $default_select = [
368 'billing_country',
369 'billing_state',
370 ];
371
372 $default_tel = [
373 'billing_phone',
374 ];
375
376 foreach ( $fields as $key => $details ) {
377 if ( in_array( $key, $default_text, true ) ) {
378 $fields[ $key ]['type'] = 'text';
379 }
380
381 if ( in_array( $key, $default_select, true ) ) {
382 $fields[ $key ]['type'] = 'select';
383 }
384
385 if ( in_array( $key, $default_tel, true ) ) {
386 $fields[ $key ]['type'] = 'tel';
387 }
388 }
389
390 $widget_billing_fields = $this->settings['billing_list'];
391 $default_fields = Helper::instance()->assign_settings_per_field( $fields, $widget_billing_fields, 'billing', $this->settings );
392
393 $priority = array_column( $default_fields, 'priority' );
394 array_multisort( $priority, SORT_ASC, $default_fields );
395
396 foreach ( $default_fields as $key => $details ) {
397 // Never shot field that it's type is not defined.
398 if ( ! array_key_exists( 'type', $details ) ) {
399 continue;
400 }
401
402 // Billing email is already defined at top of page.
403 if ( 'billing_email' === $key ) {
404 continue;
405 }
406
407 $class = $details['type'];
408 $class = '\Sellkit\Elementor\Modules\Checkout\Fields\\' . ucfirst( $class );
409 $class = new $class();
410 $field = '';
411
412 $field = $class->final_html_structure( $field, $details, $key );
413 }
414 }
415
416 /**
417 * Hook to fields before shortcode.
418 *
419 * @param array $default_fields woocommerce fields.
420 * @return array
421 * @since 1.1.0
422 */
423 public function woocommerce_checkout_fields( $default_fields ) {
424 $widget_billing_fields = $this->settings['billing_list'];
425 $widget_shipping_fields = $this->settings['shipping_list'];
426
427 // Unset fields.
428 $default_shipping_fields = $default_fields['shipping'];
429 $default_fields['shipping'] = $this->customize_shipping_field( $default_shipping_fields );
430
431 // Unset fields.
432 $default_billing_fields = $default_fields['billing'];
433 $default_fields['billing'] = $this->customize_billing_field( $default_billing_fields );
434
435 foreach ( $widget_billing_fields as $data ) {
436 $slug = $data['billing_list_field'];
437
438 if ( 'yes' !== $data['billing_list_required'] && array_key_exists( $slug, $default_fields['billing'] ) ) {
439 $default_fields['billing'][ $slug ]['required'] = false;
440 unset( $default_fields['billing'][ $slug ]['required'] );
441 }
442 }
443
444 foreach ( $widget_shipping_fields as $data ) {
445 $slug = isset( $data['shipping_list_field'] ) ? $data['shipping_list_field'] : '';
446
447 if ( 'yes' !== $data['shipping_list_required'] && array_key_exists( $slug, $default_fields['shipping'] ) ) {
448 $default_fields['shipping'][ $slug ]['required'] = false;
449 unset( $default_fields['shipping'][ $slug ]['required'] );
450 }
451 }
452
453 return $default_fields;
454 }
455
456 /**
457 * Rename Shipping text
458 *
459 * @param string $name title of shipping methods.
460 * @return string
461 * @since 1.1.0
462 */
463 public function rename_shipping_text( $name ) {
464 return '<h4 id="shipping_header_title" class="shipping-method-header heading">' . __( 'Shipping Methods', 'sellkit' ) . '</h4>';
465 }
466
467 /**
468 * Adds widget settings as hidden input to checkout form, to be used in various places.
469 *
470 * @return void
471 * @since 1.1.0
472 */
473 public function widget_settings() {
474 ?>
475 <input type="hidden" name="post_id" value="<?php echo esc_attr( self::get_current_post_id() ); ?>" />
476 <input type="hidden" name="form_id" value="<?php echo esc_attr( $this->widget->get_id() ); ?>" />
477 <?php
478 }
479
480 /**
481 * Get post ID based on document.
482 *
483 * @since 1.1.0
484 */
485 public static function get_current_post_id() {
486 if ( isset( Elementor::$instance->documents ) && ! empty( Elementor::$instance->documents->get_current() ) ) {
487 return Elementor::$instance->documents->get_current()->get_main_id();
488 }
489
490 return get_the_ID();
491 }
492
493 /**
494 * Express checkout html.
495 *
496 * @return void
497 * @since 1.1.0
498 */
499 public function express_checkout_html() {
500 ob_start();
501 ?>
502 <section class="sellkit-checkout-widget-express-checkout sellkit-checkout-express-checkout-step-1">
503 <fieldset class="express-box sellkit-checkout-widget-divider">
504 <legend class="sellkit-express-checkout-legend heading">
505 <?php echo __( 'Express Checkout', 'sellkit' ); ?>
506 </legend>
507 <div class="express-methods">
508 <?php do_action( 'sellkit-checkout-widget-express-methods' ); ?>
509 </div>
510 </fieldset>
511 </section>
512 <section class="sellkit-checkout-widget-express-checkout sellkit-checkout-express-checkout-step-2">
513 <fieldset id="sellkit-checkout-or-divider" class="divider-box sellkit-checkout-widget-divider">
514 <legend class="heading"><?php echo __( 'OR', 'sellkit' ); ?></legend>
515 </fieldset>
516 </section>
517 <?php
518 $express = ob_get_clean();
519 $express = apply_filters( 'sellkit-checkout-widget-express-checkout', $express );
520 echo $express;
521 }
522
523 /**
524 * Apply custom Messages.
525 *
526 * @return void
527 * @since 1.1.0
528 */
529 private function custom_messages() {
530 add_filter( 'sellkit_core/widgets/checkout/custom_message/already_have_account_text', function() {
531 return $this->settings['already_have_account_text'];
532 } );
533
534 add_filter( 'sellkit_core/widgets/checkout/custom_message/login_toggle_text', function() {
535 return $this->settings['login_toggle_text'];
536 } );
537
538 add_action( 'sellkit_core/widgets/checkout/custom_message/create_website_account', function() {
539 $site = get_bloginfo( 'name' );
540 $text = $this->settings['create_website_account'];
541
542 echo str_replace( '{{website}}', $site, $text );
543 } );
544
545 add_filter( 'sellkit_core/widgets/checkout/custom_message/secure_transaction_text', function() {
546 return $this->settings['secure_transaction_text'];
547 } );
548
549 add_filter( 'sellkit_core/widgets/checkout/custom_message/select_address_text', function() {
550 return $this->settings['select_address_text'];
551 } );
552
553 add_filter( 'sellkit-checkout-place-order-btn-text', function( $default ) {
554 if ( ! empty( $this->settings['place_order_btn_txt'] ) ) {
555 return $this->settings['place_order_btn_txt'];
556 }
557
558 return $default;
559 }, 10 );
560 }
561
562 /**
563 * Enable or disable some of checkout sections.
564 *
565 * @param array $settings widget settings.
566 * @param array $posted_data checkout form data.
567 * @return void
568 * @since 1.1.0
569 */
570 public static function enable_disable_sections( $settings, $posted_data = [] ) {
571 if ( Elementor::$instance->editor->is_edit_mode() || array_key_exists( 'sellkit-elementor-editor-mode', $posted_data ) ) {
572
573 add_action( 'sellkit-checkout-after-coupon-form-ajax', [ Helper::instance(), 'editor_mode_extra_js' ] );
574
575 // Let coupon form to be viewable in editor mode.
576 add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
577 Global_Hooks::instance()->coupon_form( $settings );
578 } );
579
580 // Improve UI issue in the editor for the order summary input.
581 add_filter( 'woocommerce_cart_item_class', function( $default ) use ( $settings, $posted_data ) {
582 $default = str_replace( 'product-title-one-row', '', $default );
583
584 if (
585 ! array_key_exists( 'show_cart_edit', $settings ) ||
586 array_key_exists( 'bundle-products-force', $posted_data ) ||
587 empty( $settings['show_cart_edit'] )
588 ) {
589 $default .= ' product-title-one-row';
590 }
591
592 return $default;
593 } );
594
595 return;
596 }
597
598 if ( wp_doing_ajax() ) {
599 // Hide order item images.
600 add_filter( 'sellkit/includes/elementor/modules/checkout/product-image', function( $img_html ) use ( $settings ) {
601 if ( ! array_key_exists( 'order_summary_show_image', $settings ) ) {
602 return $img_html;
603 }
604
605 return '';
606 } );
607
608 add_filter( 'sellkit-checkout-cart-item', function( $cart_item ) use ( $settings ) {
609 if ( ! array_key_exists( 'show_cart_items', $settings ) ) {
610 return $cart_item;
611 }
612
613 return '';
614 } );
615
616 // Disable order item quantity input.
617 if ( array_key_exists( 'show_cart_edit', $settings ) || array_key_exists( 'bundle-products-force', $posted_data ) ) {
618 add_filter( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
619
620 add_filter( 'woocommerce_cart_item_class', function( $default ) {
621 return $default . ' product-title-one-row';
622 } );
623 }
624
625 add_filter( 'woocommerce_shipping_package_name', function() {
626 return '<h4 id="shipping_header_title" class="shipping-method-header heading">' . esc_html__( 'Shipping Methods', 'sellkit' ) . '</h4>';
627 } );
628
629 add_filter( 'woocommerce_cart_ready_to_calc_shipping', function() use ( $settings ) {
630 if ( ! array_key_exists( 'show_shipping_method', $settings ) ) {
631 return true;
632 }
633
634 return false;
635 } );
636
637 add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
638 Global_Hooks::instance()->coupon_form( $settings );
639 } );
640
641 return;
642 }
643
644 // Normal mode when page is loading.
645 add_filter( 'woocommerce_cart_ready_to_calc_shipping', function() use ( $settings ) {
646 if ( 'yes' === $settings['show_shipping_method'] ) {
647 return true;
648 }
649
650 return false;
651 }, 10 );
652
653 add_filter( 'sellkit/includes/elementor/modules/checkout/product-image', function( $img_html ) use ( $settings ) {
654 if ( 'yes' === $settings['order_summary_show_image'] ) {
655 return $img_html;
656 }
657
658 return '';
659 } );
660
661 add_filter( 'sellkit-checkout-cart-item', function( $cart_item ) use ( $settings ) {
662 if ( 'yes' === $settings['show_cart_items'] ) {
663 return $cart_item;
664 }
665
666 return '';
667 }, 10 );
668
669 // Disable order item quantity input.
670 if ( array_key_exists( 'show_cart_edit', $settings ) ) {
671 add_filter( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
672
673 add_filter( 'sellkit/includes/elementor/modules/checkout/quanity/class', function( $classes ) {
674 return $classes .= ' sellkit-checkout-widget-d-block';
675 } );
676 }
677
678 if ( array_key_exists( 'show_cart_edit', $settings ) || array_key_exists( 'bundle-products-force', $posted_data ) ) {
679 add_filter( 'woocommerce_cart_item_class', function( $default ) {
680 return $default . ' product-title-one-row';
681 } );
682 }
683
684 add_filter( 'sellkit-checkout-place-order-btn-text', function( $place_order_btn ) {
685 if ( ! empty( $settings['place_order_btn_txt'] ) ) {
686 return $settings['place_order_btn_txt'];
687 }
688
689 return $place_order_btn;
690 }, 10 );
691
692 add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
693 if ( 'yes' !== $settings['show_coupon_field'] ) {
694 return;
695 }
696
697 self::coupon_form( $settings );
698 } );
699 }
700
701 /**
702 * Inject google autocomplete address script if enabled for shipping/billing section.
703 *
704 * @return void
705 * @since 1.1.0
706 */
707 public function inject_google_autocomplete() {
708 if ( ! sellkit()->has_pro ) {
709 return;
710 }
711
712 \Sellkit_Elementor_Checkout_Pro_Module::checkout_google_autocomplete_address( $this->settings );
713 }
714
715 /**
716 * Place custom coupon form in checkout order-review based design.
717 * ! we have used same form in Global_Hooks. but this one for first load of page.
718 *
719 * @param array $settings widget settings.
720 * @see sellkit_Core\Raven\Modules\Checkout\Classes\Global_Hooks::coupon_form.
721 * @return void
722 * @since 1.1.0
723 */
724 public static function coupon_form( $settings ) {
725 echo '<tr class="coupon-form border-none"><td colspan="2">';
726 self::form( $settings );
727 echo '</td></tr>';
728 }
729
730 /**
731 * New custom coupon form HTML.
732 *
733 * @param array $settings widget settings.
734 * @return void
735 * @since 1.1.0
736 */
737 public static function form( $settings ) {
738 $class = 'sellkit-custom-coupon-form-d-none';
739 if ( ! array_key_exists( 'coupon_field_type', $settings ) || 'normal' === $settings['coupon_field_type'] ) {
740 $class = 'sellkit-normal-coupon-form';
741 }
742 ?>
743 <?php if ( array_key_exists( 'coupon_field_type', $settings ) && 'collapsible' === $settings['coupon_field_type'] ) : ?>
744 <span id="copoun_toggle" class="copoun_toggle sellkit-coupon-toggle sellkit-checkout-widget-links">
745 <?php echo __( 'Have a coupon? Click here to enter your code', 'sellkit' ); ?>
746 </span>
747 <?php $class .= ' sellkit-checkout-collapsible'; ?>
748 <?php Helper::instance()->editor_mode_extra_js(); ?>
749 <?php endif; ?>
750 <div class="sellkit-custom-coupon-form <?php echo $class; ?>">
751 <p class="jx-form-row-first">
752 <input class="jx-coupon" type="text" placeholder="<?php esc_attr_e( 'Enter Promo code', 'sellkit' ); ?>" />
753 </p>
754
755 <p class="jx-form-row-last">
756 <span class="sellkit-checkout-widget-secondary-button jx-apply-coupon sellkit-apply-coupon" >
757 <?php esc_html_e( 'Apply', 'sellkit' ); ?>
758 </span>
759 </p>
760 </div>
761 <?php
762 }
763
764 /**
765 * Insert bundled product.
766 *
767 * @since 1.1.0
768 * @return void
769 */
770 public function bundled_products() {
771 global $wp_query;
772 $query = $wp_query->query_vars;
773 $option = 'default';
774
775 if ( ! array_key_exists( 'funnel_product_settings', $query ) ) {
776 return;
777 }
778
779 if ( ! empty( $query['funnel_product_settings'] ) ) {
780 $option = $query['funnel_product_settings'];
781 }
782
783 if ( 'default' === $option ) {
784 return;
785 }
786
787 add_action( 'sellkit_checkout_required_hidden_fields', function() use ( $option ) {
788 ?>
789 <input type="hidden" name="sellkit-bundle-products" value="<?php echo esc_attr( $option ); ?>" >
790 <?php
791 }, 10 );
792
793 self::bundle_product_action( $option );
794 }
795
796 /**
797 * Display bundle products.
798 *
799 * @param string $option bundle products type.
800 * @since 1.1.0
801 * return void
802 */
803 public static function bundle_product_action( $option ) {
804 add_filter( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
805
806 if ( 'force-products' === $option || Elementor::$instance->editor->is_edit_mode() ) {
807 return;
808 }
809
810 $fields_type = 'radio';
811 $products = WC()->cart->get_cart();
812 $default = null;
813 $default_q = null;
814 $readonly = 'readonly';
815
816 if ( 'allow-buyers' === $option ) {
817 $fields_type = 'checkbox';
818 $readonly = '';
819 }
820
821 ob_start();
822 ?>
823 <section class="sellkit-checkout-bundled-products">
824 <div class="sellkit-checkout-bundled-inner-wrap">
825 <span class="sellkit-checkout-bundled-header heading"><?php echo esc_html__( 'Your Products', 'sellkit' ); ?></span>
826 <table class="sellkit-checkout-bundled-products-table">
827 <thead>
828 <tr class="sellkit-checkout-bundled-products-head-row">
829 <th class="sellkit-head-row-title"><?php echo esc_html__( 'Product', 'sellkit' ); ?></th>
830 <th class="sellkit-head-row-quantity"><?php echo esc_html__( 'Quantity', 'sellkit' ); ?></th>
831 <th class="sellkit-head-row-price"><?php echo esc_html__( 'Price', 'sellkit' ); ?></th>
832 </tr>
833 </thead>
834 <tbody>
835 <?php foreach ( $products as $cart_item_key => $cart_item ) : ?>
836 <?php
837 if ( in_array( $cart_item_key, self::$in_cart, true ) ) {
838 continue;
839 }
840
841 $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
842 $default = $_product->get_id();
843 $default_q = $cart_item['quantity'];
844 $unique_id = 'bundle-unique-id-' . $default;
845
846 self::$in_cart[ array_key_last( $products ) ] = $cart_item_key;
847 ?>
848 <tr class="sellkit-checkout-bundled-products-item">
849 <td class="sellkit-checkout-bundled-title">
850 <input
851 type="<?php echo esc_attr( $fields_type ); ?>"
852 value="<?php echo esc_attr( $_product->get_id() ); ?>"
853 name="sellkit-checkout-bundle-item"
854 class="sellkit-checkout-bundle-item"
855 id="<?php echo esc_attr( $unique_id ); ?>"
856 <?php echo ( array_key_last( $products ) === $cart_item_key ) ? 'checked' : ''; ?>
857 >
858 <label for="<?php echo esc_attr( $unique_id ); ?>">
859 <?php echo $_product->get_name(); ?>
860 </label>
861 </td>
862 <td class="sellkit-checkout-bundled-quantity">
863 <input type="number" <?php echo esc_attr( $readonly ); ?> min="1" value="<?php echo esc_attr( $cart_item['quantity'] ); ?>" data-id="<?php echo esc_attr( $cart_item_key ); ?>" class="sellkit-checkout-single-bundle-item-quantity" >
864 </td>
865 <td class="sellkit-checkout-bundled-price">
866 <?php echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); ?>
867 </td>
868 <tr>
869 <?php if ( array_key_last( $products ) !== $cart_item_key ) : ?>
870 <tr>
871 <td colspan="3" class="sellkit-checkout-bundled-spacer-row">
872 <hr>
873 </td>
874 </tr>
875 <?php endif; ?>
876 <?php endforeach; ?>
877 </tbody>
878 </table>
879 </div>
880 </section>
881 <?php
882 $bundle_html = ob_get_clean();
883
884 add_action( 'sellkit-bundled-products-position', function() use ( $bundle_html ) {
885 echo $bundle_html;
886 } );
887
888 global $wp_query;
889
890 $query = $wp_query->query_vars;
891 $reset_cart = 'true';
892
893 if ( isset( $query['funnel_reset_cart'] ) && array_key_exists( 'funnel_reset_cart', $query ) ) {
894 $reset_cart = $query['funnel_reset_cart'];
895 }
896
897 if ( ! wp_doing_ajax() ) {
898 if ( 'true' === $reset_cart ) {
899 WC()->cart->empty_cart();
900 }
901
902 if ( null !== $default ) {
903 if ( ! array_key_exists( $default, self::$in_cart ) && 'true' !== $reset_cart ) {
904 WC()->cart->add_to_cart( $default, $default_q );
905 }
906
907 if ( 'true' === $reset_cart ) {
908 WC()->cart->add_to_cart( $default, $default_q );
909 }
910
911 if ( ! WC()->cart->needs_shipping() ) {
912 wp_add_inline_script( 'sellkit-initialize-widgets', 'var sellkitCheckoutShipping = true', 'before' );
913 }
914 }
915
916 Global_Hooks::make_changes_after_cart_item_edit( get_the_id() );
917 }
918 }
919
920 /**
921 * Trigger to activate order bumb or not for this checkout.
922 *
923 * @param array $data checkout form data.
924 * @since 1.1.0
925 * @return void
926 */
927 public static function order_bump( $data = [] ) {
928 if ( 'woocommerce_before_checkout_form' === current_action() ) {
929 self::order_bump_frontend_manager();
930 return;
931 }
932 }
933
934 /**
935 * Order bump to manage frontend.
936 *
937 * @since 1.1.0
938 * @return void
939 */
940 private static function order_bump_frontend_manager() {
941 global $wp_query;
942 $query = $wp_query->query_vars;
943
944 if ( ! array_key_exists( 'bump_data', $query ) ) {
945 return;
946 }
947
948 $bumps = $query['bump_data'];
949
950 foreach ( $bumps as $bump ) {
951 if ( ! array_key_exists( 'products', $bump['data'] ) ) {
952 continue;
953 }
954
955 $design = $bump['data']['design'];
956 $products = $bump['data']['products'];
957
958 add_action( $design['sellkit_funnels_bump_position'], function() use ( $design, $products ) {
959 self::bump_html( $design, $products );
960 }, 5 );
961 }
962 }
963
964 /**
965 * Order bump html.
966 *
967 * @param array $design design properties.
968 * @param array $products products details.
969 * @since 1.1.0
970 * @return void
971 */
972 public static function bump_html( $design, $products ) {
973 $list = $products['list'];
974 $ids = [];
975
976 foreach ( $list as $id => $details ) {
977 $ids[] = $id;
978 $qty = $details['quantity'];
979 $discount = $details['discount'];
980 $type = $details['discountType'];
981 }
982
983 $ids_string = implode( '|', $ids );
984 $product = wc_get_product( $ids_string );
985 $qty = ( empty( $qty ) ) ? 1 : $qty;
986
987 if ( empty( $product ) || ! $product->get_id() ) {
988 return;
989 }
990
991 $unique_id = 'bump-title-' . $ids_string;
992
993 $class = '';
994 if (
995 'sellkit-checkout-before-order-summary' === current_action() ||
996 'sellkit-checkout-after-order-summary' === current_action()
997 ) {
998 $class = 'sellkit-bump-review-order';
999 }
1000 ?>
1001 <div class="sellkit-checkout-step-bump-wrapper <?php echo esc_attr( $class ); ?>" >
1002 <div class="sellkit-checkout-bump-order-header">
1003 <div class="sellkit-bump-order-left-header">
1004 <img src="<?php echo esc_attr( sellkit()->plugin_assets_url() . 'img/right-arrow.svg' ); ?>" >
1005 <input
1006 type="checkbox"
1007 value="<?php echo esc_attr( $ids_string ); ?>"
1008 class="sellkit-checkout-bump-order-products"
1009 data-qty="<?php echo esc_attr( $qty ); ?>"
1010 name="sellkit_bump_data_<?php echo esc_attr( $ids_string ); ?>"
1011 id="<?php echo esc_html( $unique_id ); ?>"
1012 >
1013 <label
1014 for="<?php echo esc_attr( $unique_id ); ?>"
1015 class="sellkit-checkout-order-bump-title"
1016 >
1017 <?php echo esc_html( $design['sellkit_funnels_bump_checkbox_label'] ); ?>
1018 </label>
1019 </div>
1020 <div class="sellkit-bump-order-right-header">
1021 <span class="sellkit-checkout-order-bump-price">
1022 <?php
1023 $discounted_price = Helper::calculate_discount( (int) $ids_string, $type, $discount );
1024 $sale_price = $product->get_sale_price();
1025 $regular_price = $product->get_regular_price();
1026 $main_price = ( strpos( $type, 'sale' ) !== false ) ? $sale_price : $regular_price;
1027
1028 if ( floatval( $main_price ) > floatval( $discounted_price ) ) {
1029 ?>
1030 <del aria-hidden="true">
1031 <span class="woocommerce-Price-amount amount">
1032 <bdi>
1033 <span class="woocommerce-Price-currencySymbol">
1034 <?php echo wc_price( $main_price ); ?>
1035 </span>
1036 </bdi>
1037 </span>
1038 </del>
1039 <bdi class="bump-price-bolded"><?php echo wc_price( $discounted_price ); ?></bdi>
1040 <?php
1041 } else {
1042 ?>
1043 <bdi><?php echo wc_price( $main_price ); ?></bdi>
1044 <?php
1045 }
1046 ?>
1047 </span>
1048 </div>
1049 </div>
1050 <div class="sellkit-checkout-bump-order-body" >
1051 <div class="sellkit-bump-order-left-body">
1052 <?php $image = wp_get_attachment_image_src( $product->get_image_id() ); ?>
1053 <?php if ( 'true' === $design['sellkit_funnels_bump_product_image'] && ! empty( $image[0] ) ) : ?>
1054 <img src="<?php echo esc_attr( $image[0] ); ?>" >
1055 <?php endif; ?>
1056 </div>
1057 <div class="sellkit-bump-order-right-body">
1058 <div class="sellkit-bump-order-description">
1059 <?php echo $design['sellkit_content']; ?>
1060 </div>
1061 </div>
1062 </div>
1063 </div>
1064 <?php
1065 }
1066
1067 /**
1068 * Checks if funnel includes upsell step.
1069 *
1070 * @param string $goal customize return value.
1071 * @param int $ajax_id upsell id through ajax.
1072 * @since 1.6.2
1073 */
1074 public static function is_funnel_includes_upsell( $goal = 'is_upsell', $ajax_id = null ) {
1075 $id = get_queried_object_id();
1076
1077 if ( wp_doing_ajax() ) {
1078 $id = $ajax_id;
1079 }
1080
1081 $step_data = get_post_meta( $id, 'step_data', true );
1082 $include_upsell = false;
1083 $upsell_steps = [];
1084 $upsell_ids = [];
1085 $global_checkout_id = get_option( Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
1086
1087 // We are in default WooCommerce checkout page.
1088 if ( empty( $step_data ) && $global_checkout_id > 0 && 'publish' === get_post_status( $global_checkout_id ) ) {
1089 $steps = get_post_meta( $global_checkout_id, 'nodes', true );
1090 $checkout_id = 0;
1091
1092 foreach ( $steps as $step ) {
1093 $step['type'] = (array) $step['type'];
1094
1095 if ( 'checkout' === $step['type']['key'] ) {
1096 $checkout_id = $step['page_id'];
1097 }
1098 }
1099
1100 $step_data = get_post_meta( $checkout_id, 'step_data', true );
1101 }
1102
1103 if ( empty( $step_data ) ) {
1104 return $include_upsell;
1105 }
1106
1107 $funnel_id = intval( $step_data['funnel_id'] );
1108 $funnel_data = get_post_meta( $funnel_id, 'nodes', true );
1109
1110 if ( empty( $funnel_data ) ) {
1111 return $include_upsell;
1112 }
1113
1114 $popups = [ 'downsell', 'upsell' ];
1115
1116 foreach ( $funnel_data as $step ) {
1117 $step['type'] = (array) $step['type'];
1118
1119 if ( in_array( $step['type']['key'], $popups, true ) ) {
1120 $include_upsell = true;
1121 $upsell_steps[] = $step;
1122 $upsell_ids[] = $step['page_id'];
1123 }
1124 }
1125
1126 if ( 'get_ids' === $goal ) {
1127 return $upsell_ids;
1128 }
1129
1130 // Send data to the filter that keeps steps data to prepare popup.
1131 add_filter( 'sellkit_upsell_steps_popup_information', function( $default ) use ( $upsell_steps ) {
1132 if ( ! empty( $upsell_steps ) ) {
1133 return $upsell_steps;
1134 }
1135
1136 return $default;
1137 } );
1138
1139 return $include_upsell;
1140 }
1141
1142 /**
1143 * Add an extra hidden field to checkout form if funnel includes upsell step.
1144 *
1145 * @since 1.6.2
1146 */
1147 public function add_trigger_field_for_upsell_steps() {
1148 $include_upsell = self::is_funnel_includes_upsell();
1149
1150 if ( false === $include_upsell ) {
1151 return;
1152 }
1153
1154 echo '<input type="hidden" id="sellkit_funnel_has_upsell" value="upsell" >';
1155 echo '<input type="hidden" id="sellkit_funnel_popup_step_id" value="0" >';
1156 }
1157
1158 /**
1159 * Display sellkit upsell steps as popup.
1160 *
1161 * @since 1.6.2
1162 */
1163 public function sellkit_funnel_display_upsell_as_popup() {
1164 $upsell_data = apply_filters( 'sellkit_upsell_steps_popup_information', [] );
1165 $i = 1;
1166
1167 if ( empty( $upsell_data ) ) {
1168 return;
1169 }
1170
1171 foreach ( $upsell_data as $data ) {
1172 $id = 'sellkit_funnel_upsell_popup_' . $i;
1173 $class = 'sellkit_funnel_upsell_popup sellkit_funnel_upsell_popup_' . $data['page_id'];
1174 ?>
1175 <div class="<?php echo esc_attr( $class ); ?>" id="<?php echo esc_attr( $id ); ?>">
1176 <?php echo Elementor::instance()->frontend->get_builder_content_for_display( $data['page_id'] ); ?>
1177 <input type="hidden" class="identify" value="<?php echo esc_attr( $data['page_id'] ); ?>" >
1178 </div>
1179 <?php
1180 $i++;
1181 }
1182 }
1183 }
1184