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

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