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

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