PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / trunk
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster vtrunk
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 / funnel / class.php

class.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster trunk, at includes/funnel/class.php

462 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Elementor\Utils as ElementorUtils;
4 use Sellkit\Global_Checkout\Checkout as Global_Checkout;
5
6 defined( 'ABSPATH' ) || die();
7
8 /**
9 * Class Sellkit_Funnel
10 *
11 * @since 1.1.0
12 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
13 * @SuppressWarnings(PHPMD.NPathComplexity)
14 */
15 class Sellkit_Funnel {
16
17 /**
18 * Funnel Id.
19 *
20 * @var integer Funnel Id.
21 * @since 1.1.0
22 */
23 public $funnel_id;
24
25 /**
26 * Next step data.
27 *
28 * @var array $next_step_data.
29 * @since 1.1.0
30 */
31 public $next_step_data;
32
33 /**
34 * Next no step data.
35 *
36 * @var array $next_no_step_data.
37 * @since 1.5.0
38 */
39 public $next_no_step_data;
40
41 /**
42 * Ending node step data.
43 *
44 * @var array $end_node_step_data.
45 * @since 1.5.0
46 */
47 public $end_node_step_data;
48
49 /**
50 * The current step data.
51 *
52 * @var array $current_step_data.
53 * @since 1.1.0
54 */
55 public $current_step_data;
56
57 /**
58 * The class instance.
59 *
60 * @var Object Class instance.
61 * @since 1.1.0
62 */
63 public static $instance = null;
64
65 /**
66 * Sellkit_Funnel constructor.
67 *
68 * @param string $page_id Page Id.
69 * @since 1.1.0
70 */
71 public function __construct( $page_id = null ) {
72 $this->order_customization();
73
74 if ( null === $page_id ) {
75 $page_id = self::get_page_id();
76 }
77
78 if ( empty( $page_id ) ) {
79 $page_id = $this->get_funnel_step_homepage_id();
80
81 if ( empty( $page_id ) ) {
82 return;
83 }
84 }
85
86 $this->set_funnel_data( $page_id );
87
88 if ( empty( $this->funnel_id ) ) {
89 return;
90 }
91
92 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_funnel_frontend_script' ] );
93 }
94
95 /**
96 * Gets funnel step homepage id.
97 *
98 * @since 1.9.6
99 * @return int|bool
100 */
101 private function get_funnel_step_homepage_id() {
102 if ( is_admin() ) {
103 return false;
104 }
105
106 $front_page_id = get_option( 'page_on_front', 0 );
107
108 if ( empty( $front_page_id ) ) {
109 return false;
110 }
111
112 $scheme = ( ! empty( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] ) ? 'https' : 'http';
113
114 $host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ); // phpcs:ignore
115 $request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore
116
117 $url = esc_url( "$scheme://$host$request_uri" );
118 $home_url = get_home_url() . '/';
119
120 if ( '/' !== substr( $url, -1 ) ) {
121 $home_url = get_home_url();
122 }
123
124 if ( $home_url !== $url ) {
125 return false;
126 }
127
128 $post_id = url_to_postid( $url );
129
130 if ( empty( $post_id ) ) {
131 return false;
132 }
133
134 $post_type = get_post_type( $post_id );
135 $status = Funnel_Homepage::check_funnel_step_status( $post_id, $post_type );
136
137 if ( $status && intval( $post_id ) === intval( $front_page_id ) ) {
138 return $front_page_id;
139 }
140
141 return false;
142 }
143
144 /**
145 * Class Instance.
146 *
147 * @since 1.1.0
148 * @return Sellkit_Funnel|null
149 */
150 public static function get_instance() {
151 if ( null === self::$instance ) {
152 self::$instance = new self();
153 }
154
155 return self::$instance;
156 }
157
158 /**
159 * Gets funnel if the page is step otherwise do nothing.
160 *
161 * @since 1.1.0
162 * @return void
163 * @param string $page_id Page id.
164 */
165 public function set_funnel_data( $page_id ) {
166 $this->current_step_data = get_post_meta( $page_id, 'step_data', true );
167
168 if ( empty( $this->current_step_data['funnel_id'] ) ) {
169 return;
170 }
171
172 $is_nofollow = sellkit_get_option( 'disallow_funnels_from_search_engine' );
173
174 if ( true === (bool) $is_nofollow ) {
175 add_action( 'wp_head', function () {
176 echo '<meta name="robots" content="noindex" />';
177 } );
178 }
179
180 $funnel_data = (array) get_post_meta( $this->current_step_data['funnel_id'], 'nodes', true );
181 $is_flowchart = true;
182
183 if ( empty( $funnel_data ) ) {
184 $funnel_data = get_post_meta( $this->current_step_data['funnel_id'], 'sellkit_steps', true );
185 $is_flowchart = false;
186 }
187
188 $this->funnel_id = $this->current_step_data['funnel_id'];
189
190 if ( false === $is_flowchart ) {
191 $this->next_step_data = ! empty( $funnel_data[ $this->current_step_data['number'] + 1 ] ) ? $funnel_data[ $this->current_step_data['number'] + 1 ] : false;
192 }
193
194 if (
195 true === $is_flowchart &&
196 ! empty( $this->current_step_data['targets'] ) &&
197 ! empty( $funnel_data[ $this->current_step_data['targets'][0]['nodeId'] ] )
198 ) {
199 $this->next_step_data = 'none' !== $funnel_data[ $this->current_step_data['targets'][0]['nodeId'] ] ? $funnel_data[ $this->current_step_data['targets'][0]['nodeId'] ] : false;
200 }
201
202 if ( true === $is_flowchart && ! empty( $this->current_step_data['targets'][1]['nodeId'] ) ) {
203 $this->next_no_step_data = ! empty( $funnel_data[ $this->current_step_data['targets'][1]['nodeId'] ] ) ? $funnel_data[ $this->current_step_data['targets'][1]['nodeId'] ] : false;
204 }
205
206 $end_node_id = $this->get_end_node_id( $funnel_data );
207
208 if ( ! empty( $end_node_id ) ) {
209 $this->end_node_step_data = $funnel_data[ $end_node_id ];
210 }
211 }
212
213 /**
214 * Enqueue funnel frontend scripts.
215 *
216 * @since 1.1.0
217 * @return void
218 */
219 public function enqueue_funnel_frontend_script() {
220 $suffix = defined( 'WP_DEBUG' ) && WP_DEBUG ? '' : '.min';
221
222 wp_enqueue_script(
223 'funnel-frontend',
224 sellkit()->plugin_url() . 'assets/dist/js/funnel-frontend' . $suffix . '.js',
225 [ 'jquery', 'wp-util' ],
226 sellkit()->version(),
227 true
228 );
229
230 wp_localize_script( 'funnel-frontend', 'funnel', $this->get_localize_data() );
231
232 if ( empty( sellkit_get_option( 'google_analytics' ) ) && empty( sellkit_get_option( 'facebook_pixel' ) ) ) {
233 return;
234 }
235
236 wp_enqueue_script(
237 'funnel-settings-variables',
238 sellkit()->plugin_url() . 'assets/dist/js/funnel-settings-variables' . $suffix . '.js',
239 [ 'jquery', 'wp-util' ],
240 sellkit()->version(),
241 true
242 );
243 }
244
245 /**
246 * Get localize data.
247 *
248 * @return array
249 */
250 public function get_localize_data() {
251 if ( ! empty( $this->next_step_data['page_id'] ) ) {
252 $next_step_link = ! empty( $this->next_step_data['page_id'] ) ? get_the_permalink( $this->next_step_data['page_id'] ) : '';
253
254 if ( empty( $next_step_link ) ) {
255 $next_step_link = add_query_arg( [
256 'post_type' => Sellkit_Admin_Steps::SELLKIT_STEP_POST_TYPE,
257 'p' => $this->next_step_data['page_id'],
258 ], site_url() );
259 }
260 }
261
262 return [
263 'currentStep' => $this->current_step_data,
264 'nextStep' => $this->next_step_data,
265 'nextStepLink' => ! empty( $next_step_link ) ? $next_step_link : '',
266 'siteUrl' => site_url(),
267 ];
268 }
269
270 /**
271 * Gets page id.
272 *
273 * @since 1.1.0
274 * @return false|int
275 * @SuppressWarnings(PHPMD.NPathComplexity)
276 */
277 public static function get_page_id() {
278 $page_id = sellkit_htmlspecialchars( INPUT_POST, 'sellkit_current_page_id' );
279 $structure = get_option( 'permalink_structure' );
280 $post_data = filter_input( INPUT_POST, 'post_data', FILTER_DEFAULT );
281
282 if ( ! empty( $post_data ) ) {
283 parse_str( $post_data, $post_data );
284
285 if ( ! empty( $post_data['sellkit_current_page_id'] ) ) {
286 return (int) $post_data['sellkit_current_page_id'];
287 }
288 }
289
290 if ( is_numeric( $page_id ) ) {
291 return $page_id;
292 }
293
294 // Stripe Express Checkout (and similar express flows) POST to wc-ajax=checkout
295 // without SellKit's hidden fields. Fall back to the checkout step id stored in
296 // the WC session when the user first landed on the funnel checkout page so we
297 // can still resolve the funnel context on the server side. The stored value is
298 // only trusted when recent, to avoid reusing stale ids from older visits.
299 if ( wp_doing_ajax() && function_exists( 'WC' ) && is_object( WC()->session ) ) {
300 $session_page_id = (int) WC()->session->get( 'sellkit_current_page_id' );
301 $session_time = (int) WC()->session->get( 'sellkit_current_page_id_time' );
302
303 if ( $session_page_id > 0 && ( time() - $session_time ) < HOUR_IN_SECONDS ) {
304 return $session_page_id;
305 }
306 }
307
308 $current_url = esc_url_raw( remove_query_arg( 'order-key' ) );
309
310 if ( ! empty( $structure ) ) {
311 $current_url = self::get_base_url( $current_url );
312 }
313
314 if ( empty( strpos( $current_url, Funnel_Redirect::$step_base ) ) && ! empty( get_option( 'rewrite_rules' ) ) ) {
315 return false;
316 }
317
318 if ( empty( $current_url ) ) {
319 return false;
320 }
321
322 $page_id = sellkit_htmlspecialchars( INPUT_GET, 'p' );
323 $page_slug = sellkit_htmlspecialchars( INPUT_GET, 'sellkit_step' );
324 $page = get_page_by_path( basename( untrailingslashit( $current_url ) ), null, 'sellkit_step' );
325
326 if ( empty( $page ) && ! empty( $page_slug ) ) {
327 $page = get_page_by_path( sanitize_text_field( $page_slug ), null, 'sellkit_step' );
328 }
329
330 if ( empty( $page ) && empty( $page_id ) ) {
331 return false;
332 }
333
334 if ( ! empty( $page_id ) && 'sellkit_step' === get_post_type( $page_id ) ) {
335 return $page_id;
336 }
337
338 if ( empty( $page->ID ) ) {
339 return false;
340 }
341
342 return $page->ID;
343 }
344
345 /**
346 * Gets main url.
347 *
348 * @since 1.5.4
349 * @param string $url Url.
350 * @return string
351 */
352 private static function get_base_url( $url ) {
353 $parsed = wp_parse_url( $url );
354
355 if ( empty( $parsed['query'] ) ) {
356 return $url;
357 }
358
359 return explode( '?', $url )[0];
360 }
361
362 /**
363 * Customization order pages in admin area.
364 *
365 * @since 1.1.0
366 */
367 public function order_customization() {
368 if ( ! is_admin() ) {
369 return;
370 }
371
372 // Orders filters.
373 add_filter( 'woocommerce_get_formatted_order_total', [ $this, 'add_order_type' ], 10, 2 );
374 add_action( 'woocommerce_admin_order_data_after_order_details', [ $this, 'show_linked_orders' ], 10, 1 );
375 }
376
377 /**
378 * Adding order type to order list table.
379 *
380 * @since 1.1.0
381 * @return string
382 * @param string $formatted_total formatted total price.
383 * @param object $order Order object.
384 */
385 public function add_order_type( $formatted_total, $order ) {
386 $screen = get_current_screen();
387 $is_upsell = $order->get_meta( 'sellkit_main_order_id' );
388
389 if ( ! $screen || 'edit' !== $screen->base || 'shop_order' !== $screen->post_type ) {
390 return $formatted_total;
391 }
392
393 if ( is_numeric( $is_upsell ) ) {
394 $formatted_total .= '<div><span>' . esc_html__( 'Sellkit Upsell', 'sellkit' ) . '</span></div>';
395 }
396
397 return $formatted_total;
398 }
399
400 /**
401 * Show the related between orders.
402 *
403 * @since 1.1.0
404 * @param object $order Order object.
405 */
406 public function show_linked_orders( $order ) {
407 $upsell_order_id = $order->get_meta( 'sellkit_upsell_order_id' );
408 $main_order_id = $order->get_meta( 'sellkit_main_order_id' );
409
410 if ( is_numeric( $main_order_id ) ) {
411 $parent_order = wc_get_order( $main_order_id );
412 $order_number = $parent_order->get_order_number();
413 $parent_order_html = sprintf( '<p class="form-field form-field-wide" style= "margin-top: 20px;"><strong>%s: </strong>', esc_html__( 'SellKit Parent Order', 'sellkit' ) );
414 $parent_order_html .= sprintf( '<span style= "display: block;"><a href="%1s"><strong>#%2s</strong></a></span>', get_edit_post_link( $main_order_id ), esc_attr( $order_number ) );
415 $parent_order_html .= '</p>';
416
417 echo wp_kses_post( $parent_order_html );
418 }
419
420 if ( is_numeric( $upsell_order_id ) ) {
421 $upsell_order = wc_get_order( $upsell_order_id );
422 $order_number = $upsell_order->get_order_number();
423 $upsell_order_html = sprintf( '<p class="form-field form-field-wide" style= "margin-top: 20px;"><strong>%s: </strong>', esc_html__( 'SellKit Upsell Order', 'sellkit' ) );
424 $upsell_order_html .= sprintf( '<span style= "display: block;"><a href="%1s"><strong>#%2s</strong></a></span>', get_edit_post_link( $upsell_order_id ), esc_attr( $order_number ) );
425 $upsell_order_html .= '</p>';
426
427 echo wp_kses_post( $upsell_order_html );
428 }
429 }
430
431 /**
432 * Gets end node.
433 *
434 * @since 1.5.0
435 * @param array $funnels Funnels array.
436 */
437 public function get_end_node_id( $funnels ) {
438 $funnels = (array) $funnels;
439
440 foreach ( $funnels as $key => $funnel ) {
441 if ( ! empty( $funnel['origin_node'] ) && 'last-node' === $funnel['origin_node'] ) {
442 return $key;
443 }
444 }
445
446 return false;
447 }
448 }
449
450 if ( ! function_exists( 'sellkit_funnel' ) ) {
451 /**
452 * Sellkit funnel wrapper.
453 *
454 * @since 1.1.0
455 * @return Object|Sellkit_Funnel|null
456 */
457 function sellkit_funnel() {
458 return Sellkit_Funnel::get_instance();
459 }
460 }
461
462