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

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