PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.7.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.7.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
← All changes | includes/elementor/modules/checkout/widgets/checkout.php +113 -28 1.2.22.7.0 View file →
@@ -4,12 +4,14 @@
4 4
5 5 use Sellkit\Elementor\Modules\Checkout\Widgets\{ Settings_Controls, Style_Controls };
6 6 use Sellkit\Elementor\Modules\Checkout\Classes\{Global_Hooks, Local_Hooks, Multi_Step, Helper };
7 7 use Elementor\Plugin as Elementor;
8 +use Sellkit\Global_Checkout\Checkout;
8 9
9 10 /**
10 11 * Checkout class.
11 12 *
13 + * @SuppressWarnings(PHPMD.NPathComplexity)
12 14 * @since 1.1.0
13 15 */
14 16 class Sellkit_Elementor_Checkout_Widget extends Sellkit_Elementor_Base_Widget {
15 17
@@ -43,8 +45,12 @@
43 45 public function is_reload_preview_required() {
44 46 return true;
45 47 }
46 48
49 + public function get_style_depends() {
50 + return [ 'font-awesome' ];
51 + }
52 +
47 53 /**
48 54 * Apply multi-step functionality.
49 55 *
50 56 * @return void
@@ -64,28 +70,123 @@
64 70 new Local_Hooks( $settings, $this );
65 71 }
66 72
67 73 protected function register_controls() {
68 - require __DIR__ . '/settings-controls.php';
69 - require __DIR__ . '/style-controls.php';
74 + require_once __DIR__ . '/settings-controls.php';
75 + require_once __DIR__ . '/style-controls.php';
70 76
71 77 new Settings_Controls();
72 78 new Style_Controls();
73 79 }
74 80
81 + /**
82 + * @SuppressWarnings(PHPMD.CyclomaticComplexity)
83 + */
75 84 protected function render() {
85 + if ( ! function_exists( 'wc' ) ) {
86 + return;
87 + }
88 +
89 + $settings = $this->get_settings_for_display();
90 + $checkout_id = get_the_ID();
91 + $step_data = get_post_meta( $checkout_id, 'step_data', true );
92 + $global_checkout = apply_filters( 'sellkit_global_checkout_activated', false );
93 + $global_checkout_id = get_option( Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
94 + $current_funnel_id = ( ! empty( $step_data ) ) ? $step_data['funnel_id'] : -1;
95 + $empty_cart_msg = $settings['empty_cart']; // phpcs:ignore
96 +
97 + $order_key = ! empty( sellkit_htmlspecialchars( INPUT_GET, 'order-key' ) ) ? sellkit_htmlspecialchars( INPUT_GET, 'order-key' ) : sellkit_htmlspecialchars( INPUT_GET, 'key' );
98 +
99 + if (
100 + $global_checkout &&
101 + is_wc_endpoint_url( 'order-received' ) &&
102 + ! empty( $order_key )
103 + ) {
104 + $order_id = wc_get_order_id_by_order_key( $order_key );
105 +
106 + if ( empty( $order_id ) ) {
107 + /* translators: %s: Empty cart message. */
108 + echo sprintf(
109 + '<div class="elementor-alert elementor-alert-info sellkit-checkout-empty-cart-message-box">%s</div>',
110 + esc_html( $empty_cart_msg )
111 + );
112 +
113 + return;
114 + }
115 +
116 + // Valid order on the order-received endpoint: render WooCommerce's thank-you
117 + // template instead of the checkout form. SellKit's redirect_after_purchase
118 + // already runs earlier on template_redirect, but express-checkout flows that
119 + // skip SellKit's hidden fields (so no sellkit_funnel_next_step_data meta is
120 + // saved) fall through to here and must not show a filled-in checkout form.
121 + echo '<div class="woocommerce">';
122 + wc_get_template( 'checkout/thankyou.php', [ 'order' => wc_get_order( $order_id ) ] );
123 + echo '</div>';
124 +
125 + return;
126 + }
127 +
128 + if (
129 + $global_checkout &&
130 + 0 === WC()->cart->get_cart_contents_count() &&
131 + ! is_wc_endpoint_url( 'order-received' )
132 + ) {
133 + /* translators: %s: Empty cart message. */
134 + echo sprintf(
135 + '<div class="elementor-alert elementor-alert-info sellkit-checkout-empty-cart-message-box">%s</div>',
136 + esc_html( $empty_cart_msg )
137 + );
138 +
139 + return;
140 + }
141 +
142 + if (
143 + false === $global_checkout &&
144 + intval( $global_checkout_id ) !== intval( $current_funnel_id ) &&
145 + is_array( $step_data ) &&
146 + (
147 + ! array_key_exists( 'data', $step_data ) ||
148 + ! array_key_exists( 'list', $step_data['data']['products'] )
149 + )
150 + ) {
151 + /* translators: %s: Empty cart message. */
152 + echo sprintf(
153 + '<div class="elementor-alert elementor-alert-info sellkit-checkout-empty-cart-message-box">%s</div>',
154 + esc_html( $empty_cart_msg )
155 + );
156 + return;
157 + }
158 +
76 159 add_filter( 'woocommerce_is_checkout', function() {
77 160 return true;
78 161 }, 10 );
79 162
80 - $settings = $this->get_settings_for_display();
163 + $this->clear_sellkit_checkout_custom_hooks( $settings );
81 164
82 - $this->clear_sellkit_checkout_custom_hooks( $settings );
165 + $shipping_destination = get_option( 'woocommerce_ship_to_destination', true );
83 166
84 167 if ( 'multi-step' === $settings['layout-type'] ) {
85 168 $this->apply_multi_step_design( $settings );
169 +
170 + remove_action( 'woocommerce_checkout_billing', [ WC()->checkout(), 'checkout_form_billing' ] );
171 +
172 + if ( 'billing_only' === $shipping_destination ) {
173 + remove_action( 'woocommerce_checkout_shipping', [ WC()->checkout(), 'checkout_form_shipping' ] );
174 + add_action( 'woocommerce_checkout_shipping', [ WC()->checkout(), 'checkout_form_billing' ], 10 );
175 + }
86 176 }
87 177
178 + if ( 'one-page' === $settings['layout-type'] ) {
179 + remove_action( 'woocommerce_checkout_billing', [ WC()->checkout(), 'checkout_form_billing' ] );
180 +
181 + if ( 'billing_only' === $shipping_destination ) {
182 + remove_action( 'woocommerce_checkout_shipping', [ WC()->checkout(), 'checkout_form_shipping' ] );
183 + add_action( 'woocommerce_checkout_shipping', [ WC()->checkout(), 'checkout_form_billing' ], 10 );
184 + } else {
185 + add_action( 'woocommerce_checkout_order_review', [ WC()->checkout(), 'checkout_form_billing' ], 10 );
186 + }
187 + }
188 +
88 189 $this->apply_local_hooks( $settings );
89 190
90 191 if ( 'one-page' === $settings['layout-type'] ) {
91 192 $this->add_render_attribute(
@@ -102,12 +203,11 @@
102 203 }
103 204
104 205 if ( Elementor::$instance->editor->is_edit_mode() ) {
105 206 $current_user = wp_get_current_user()->ID;
106 - wp_set_current_user( 0 );
107 207 }
108 208
109 - echo '<div ' . $this->get_render_attribute_string( 'wrapper' ) . ' >';
209 + echo '<div ' . $this->get_render_attribute_string( 'wrapper' ) . ' >'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Elementor-generated attributes.
110 210 echo do_shortcode( '[woocommerce_checkout]' );
111 211 echo do_shortcode( '[sellkit_checkout_widget_simulated]' );
112 212 echo '</div>';
113 213
@@ -141,19 +241,18 @@
141 241 remove_all_actions( 'sellkit-checkout-multistep-sidebar-begins' );
142 242 remove_all_actions( 'sellkit-checkout-multistep-sidebar-ends' );
143 243 remove_all_actions( 'sellkit-checkout-widget-breadcrumb-desktop' );
144 244 remove_all_actions( 'sellkit-checkout-widget-breadcrumb-mobile' );
145 -
146 245 remove_all_actions( 'sellkit_checkout_shipping_fields' );
147 246 remove_all_actions( 'sellkit_checkout_billing_fields' );
148 -
149 247 remove_all_actions( 'sellkit_checkout_after_shipping_section' );
150 -
151 248 remove_all_actions( 'sellkit_checkout_one_page_express_methods' );
152 -
153 249 remove_all_actions( 'sellkit-checkout-widget-custom-coupon-form' );
250 + remove_all_filters( 'woocommerce_locate_template' );
154 251
155 - remove_all_filters( 'woocommerce_locate_template' );
252 + // !TODO We can use this method to move / remove billing and shipping fields.
253 + add_action( 'woocommerce_checkout_shipping', [ WC()->checkout(), 'checkout_form_shipping' ] ); // ! added for theme integration. Astra.
254 + remove_action( 'woocommerce_checkout_billing', [ WC()->checkout(), 'checkout_form_shipping' ] ); // ! added for theme integration. Astra.
156 255
157 256 if ( Elementor::$instance->editor->is_edit_mode() ) {
158 257 $this->editor_mode( $settings );
159 258 }
@@ -165,27 +264,13 @@
165 264 * @since 1.1.0
166 265 * @return void
167 266 */
168 267 private function editor_mode( $settings ) {
169 - // Trick to render widget content after clicking its wrapper in editor mode.
170 - add_action( 'wp_footer', function() {
171 - ?>
172 - <script>
173 - elementor.hooks.addAction( 'panel/open_editor/widget', function( panel, model, view ) {
174 - let $element = jQuery( 'input[data-setting="place_order_btn_txt"' );
175 - let value = $element.val();
176 - $element.val( '' );
177 - $element.val( value ).trigger( 'input' );
178 - } );
179 - </script>
180 - <?php
181 - }, 999 );
182 -
183 268 add_action( 'sellkit_checkout_required_hidden_fields', function() {
184 269 echo '<input type="hidden" name="sellkit-elementor-editor-mode" value="true" >';
185 270 } );
186 271
187 - // Some frontend Hooks don't work in editor mode.
272 + // Some frontend Hooks doesn't work in editor mode.
188 273 // So we just hide them in editor mode, BUT we use real hooks in frontend.
189 274 // Purpose is to increase user experience in editor mode.
190 275
191 276 if ( 'yes' !== $settings['show_cart_items'] ) {
@@ -194,9 +279,9 @@
194 279
195 280 if ( 'yes' !== $settings['show_cart_edit'] ) {
196 281 $this->hide_items_in_editor( 'sellkit-one-page-checkout-product-qty' );
197 282
198 - add_action( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
283 + add_filter( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
199 284 }
200 285
201 286 if ( 'yes' !== $settings['show_shipping_method'] ) {
202 287 $this->hide_items_in_editor( 'sellkit-one-page-shipping-methods' );
@@ -218,8 +303,8 @@
218 303 private function hide_items_in_editor( $class ) {
219 304 echo sprintf(
220 305 /** Translators : %s : class name */
221 306 '<style>.%s { display:none !important; }</style>',
222 - $class
307 + esc_attr( $class )
223 308 );
224 309 }
225 310 }