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

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