PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.2
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.2
2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 1.8.1 All 42 releases
sellkit / includes / elementor / modules / checkout / classes / local-hooks.php

local-hooks.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster 1.7.2, at includes/elementor/modules/checkout/classes/local-hooks.php

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