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/global-checkout/class.php +444 -45 1.7.92.7.0 View file →
@@ -1,12 +1,11 @@
1 1 <?php
2 2
3 3 namespace Sellkit\Global_Checkout;
4 4
5 -defined( 'ABSPATH' ) || exit;
5 +defined('ABSPATH') || exit;
6 6
7 7 use Elementor\Plugin as Elementor;
8 -use Sellkit\Elementor\Modules\Checkout\Classes\Helper;
9 8 use Sellkit\Funnel\Steps\Checkout as CheckoutStep;
10 9
11 10 /**
12 11 * Checkout.
@@ -12,99 +11,499 @@
12 11 * Checkout.
13 12 *
14 13 * @SuppressWarnings(PHPMD.NPathComplexity)
15 14 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
15 + * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
16 + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
16 17 * @since 1.7.4
17 18 */
18 -class Checkout {
19 - const SELLKIT_GLOBAL_CHECKOUT_OPTION = 'sellkit_global_checkout_id';
19 +class Checkout
20 +{
21 + const SELLKIT_GLOBAL_CHECKOUT_OPTION = 'sellkit_global_checkout_id';
22 + const SELLKIT_GLOBAL_CHECKOUT_HEADER_FOOTER_TEMPLATE = 'sellkit_global_checkout_header_footer_templates';
20 23
21 24 /**
25 + * Current post ID.
26 + *
27 + * @since 2.3.0
28 + * @var null|integer
29 + */
30 + public $post_id = null;
31 +
32 + /**
22 33 * Construct.
23 34 *
24 35 * @since 1.7.4
25 36 */
26 - public function __construct() {
27 - add_action( 'wp', [ $this, 'init_sellkit_global_checkout' ] );
37 + public function __construct()
38 + {
39 + if (! function_exists('sellkit_pro') || ! sellkit_pro()->is_active_sellkit_pro) {
40 + return;
41 + }
42 +
43 + add_action('wp', [$this, 'checkout_block_bump_query_var']);
44 + add_action('wp', [$this, 'init_sellkit_global_checkout']);
45 + add_action('wp_ajax_handle_sellkit_global_checkout_ajax_requests', [$this, 'handle_ajax_requests']);
28 46 }
29 47
30 48 /**
49 + * Order bump for sellkit checkout block
50 + *
51 + * @since 2.3.0
52 + */
53 + public function checkout_block_bump_query_var()
54 + {
55 + $global_checkout_id = get_option(self::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0);
56 +
57 + $steps = get_post_meta($global_checkout_id, 'nodes', true);
58 + $bump_data = [];
59 +
60 + if (! is_array($steps)) {
61 + return;
62 + }
63 +
64 + foreach ($steps as $step) {
65 + $step['type'] = (array) $step['type'];
66 +
67 + if ('checkout' === $step['type']['key']) {
68 + $bump_data = ! empty($step['bump']) ? $step['bump'] : [];
69 + }
70 + }
71 +
72 + if (! empty($bump_data)) {
73 + $bump_data = $this->get_valid_bumps($bump_data);
74 +
75 + set_query_var('bump_data', $bump_data);
76 + }
77 + }
78 +
79 + /**
31 80 * Init sellkit global checkout.
32 81 *
33 82 * @since 1.7.4
34 83 */
35 - public function init_sellkit_global_checkout() {
36 - $global_checkout_id = get_option( self::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
84 + public function init_sellkit_global_checkout()
85 + {
86 + $global_checkout_id = get_option(self::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0);
37 87
38 88 if (
39 89 0 === $global_checkout_id ||
40 - 'publish' !== get_post_status( (int) $global_checkout_id ) ||
41 - ! is_checkout() ||
42 - ! defined( 'ELEMENTOR_VERSION' )
90 + 'publish' !== get_post_status((int) $global_checkout_id) ||
91 + (function_exists('is_checkout') && ! is_checkout())
43 92 ) {
44 93 return;
45 94 }
46 95
47 - $steps = get_post_meta( $global_checkout_id, 'nodes', true );
96 + $steps = get_post_meta($global_checkout_id, 'nodes', true);
48 97 $checkout_id = 0;
49 98 $bump_data = [];
50 99 $optimization_data = '';
100 + $funnel_id = 0;
51 101
52 - foreach ( $steps as $step ) {
102 + if (! is_array($steps)) {
103 + return;
104 + }
105 +
106 + foreach ($steps as $step) {
53 107 $step['type'] = (array) $step['type'];
54 108
55 - if ( 'checkout' === $step['type']['key'] ) {
56 - $checkout_id = $step['page_id'];
57 - $bump_data = ! empty( $step['bump'] ) ? $step['bump'] : [];
58 - $optimization_data = ! empty( $step['data']['optimization'] ) ? $step['data']['optimization'] : '';
109 + if ('checkout' === $step['type']['key']) {
110 + $checkout_id = apply_filters('wpml_object_id', $step['page_id'], 'sellkit_step', true); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
111 + $bump_data = ! empty($step['bump']) ? $step['bump'] : [];
112 + $optimization_data = ! empty($step['data']['optimization']) ? $step['data']['optimization'] : '';
113 + $funnel_id = isset($step['funnel_id']) ? $step['funnel_id'] : 0;
59 114 }
60 115 }
61 116
62 - if ( 0 === $checkout_id ) {
117 + $checkout_steps = get_post_meta($checkout_id, 'step_data', true);
118 +
119 + if (empty($funnel_id) && ! empty($checkout_steps)) {
120 + $funnel_id = isset($checkout_steps['funnel_id']) ? $checkout_steps['funnel_id'] : 0;
121 + }
122 +
123 + if (0 === $checkout_id) {
63 124 return;
64 125 }
65 126
66 127 // Remove previous content.
67 - remove_all_filters( 'the_content' );
128 + remove_all_filters('the_content');
68 129
69 - // Set the page content.
70 - add_filter( 'the_content', function() use ( $checkout_id ) {
71 - ob_Start();
72 - echo Elementor::instance()->frontend->get_builder_content_for_display( (int) $checkout_id, true );
73 - return ob_get_clean();
74 - }, 5 );
130 + if (defined('ELEMENTOR_VERSION') && 'elementor' === sellkit()->page_builder()) {
131 + // Set the page content.
132 + add_filter('the_content', function () use ($checkout_id) {
133 + ob_Start();
134 + $content = Elementor::instance()->frontend->get_builder_content_for_display((int) $checkout_id, true);
135 + echo do_shortcode($content);
136 + return ob_get_clean();
137 + }, 5);
75 138
76 - // Set sellkit canvas templates as the page template.
77 - add_action( 'template_redirect', function() {
78 - sellkit()->load_files( [
79 - 'templates/canvas'
80 - ] );
139 + // Set sellkit canvas templates as the page template.
140 + add_action('template_redirect', function () {
141 + // Let WooCommerce clear the cart on a successful order-received visit before
142 + // our exit prevents its priority-20 hook from ever running.
143 + if (function_exists('wc_clear_cart_after_payment')) {
144 + wc_clear_cart_after_payment();
145 + }
81 146
82 - exit;
83 - } );
147 + sellkit()->load_files([
148 + 'templates/canvas'
149 + ]);
84 150
85 - add_filter( 'sellkit_global_checkout_activated', function() {
151 + exit;
152 + });
153 + }
154 +
155 + if ('gutenberg' === sellkit()->page_builder()) {
156 + $this->load_checkout_block_frontend();
157 +
158 + $checkout_post = get_post($checkout_id);
159 +
160 + global $post;
161 + $post = $checkout_post; // phpcs:ignore:WordPress.WP.GlobalVariablesOverride.OverrideProhibited
162 + setup_postdata($post);
163 +
164 + $content = do_blocks($post->post_content);
165 +
166 + $content = apply_filters('the_content', $content); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core filter.
167 +
168 + add_filter('the_content', function () use ($content) {
169 + ob_Start();
170 +
171 + echo $content; // phpcs:ignore:WordPress.Security.EscapeOutput.OutputNotEscaped
172 +
173 + return ob_get_clean();
174 + }, 5);
175 +
176 + // Set sellkit canvas templates as the page template.
177 + add_action('template_redirect', function () {
178 + // Let WooCommerce clear the cart on a successful order-received visit before
179 + // our exit prevents its priority-20 hook from ever running.
180 + if (function_exists('wc_clear_cart_after_payment')) {
181 + wc_clear_cart_after_payment();
182 + }
183 +
184 + sellkit()->load_files([
185 + 'templates/default-canvas'
186 + ]);
187 +
188 + exit;
189 + });
190 + }
191 +
192 + add_filter('sellkit_global_checkout_activated', function () {
86 193 return true;
87 - } );
194 + });
88 195
89 - add_action( 'sellkit_checkout_required_hidden_fields', function() use ( $checkout_id ) {
90 - ?>
91 - <input type="hidden" name="sellkit_current_page_id" value="<?php echo esc_attr( $checkout_id ); ?>" >
92 - <input type="hidden" name="sellkit_global_checkout_id" value="<?php echo esc_attr( $checkout_id ); ?>" >
93 - <?php
94 - } );
196 + add_action('sellkit_checkout_required_hidden_fields', function () use ($checkout_id) {
197 +?>
198 + <input type="hidden" name="sellkit_current_page_id" value="<?php echo esc_attr($checkout_id); ?>">
199 + <input type="hidden" name="sellkit_global_checkout_id" value="<?php echo esc_attr($checkout_id); ?>">
200 +<?php
201 + });
95 202
96 - if ( ! empty( $bump_data ) ) {
97 - set_query_var( 'bump_data', $bump_data );
203 + if (! empty($bump_data)) {
204 + $bump_data = $this->get_valid_bumps($bump_data);
205 +
206 + set_query_var('bump_data', $bump_data);
98 207 }
99 208
100 - if ( ! empty( $optimization_data ) && CheckoutStep::apply_coupon_validation( $optimization_data ) ) {
101 - foreach ( $optimization_data['auto_apply_coupons'] as $auto_apply_coupon ) {
102 - wc()->cart->add_discount( get_the_title( $auto_apply_coupon['value'] ) );
209 + if (! empty($optimization_data) && CheckoutStep::apply_coupon_validation($optimization_data)) {
210 + foreach ($optimization_data['auto_apply_coupons'] as $auto_apply_coupon) {
211 + wc()->cart->add_discount(get_the_title($auto_apply_coupon['value']));
103 212 }
104 213
105 214 wc_clear_notices();
106 215 }
216 +
217 + $header_footer_templates = get_post_meta(intval($funnel_id), self::SELLKIT_GLOBAL_CHECKOUT_HEADER_FOOTER_TEMPLATE, true);
218 +
219 + if (empty($header_footer_templates) || ! is_array($header_footer_templates)) {
220 + $header_footer_templates = maybe_unserialize($header_footer_templates);
221 + }
222 +
223 + if (empty($header_footer_templates) || ! is_array($header_footer_templates)) {
224 + $header_footer_templates = [];
225 + }
226 +
227 + add_filter('sellkit_global_checkout_header_applied_id', function ($default) use ($header_footer_templates) {
228 + if (
229 + array_key_exists('header', $header_footer_templates) &&
230 + array_key_exists('value', $header_footer_templates['header'])
231 + ) {
232 + return $header_footer_templates['header']['value'];
233 + }
234 +
235 + return $default;
236 + });
237 +
238 + add_filter('sellkit_global_checkout_footer_applied_id', function ($default) use ($header_footer_templates) {
239 + if (
240 + array_key_exists('footer', $header_footer_templates) &&
241 + array_key_exists('value', $header_footer_templates['footer'])
242 + ) {
243 + return $header_footer_templates['footer']['value'];
244 + }
245 +
246 + return $default;
247 + });
248 + }
249 +
250 + /**
251 + * Load checkout block on frontend.
252 + *
253 + * @since 2.3.0
254 + */
255 + public function load_checkout_block_frontend()
256 + {
257 + global $post;
258 +
259 + if (empty($post->post_content)) {
260 + return;
261 + }
262 +
263 + $this->post_id = $post->ID;
264 +
265 + $block = 'blocks/checkout';
266 +
267 + $block_data = explode('/', $block);
268 + $block_name = $block_data[1];
269 +
270 + $class_name = str_replace('-', ' ', $block_name);
271 + $class_name = str_replace(' ', '_', ucwords($class_name));
272 + $class_name = "Sellkit\blocks\Render\\{$class_name}";
273 + $class_path = 'block-editor/' . $block . '/index';
274 +
275 + sellkit()->load_files([
276 + $class_path,
277 + ]);
278 +
279 + $new_class = new $class_name($this->post_id);
280 +
281 + if (! \WP_Block_Type_Registry::get_instance()->is_registered("sellkit-blocks/{$block_name}")) {
282 + $this->register_inner_blocks_by_parent($new_class);
283 + $new_class->register_block_meta();
284 + }
285 + }
286 +
287 + /**
288 + * Register inner blocks by parent.
289 + *
290 + * @param Object $parent_class Parent class name.
291 + * @since 2.3.0
292 + * @return void
293 + */
294 + private function register_inner_blocks_by_parent($parent_class)
295 + {
296 + if (! method_exists($parent_class, 'has_inner_blocks')) {
297 + return;
298 + }
299 +
300 + $inner_blocks = $parent_class->get_inner_block();
301 +
302 + sellkit()->load_files($inner_blocks);
303 +
304 + foreach ($inner_blocks as $key => $value) {
305 + if (isset($this->inner_blocks["blocks/{$key}"])) {
306 + continue;
307 + }
308 +
309 + $inner_block_class = 'Sellkit\Blocks\Inner_Block\\' . str_replace('-', '_', ucwords($key));
310 +
311 + if (! class_exists($inner_block_class)) {
312 + continue;
313 + }
314 +
315 + $inner_block_instance = new $inner_block_class($this->post_id);
316 + if (! \WP_Block_Type_Registry::get_instance()->is_registered("sellkit-inner-blocks/{$key}")) {
317 + $inner_block_instance->register_block_meta();
318 + }
319 + }
320 + }
321 +
322 + /**
323 + * Handle ajax requests.
324 + *
325 + * @since 1.8.9
326 + */
327 + public function handle_ajax_requests()
328 + {
329 + check_ajax_referer('sellkit', 'nonce');
330 +
331 + if (! current_user_can('manage_options')) {
332 + wp_send_json_error('You do not have access to this section.', 'sellkit');
333 + }
334 +
335 + $action = filter_var($_REQUEST['sub_action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); // phpcs:ignore
336 +
337 + call_user_func([$this, $action]);
338 + }
339 +
340 + /**
341 + * Get header & footer design templates for Global checkout.
342 + *
343 + * @since 1.8.9
344 + */
345 + private function get_design_templates()
346 + {
347 + $type = filter_input(INPUT_GET, 'template_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
348 + $search = filter_input(INPUT_GET, 'input_value', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
349 +
350 + $args = [
351 + 'post_type' => 'elementor_library',
352 + 'post_status' => ['private', 'publish'],
353 + 's' => $search,
354 + 'posts_per_page' => -1,
355 + 'meta_query' => [ //phpcs:ignore
356 + [
357 + 'key' => '_elementor_template_type',
358 + 'value' => $type,
359 + ],
360 + ],
361 + ];
362 +
363 + $posts = new \WP_Query($args);
364 +
365 + $values = [
366 + [
367 + 'label' => esc_html__('Default', 'sellkit'),
368 + 'value' => 'default',
369 + ]
370 + ];
371 +
372 + if ($posts->post_count > 0) {
373 + foreach ($posts->posts as $post) {
374 + $values[] = [
375 + 'label' => $post->post_title,
376 + 'value' => $post->ID,
377 + ];
378 + }
379 +
380 + wp_send_json_success($values);
381 + }
382 +
383 + wp_send_json_success($values);
384 + }
385 +
386 + /**
387 + * Save selected header/footer template id ofr the global checkout.
388 + *
389 + * @since 1.8.9
390 + */
391 + private function save_selected_template_id()
392 + {
393 + $funnel_id = filter_input(INPUT_POST, 'funnel', FILTER_SANITIZE_NUMBER_INT);
394 + $template_id = filter_input(INPUT_POST, 'template_id', FILTER_DEFAULT, FILTER_FORCE_ARRAY);
395 + $template_type = filter_input(INPUT_POST, 'template', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
396 + $data = get_post_meta($funnel_id, self::SELLKIT_GLOBAL_CHECKOUT_HEADER_FOOTER_TEMPLATE, true);
397 +
398 + if (empty($data) || ! is_array($data)) {
399 + $data = [];
400 + }
401 +
402 + if (isset($data[$template_type])) {
403 + unset($data[$template_type]);
404 + }
405 +
406 + $data[$template_type] = [
407 + 'label' => (empty($template_id['label'])) ? esc_html__('Search...', 'sellkit') : $template_id['label'],
408 + 'value' => (empty($template_id['value'])) ? 0 : $template_id['value'],
409 + ];
410 +
411 + update_post_meta($funnel_id, self::SELLKIT_GLOBAL_CHECKOUT_HEADER_FOOTER_TEMPLATE, $data);
412 +
413 + wp_send_json_success($data);
414 + }
415 +
416 + /**
417 + * Get selected header/footer id.
418 + *
419 + * @since 1.8.9
420 + */
421 + private function get_selected_template_id()
422 + {
423 + $funnel_id = filter_input(INPUT_POST, 'funnel', FILTER_SANITIZE_NUMBER_INT);
424 + $data = get_post_meta($funnel_id, self::SELLKIT_GLOBAL_CHECKOUT_HEADER_FOOTER_TEMPLATE, true);
425 + $template = filter_input(INPUT_POST, 'template', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
426 +
427 + if (empty($data)) {
428 + $data = [
429 + 'header' => [
430 + 'label' => esc_html__('Search...', 'sellkit'),
431 + 'value' => 0,
432 + ],
433 + 'footer' => [
434 + 'label' => esc_html__('Search...', 'sellkit'),
435 + 'value' => 0,
436 + ],
437 + ];
438 + }
439 +
440 + if (! is_array($data)) {
441 + $data = maybe_unserialize($data);
442 + }
443 +
444 + wp_send_json_success($data);
445 + }
446 +
447 + /**
448 + * Create new Elementor template.
449 + *
450 + * @since 1.8.9
451 + */
452 + private function create_new_template()
453 + {
454 + $template = filter_input(INPUT_POST, 'template', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
455 + $post_title = filter_input(INPUT_POST, 'post_title', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
456 +
457 + $args = [
458 + 'post_type' => 'elementor_library',
459 + 'post_title' => $post_title,
460 + 'post_status' => 'publish',
461 + 'meta_input' => [
462 + '_elementor_template_type' => $template,
463 + '_elementor_edit_mode' => 'builder',
464 + 'jx-layout-type' => $template,
465 + '_wp_page_template' => 'full-width.php',
466 + ],
467 + ];
468 +
469 + $id = wp_insert_post($args);
470 +
471 + if (intval($id) > 0) {
472 + $editor_url = Elementor::$instance->documents->get($id)->get_edit_url();
473 +
474 + wp_send_json_success([
475 + 'label' => $post_title,
476 + 'value' => $id,
477 + 'url' => $editor_url,
478 + ]);
479 + }
480 +
481 + wp_send_json_error();
482 + }
483 +
484 + /**
485 + * Checks all bumps and return data.
486 + *
487 + * @since 1.8.1
488 + * @param array $bump_data Bump data.
489 + * @return array
490 + */
491 + public function get_valid_bumps($bump_data)
492 + {
493 + $valid_bumps = [];
494 +
495 + foreach ($bump_data as $bump) {
496 + $conditions = ! empty($bump['data']['conditions']) ? $bump['data']['conditions'] : '';
497 +
498 + if (! empty($conditions) && empty(sellkit_conditions_validation($conditions))) {
499 + continue;
500 + }
501 +
502 + $valid_bumps[] = $bump;
503 + }
504 +
505 + return $valid_bumps;
107 506 }
108 507 }
109 508
110 509 new Checkout();