PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.5
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.5
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.5, at includes/elementor/modules/checkout/classes/local-hooks.php

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