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

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