PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
← All changes | classes/class-cartflows-frontend.php +691 -65 1.1.0.1 → 3.3.0 View file →
@@ -4,8 +4,12 @@
4 4 *
5 5 * @package CartFlows
6 6 */
7 7
8 +// Exit if accessed directly.
9 +if ( ! defined( 'ABSPATH' ) ) {
10 + exit;
11 +}
8 12 /**
9 13 * Class Cartflows_Frontend.
10 14 */
11 15 class Cartflows_Frontend {
@@ -21,9 +25,9 @@
21 25 * Initiator
22 26 */
23 27 public static function get_instance() {
24 28 if ( ! isset( self::$instance ) ) {
25 - self::$instance = new self;
29 + self::$instance = new self();
26 30 }
27 31 return self::$instance;
28 32 }
29 33
@@ -34,8 +38,10 @@
34 38
35 39 /* Set / Destroy Flow Sessions. Set data */
36 40 add_action( 'wp', array( $this, 'init_actions' ), 1 );
37 41
42 + add_action( 'init', array( $this, 'debug_data_setting_actions' ) );
43 + add_action( 'init', array( $this, 'setup_optin_checkout_filter' ) );
38 44 /* Enqueue global required scripts */
39 45 add_action( 'wp', array( $this, 'wp_actions' ), 55 );
40 46
41 47 /* Modify the checkout order received url to go thank you page in our flow */
@@ -44,13 +50,14 @@
44 50
45 51 /**
46 52 * Redirect to thank page if upsell not exists
47 53 *
48 - * @param string $order_recieve_url url.
54 + * @param string $order_receive_url url.
49 55 * @param object $order order object.
56 + * @return string $order_receive_url next step URL.
50 57 * @since 1.0.0
51 58 */
52 - function redirect_to_thankyou_page( $order_recieve_url, $order ) {
59 + public function redirect_to_thankyou_page( $order_receive_url, $order ) {
53 60
54 61 /* Only for thank you page */
55 62 wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ );
56 63 wcf()->logger->log( 'Only for thank you page' );
@@ -61,12 +68,12 @@
61 68
62 69 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
63 70
64 71 if ( ! $checkout_id ) {
65 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
72 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
66 73 }
67 74 } else {
68 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
75 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
69 76 }
70 77
71 78 wcf()->logger->log( 'Checkout ID : ' . $checkout_id );
72 79
@@ -72,20 +79,25 @@
72 79
73 80 if ( $checkout_id ) {
74 81
75 82 $thankyou_step_id = wcf()->flow->get_thankyou_page_id( $order );
83 + $thankyou_step_id = apply_filters( 'cartflows_checkout_next_step_id', $thankyou_step_id, $order, $checkout_id );
76 84
77 85 if ( $thankyou_step_id ) {
78 86
79 - $order_recieve_url = get_permalink( $thankyou_step_id );
80 -
81 - $order_recieve_url = add_query_arg(
87 + $query_param = wcf()->utils->may_be_append_query_string(
88 + // Default query string args.
82 89 array(
90 + 'wcf-key' => $order->get_order_key(),
83 91 'wcf-order' => $order->get_id(),
84 - 'wcf-key' => $order->get_order_key(),
85 - ),
86 - $order_recieve_url
92 + )
87 93 );
94 +
95 + $order_receive_url = add_query_arg(
96 + $query_param,
97 + get_permalink( $thankyou_step_id )
98 + );
99 +
88 100 }
89 101 }
90 102 }
91 103
@@ -90,9 +102,9 @@
90 102 }
91 103
92 104 wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ );
93 105
94 - return $order_recieve_url;
106 + return $order_receive_url;
95 107 }
96 108
97 109 /**
98 110 * Cancel and redirect to checkout
@@ -99,9 +111,9 @@
99 111 *
100 112 * @param string $return_url url.
101 113 * @since 1.0.0
102 114 */
103 - function redirect_to_checkout_on_cancel( $return_url ) {
115 + public function redirect_to_checkout_on_cancel( $return_url ) {
104 116
105 117 if ( _is_wcf_doing_checkout_ajax() ) {
106 118
107 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
@@ -106,12 +118,12 @@
106 118
107 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
108 120
109 121 if ( ! $checkout_id ) {
110 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
122 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
111 123 }
112 124 } else {
113 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
125 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
114 126 }
115 127
116 128 if ( $checkout_id ) {
117 129
@@ -132,43 +144,56 @@
132 144 * Remove theme styles.
133 145 *
134 146 * @since 1.0.0
135 147 */
136 - function remove_theme_styles() {
148 + public function remove_theme_styles() {
137 149
150 + if ( Cartflows_Compatibility::get_instance()->is_compatibility_theme_enabled() ) {
151 + return;
152 + }
153 +
154 + $page_template = Cartflows_Helper::get_current_page_template();
155 +
156 + if ( ! _wcf_supported_template( $page_template ) ) {
157 + return;
158 + }
159 +
138 160 // get all styles data.
139 161 global $wp_styles;
140 162 global $wp_scripts;
141 163
142 - $get_stylesheet = get_stylesheet();
143 - $get_template = get_template();
164 + $get_stylesheet = 'themes/' . get_stylesheet() . '/';
165 + $get_template = 'themes/' . get_template() . '/';
144 166
145 - if ( Cartflows_Compatibility::get_instance()->is_divi_enabled() ) {
146 - return;
147 - }
167 + $remove_styles = apply_filters( 'cartflows_remove_theme_styles', true );
148 168
149 - $get_stylesheet = 'themes/' . get_stylesheet();
150 - $get_template = 'themes/' . get_template();
169 + if ( $remove_styles ) {
151 170
152 - // loop over all of the registered scripts..
153 - foreach ( $wp_styles->registered as $handle => $data ) {
171 + // loop over all of the registered scripts..
172 + foreach ( $wp_styles->registered as $handle => $data ) {
154 173
155 - if ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) {
174 + if ( ! empty( $data->src ) && ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) ) {
156 175
157 - // remove it.
158 - wp_deregister_style( $handle );
159 - wp_dequeue_style( $handle );
176 + // remove it.
177 + wp_deregister_style( $handle );
178 + wp_dequeue_style( $handle );
179 + }
160 180 }
161 181 }
162 182
163 - // loop over all of the registered scripts.
164 - foreach ( $wp_scripts->registered as $handle => $data ) {
183 + $remove_scripts = apply_filters( 'cartflows_remove_theme_scripts', true );
165 184
166 - if ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) {
185 + if ( $remove_scripts ) {
167 186
168 - // remove it.
169 - wp_deregister_script( $handle );
170 - wp_dequeue_script( $handle );
187 + // loop over all of the registered scripts.
188 + foreach ( $wp_scripts->registered as $handle => $data ) {
189 +
190 + if ( ! empty( $data->src ) && ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) ) {
191 +
192 + // remove it.
193 + wp_deregister_script( $handle );
194 + wp_dequeue_script( $handle );
195 + }
171 196 }
172 197 }
173 198 }
174 199
@@ -178,9 +203,9 @@
178 203 * @param array $woo_styles new styles array.
179 204 * @since 1.0.0
180 205 * @return array.
181 206 */
182 - function woo_default_css( $woo_styles ) {
207 + public function woo_default_css( $woo_styles ) {
183 208
184 209 $woo_styles = array(
185 210 'woocommerce-layout' => array(
186 211 'src' => plugins_url( 'assets/css/woocommerce-layout.css', WC_PLUGIN_FILE ),
@@ -212,11 +237,22 @@
212 237 * Init Actions.
213 238 *
214 239 * @since 1.0.0
215 240 */
216 - function init_actions() {
241 + public function init_actions() {
217 242
218 - $this->set_flow_session();
243 + if ( wcf()->utils->is_step_post_type() ) {
244 +
245 + global $post;
246 +
247 + $GLOBALS['wcf_step'] = wcf_get_step( $post->ID );
248 +
249 + do_action( 'cartflows_wp', $post->ID );
250 +
251 + $this->set_flow_session();
252 +
253 + $this->delete_checkout_cookies();
254 + }
219 255 }
220 256
221 257 /**
222 258 * Set flow session.
@@ -222,34 +258,83 @@
222 258 * Set flow session.
223 259 *
224 260 * @since 1.0.0
225 261 */
226 - function set_flow_session() {
262 + public function set_flow_session() {
227 263
228 - if ( wcf()->utils->is_step_post_type() ) {
264 + global $wp;
229 265
230 - add_action( 'wp_head', array( $this, 'noindex_flow' ) );
266 + add_action( 'wp_head', array( $this, 'noindex_flow' ) );
231 267
232 - wcf()->utils->do_not_cache();
268 + wcf()->utils->do_not_cache();
233 269
234 - /* Set key to support pixel */
270 + if ( _is_wcf_thankyou_type() ) {
271 + /*
272 + Set key to support pixel
273 + */
274 + //phpcs:disable WordPress.Security.NonceVerification
235 275 if ( isset( $_GET['wcf-key'] ) ) {
236 - $_GET['key'] = $_GET['wcf-key'];
237 - $_REQUEST['key'] = $_GET['wcf-key'];
276 +
277 + $wcf_key = sanitize_text_field( wp_unslash( $_GET['wcf-key'] ) );
278 +
279 + $_GET['key'] = $wcf_key;
280 + $_REQUEST['key'] = $wcf_key;
238 281 }
282 +
283 + if ( isset( $_GET['wcf-order'] ) ) {
284 + $wcf_order = intval( wp_unslash( $_GET['wcf-order'] ) );
285 + //phpcs:enable WordPress.Security.NonceVerification
286 +
287 + $_GET['order'] = $wcf_order;
288 + $_REQUEST['order'] = $wcf_order;
289 + $_GET['order-received'] = $wcf_order;
290 + $_REQUEST['order-received'] = $wcf_order;
291 +
292 + $wp->set_query_var( 'order-received', $wcf_order );
293 + }
239 294 }
240 295 }
241 296
242 297 /**
298 + * Delete checkout cookies.
299 + *
300 + * @since 1.0.0
301 + */
302 + public function delete_checkout_cookies() {
303 +
304 + if ( _is_wcf_thankyou_type() ) {
305 +
306 + if ( ! wcf()->is_woo_active || ! isset( WC()->session ) ) {
307 + return;
308 + }
309 +
310 + $user_key = WC()->session->get_customer_id();
311 +
312 + if ( isset( $_COOKIE[ CARTFLOWS_ACTIVE_CHECKOUT ] ) ) {
313 + delete_transient( 'wcf_user_' . $user_key . '_checkout_' . sanitize_text_field( wp_unslash( $_COOKIE[ CARTFLOWS_ACTIVE_CHECKOUT ] ) ) ); //phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___COOKIE
314 + unset( $_COOKIE[ CARTFLOWS_ACTIVE_CHECKOUT ] ); //phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___COOKIE
315 + setcookie( CARTFLOWS_ACTIVE_CHECKOUT, '', time() - 3600, '/', COOKIE_DOMAIN, CARTFLOWS_HTTPS, true ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.cookies_setcookie
316 + }
317 + }
318 + }
319 +
320 + /**
243 321 * Add noindex, nofollow.
244 322 *
245 323 * @since 1.0.0
246 324 */
247 - function noindex_flow() {
325 + public function noindex_flow() {
248 326
249 327 $common = Cartflows_Helper::get_common_settings();
250 328
251 - if ( 'enable' === $common['disallow_indexing'] ) {
329 + global $post;
330 +
331 + $flow_id = wcf()->utils->get_flow_id_from_step_id( $post->ID );
332 + $flow_indexing = get_post_meta( $flow_id, 'wcf-flow-indexing', true );
333 +
334 + $allow_indexing = ( ( '' === $flow_indexing && 'enable' === $common['disallow_indexing'] ) || 'disallow' === $flow_indexing );
335 +
336 + if ( apply_filters( 'cartflows_step_add_noindex_meta', $allow_indexing, $flow_id ) ) {
252 337 echo '<meta name="robots" content="noindex,nofollow">';
253 338 }
254 339 }
255 340
@@ -257,62 +342,603 @@
257 342 * WP Actions.
258 343 *
259 344 * @since 1.0.0
260 345 */
261 - function wp_actions() {
346 + public function wp_actions() {
262 347
263 348 if ( wcf()->utils->is_step_post_type() ) {
264 349
350 + if ( ! wcf()->is_woo_active && wcf()->utils->check_is_woo_required_page() ) {
351 + wp_die( ' This page requires WooCommerce plugin installed and activated!', 'WooCommerce Required' );
352 + }
353 +
265 354 /* CSS Compatibility for All theme */
266 355 add_filter( 'woocommerce_enqueue_styles', array( $this, 'woo_default_css' ), 9999 );
267 356
268 357 add_action( 'wp_enqueue_scripts', array( $this, 'remove_theme_styles' ), 9999 );
358 + add_action( 'wp_enqueue_scripts', array( $this, 'global_flow_scripts' ), 20 );
269 359
270 - add_action( 'wp_enqueue_scripts', array( $this, 'global_flow_scripts' ), 20 );
360 + /* Load woo templates from plugin */
361 + add_filter( 'woocommerce_locate_template', array( $this, 'override_woo_template' ), 20, 3 );
362 +
363 + /* Add version class to body in frontend. */
364 + add_filter( 'body_class', array( $this, 'add_cartflows_lite_version_to_body' ) );
365 +
366 + /* Custom Script Option */
367 + add_action( 'wp_head', array( $this, 'custom_script_option' ) );
368 +
369 + /* Remove the action applied by the Flatsome theme */
370 + if ( Cartflows_Compatibility::get_instance()->is_flatsome_enabled() ) {
371 + $this->remove_flatsome_action();
372 + }
271 373 }
374 + }
272 375
376 + /**
377 + * Debug Data Setting Actions.
378 + *
379 + * @since 1.1.14
380 + */
381 + public function debug_data_setting_actions() {
382 +
383 + add_filter( 'cartflows_load_min_assets', array( $this, 'allow_load_minify' ) );
273 384 }
274 385
275 386 /**
387 + * Get/Set the allow minify option.
388 + *
389 + * @since 1.1.14
390 + */
391 + public function allow_load_minify() {
392 + $debug_data = Cartflows_Helper::get_debug_settings();
393 + $allow_minified = $debug_data['allow_minified_files'];
394 + $allow_minify = false;
395 +
396 + if ( 'enable' === $allow_minified ) {
397 + $allow_minify = true;
398 + }
399 +
400 + return $allow_minify;
401 + }
402 +
403 + /**
276 404 * Global flow scripts.
277 405 *
278 406 * @since 1.0.0
279 407 */
280 - function global_flow_scripts() {
408 + public function global_flow_scripts() {
281 409
282 - global $post;
410 + global $post, $wcf_step;
283 411
284 - $flow = get_post_meta( $post->ID, 'wcf-flow-id', true );
285 - $current_step = $post->ID;
412 + $flow = $wcf_step->get_flow_id();
413 + $current_step = $wcf_step->get_current_step();
414 + $control_step = $wcf_step->get_control_step();
286 415 $next_step_link = '';
287 416 $compatibility = Cartflows_Compatibility::get_instance();
417 + $is_checkout = _is_wcf_checkout_type();
418 + $is_optin = _is_wcf_optin_type();
288 419
289 420 if ( _is_wcf_landing_type() ) {
290 421
291 - $next_step_id = wcf()->utils->get_next_step_id( $flow, $current_step );
422 + $next_step_id = $wcf_step->get_direct_next_step_id();
292 423 $next_step_link = get_permalink( $next_step_id );
293 424 }
294 425
426 + $page_template = get_post_meta( $current_step, '_wp_page_template', true );
427 +
428 + $fb_tracking_settings = Cartflows_Helper::get_facebook_settings();
429 + $ga_tracking_settings = Cartflows_Helper::get_google_analytics_settings();
430 + $tik_pixel_settings = Cartflows_Helper::get_tiktok_settings();
431 + $pinterest_settings = Cartflows_Helper::get_pinterest_settings();
432 + $gads_settings = Cartflows_Helper::get_google_ads_settings();
433 + $snapchat_settings = Cartflows_Helper::get_snapchat_settings();
434 +
295 435 $localize = array(
296 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
297 - 'is_pb_preview' => $compatibility->is_page_builder_preview(),
298 - 'current_flow' => $flow,
299 - 'current_step' => $current_step,
300 - 'next_step' => $next_step_link,
436 + 'ajax_url' => admin_url( 'admin-ajax.php', 'relative' ),
437 + 'is_pb_preview' => $compatibility->is_page_builder_preview(),
438 + 'current_theme' => $compatibility->get_current_theme(),
439 + 'current_flow' => $flow,
440 + 'current_step' => $current_step,
441 + 'control_step' => $control_step,
442 + 'next_step' => $next_step_link,
443 + 'page_template' => $page_template,
444 + 'default_page_builder' => \Cartflows_Helper::get_common_setting( 'default_page_builder' ),
445 + 'is_checkout_page' => $is_checkout,
446 + 'fb_setting' => $fb_tracking_settings,
447 + 'ga_setting' => $ga_tracking_settings,
448 + 'tik_setting' => $tik_pixel_settings,
449 + 'pin_settings' => $pinterest_settings,
450 + 'gads_setting' => $gads_settings,
451 + 'snap_settings' => $snapchat_settings,
452 + 'active_checkout_cookie' => CARTFLOWS_ACTIVE_CHECKOUT,
453 + 'is_optin' => $is_optin,
301 454 );
302 455
303 - wp_localize_script( 'jquery', 'cartflows', apply_filters( 'global_cartflows_js_localize', $localize ) );
456 + if ( $is_checkout ) {
457 + $localize['ajax_url'] = add_query_arg(
458 + array(
459 + 'wcf_checkout_id' => $current_step,
460 + ),
461 + $localize['ajax_url']
462 + );
463 + }
304 464
305 - wp_enqueue_style( 'wcf-frontend-global', CARTFLOWS_URL . 'assets/css/frontend.css', array(), CARTFLOWS_VER );
306 - wp_style_add_data( 'wcf-frontend-global', 'rtl', 'replace' );
465 + $localize = apply_filters( 'global_cartflows_js_localize', $localize );
307 466
467 + $localize_script = '<!-- script to print the admin localized variables -->';
468 + $localize_script .= '<script type="text/javascript">';
469 + $localize_script .= 'var cartflows = ' . wp_json_encode( $localize ) . ';';
470 + $localize_script .= '</script>';
471 +
472 + echo $localize_script; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
473 +
474 + if ( _wcf_supported_template( $page_template ) ) {
475 + $page_builder = Cartflows_Helper::get_common_setting( 'default_page_builder' );
476 + if ( ! ( 'bricks-builder' === $page_builder && function_exists( 'bricks_is_builder' ) && bricks_is_builder() ) ) {
477 + wp_enqueue_style( 'wcf-normalize-frontend-global', wcf()->utils->get_css_url( 'cartflows-normalize' ), array(), CARTFLOWS_VER );
478 + }
479 + }
480 +
481 + if ( ! wcf()->is_woo_active ) {
482 + wp_register_script(
483 + 'jquery-cookie',
484 + CARTFLOWS_URL . 'assets/js/lib/jquery-cookie/jquery.cookie.min.js',
485 + array( 'jquery' ),
486 + CARTFLOWS_VER,
487 + false
488 + );
489 + }
490 +
491 + wp_enqueue_style( 'wcf-frontend-global', wcf()->utils->get_css_url( 'frontend' ), array(), CARTFLOWS_VER );
492 +
308 493 wp_enqueue_script(
309 494 'wcf-frontend-global',
310 - CARTFLOWS_URL . 'assets/js/frontend.js',
311 - array( 'jquery' ),
495 + wcf()->utils->get_js_url( 'frontend' ),
496 + array( 'jquery', 'jquery-cookie' ),
312 497 CARTFLOWS_VER,
313 - true
498 + false
314 499 );
500 + }
501 +
502 + /**
503 + * Custom Script in head.
504 + *
505 + * @since 1.0.0
506 + */
507 + public function custom_script_option() {
508 +
509 + // Check if migration to separate JS/CSS fields is completed.
510 + $migration_completed = 'completed' === Cartflows_Helper::get_script_migration_status();
511 +
512 + $global_css = trim( $this->get_global_custom_css() );
513 + $global_js = trim( $this->get_global_custom_js() );
514 + $flow_css = '';
515 + $flow_js = '';
516 + $step_css = '';
517 + $step_js = '';
518 +
519 + // Old combined script values (used only if migration not completed).
520 + $script = '';
521 + $flow_script = '';
522 +
523 + if ( $migration_completed ) {
524 + // Migration completed: load from new separate JS and CSS meta keys.
525 + $flow_css = trim( $this->get_flow_custom_css() );
526 + $flow_js = trim( $this->get_flow_custom_js() );
527 + $step_css = trim( $this->get_step_custom_css() );
528 + $step_js = trim( $this->get_step_custom_js() );
529 + } else {
530 + // Migration not completed: load from old combined script meta keys.
531 + $script = $this->get_custom_script();
532 + $flow_script = $this->get_flow_custom_script();
533 + }
534 +
535 + // -------------------------
536 + // CSS Output (Global → Flow → Step)
537 + // Step CSS takes priority due to later output.
538 + // -------------------------
539 + if ( '' !== $global_css || '' !== $flow_css || '' !== $step_css ) {
540 + echo '<!-- CartFlows Custom CSS -->';
541 + echo '<style>';
542 +
543 + if ( '' !== $global_css ) {
544 + // Global CSS is already pure CSS from settings.
545 + echo html_entity_decode( $global_css ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
546 + }
547 +
548 + if ( '' !== $flow_css ) {
549 + // New format: pure CSS. Old format: strip style tags.
550 + $flow_css_output = $migration_completed ? $flow_css : $this->strip_script_style_tags( $flow_css );
551 + echo html_entity_decode( $flow_css_output ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
552 + }
553 +
554 + if ( '' !== $step_css ) {
555 + // New format: pure CSS. Old format: strip style tags.
556 + $step_css_output = $migration_completed ? $step_css : $this->strip_script_style_tags( $step_css );
557 + echo html_entity_decode( $step_css_output ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
558 + }
559 +
560 + echo '</style>';
561 + echo '<!-- End CartFlows Custom CSS -->';
562 + echo PHP_EOL;
563 + }
564 +
565 + // -------------------------
566 + // JS Output (Global → Flow → Step)
567 + // Step JS takes priority due to later execution.
568 + // -------------------------
569 + echo '<!-- CartFlows Custom JS -->';
570 + echo '<script>';
571 +
572 + // Global JS.
573 + if ( '' !== $global_js ) {
574 + echo str_replace( '</script>', '<\/script>', html_entity_decode( $global_js ) ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
575 + }
576 +
577 + // Flow JS (new or old format).
578 + if ( $migration_completed && '' !== $flow_js ) {
579 + // New format: already pure JS, just escape closing tags.
580 + echo str_replace( '</script>', '<\/script>', html_entity_decode( $flow_js ) ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
581 + } elseif ( ! $migration_completed && '' !== $flow_script ) {
582 + // Old format: strip any existing script/style tags and output raw JS.
583 + $flow_script_output = $this->strip_script_style_tags( $flow_script );
584 + echo str_replace( '</script>', '<\/script>', html_entity_decode( $flow_script_output ) ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
585 + }
586 +
587 + // Step JS (new or old format).
588 + if ( $migration_completed && '' !== $step_js ) {
589 + // New format: already pure JS, just escape closing tags.
590 + echo str_replace( '</script>', '<\/script>', html_entity_decode( $step_js ) ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
591 + } elseif ( ! $migration_completed && '' !== $script ) {
592 + // Old format: strip any existing script/style tags and output raw JS.
593 + $step_script_output = $this->strip_script_style_tags( $script );
594 + echo str_replace( '</script>', '<\/script>', html_entity_decode( $step_script_output ) ) . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
595 + }
596 +
597 + echo '</script>';
598 + echo '<!-- End CartFlows Custom JS -->';
599 + echo PHP_EOL;
600 + }
601 +
602 + /**
603 + * Strip <script> and <style> tags from content, returning only the inner content.
604 + *
605 + * @since 2.2.3
606 + * @param string $content Content to strip tags from.
607 + * @return string Content with script/style tags removed.
608 + */
609 + private function strip_script_style_tags( $content ) {
610 + // Remove <script>...</script> tags but keep inner content.
611 + $content = preg_replace( '/<script[^>]*>(.*?)<\/script>/is', '$1', $content );
612 + // Remove <style>...</style> tags but keep inner content.
613 + $content = preg_replace( '/<style[^>]*>(.*?)<\/style>/is', '$1', $content );
614 + return trim( $content );
615 + }
616 +
617 + /**
618 + * Override woo templates.
619 + *
620 + * @param string $template new Template full path.
621 + * @param string $template_name Template name.
622 + * @param string $template_path Template Path.
623 + * @since 1.1.5
624 + * @return string.
625 + */
626 + public function override_woo_template( $template, $template_name, $template_path ) {
627 +
628 + global $woocommerce;
629 +
630 + $_template = $template;
631 +
632 + $plugin_path = CARTFLOWS_DIR . 'woocommerce/template/';
633 +
634 + if ( file_exists( $plugin_path . $template_name ) ) {
635 + $template = $plugin_path . $template_name;
636 + }
637 +
638 + if ( ! $template ) {
639 + $template = $_template;
640 + }
641 +
642 + return $template;
643 + }
644 +
645 + /**
646 + * Remove the action applied by the Flatsome theme.
647 + *
648 + * @since 1.1.5
649 + * @return void.
650 + */
651 + public function remove_flatsome_action() {
652 +
653 + // Remove action where flatsome dequeued the woocommerce's default styles.
654 + remove_action( 'wp_enqueue_scripts', 'flatsome_woocommerce_scripts_styles', 98 );
655 + }
656 +
657 + /**
658 + * Add version class to body in frontend.
659 + *
660 + * @since 1.1.5
661 + * @param array $classes classes.
662 + * @return array $classes classes.
663 + */
664 + public function add_cartflows_lite_version_to_body( $classes ) {
665 +
666 + $classes[] = 'cartflows-' . CARTFLOWS_VER;
667 +
668 + return $classes;
669 + }
670 +
671 + /**
672 + * Get custom script data.
673 + *
674 + * @since 1.0.0
675 + */
676 + public function get_custom_script() {
677 +
678 + global $post;
679 +
680 + $script = get_post_meta( $post->ID, 'wcf-custom-script', true );
681 +
682 + $script = $this->maybe_replace_vars( $script );
683 +
684 + return $script;
685 + }
686 +
687 + /**
688 + * Replace the dynamic vars in the custom script.
689 + *
690 + }
691 +
692 + /**
693 + * Get custom script data.
694 + *
695 + * @since 1.0.0
696 + */
697 + public function get_flow_custom_script() {
698 +
699 + global $post;
700 +
701 + $step_id = $post->ID;
702 +
703 + $flow_id = wcf()->utils->get_flow_id_from_step_id( $step_id );
704 +
705 + $script = get_post_meta( $flow_id, 'wcf-flow-custom-script', true );
706 +
707 + $script = $this->maybe_replace_vars( $script );
708 +
709 + return $script;
710 + }
711 +
712 + /**
713 + * Get custom JS script for steps.
714 + *
715 + * @since 2.2.2
716 + */
717 + public function get_step_custom_js() {
718 +
719 + global $post;
720 +
721 + $script = get_post_meta( $post->ID, 'wcf-step-custom-js', true );
722 +
723 + $script = $this->maybe_replace_vars( $script );
724 +
725 + return $script;
726 + }
727 +
728 + /**
729 + * Get custom CSS script for steps.
730 + *
731 + * @since 2.2.2
732 + */
733 + public function get_step_custom_css() {
734 +
735 + global $post;
736 +
737 + $style = get_post_meta( $post->ID, 'wcf-step-custom-css', true );
738 +
739 + return is_string( $style ) ? $style : '';
740 + }
741 +
742 + /**
743 + * Replace the dynamic vars in the custom script.
744 + *
745 + * @param string $script custom script.
746 + * @return string $script modified custom script.
747 + *
748 + * @since 1.10.0
749 + */
750 + public function maybe_replace_vars( $script ) {
751 + //phpcs:disable WordPress.Security.NonceVerification
752 + if ( isset( $_GET['wcf-order'] ) && isset( $_GET['wcf-key'] ) ) {
753 +
754 + $order_id = intval( wp_unslash( $_GET['wcf-order'] ) );
755 + $order_key = wc_clean( wp_unslash( $_GET['wcf-key'] ) );
756 + //phpcs:enable WordPress.Security.NonceVerification
757 + $order = wc_get_order( $order_id );
758 +
759 + if ( $order && $order_key === $order->get_order_key() ) {
760 +
761 + // These variables will be available to use on Upsell, Downsell, Thank you pages of CartFlows only.
762 + $script = str_replace( '{{order_id}}', $order_id, $script );
763 + $script = str_replace( '{{txn_id}}', $order->get_transaction_id(), $script );
764 + $script = str_replace( '{{order_total}}', $order->get_total(), $script );
765 +
766 + $script = apply_filters( 'cartflows_dynamic_js_vars', $script );
767 + }
768 + }
769 +
770 + return $script;
771 + }
772 +
773 + /**
774 + * Get global custom CSS from settings.
775 + *
776 + * @since 2.2.2
777 + * @return string
778 + */
779 + public function get_global_custom_css() {
780 +
781 + $global_scripts = get_option( '_cartflows_global_scripts', array() );
782 +
783 + if ( ! is_array( $global_scripts ) || empty( $global_scripts['global_css'] ) ) {
784 + return '';
785 + }
786 +
787 + return (string) $global_scripts['global_css'];
788 + }
789 +
790 + /**
791 + * Get global custom JS from settings.
792 + *
793 + * @since 2.2.2
794 + * @return string
795 + */
796 + public function get_global_custom_js() {
797 +
798 + $global_scripts = get_option( '_cartflows_global_scripts', array() );
799 +
800 + if ( ! is_array( $global_scripts ) || empty( $global_scripts['global_js'] ) ) {
801 + return '';
802 + }
803 +
804 + return (string) $global_scripts['global_js'];
805 + }
806 +
807 + /**
808 + * Get custom JS script for flow.
809 + *
810 + * @since 2.2.2
811 + * @return string
812 + */
813 + public function get_flow_custom_js() {
814 +
815 + global $post;
816 +
817 + $step_id = $post->ID;
818 +
819 + $flow_id = wcf()->utils->get_flow_id_from_step_id( $step_id );
820 +
821 + $script = get_post_meta( $flow_id, 'wcf-flow-custom-js', true );
822 +
823 + $script = $this->maybe_replace_vars( $script );
824 +
825 + return $script;
826 + }
827 +
828 + /**
829 + * Get custom CSS script for flow.
830 + *
831 + * @since 2.2.2
832 + * @return string
833 + */
834 + public function get_flow_custom_css() {
835 +
836 + global $post;
837 +
838 + $step_id = $post->ID;
839 +
840 + $flow_id = wcf()->utils->get_flow_id_from_step_id( $step_id );
841 +
842 + $style = get_post_meta( $flow_id, 'wcf-flow-custom-css', true );
843 +
844 + return is_string( $style ) ? $style : '';
845 + }
846 +
847 + /**
848 + * Set appropriate filter sctions.
849 + *
850 + * @since 1.1.14
851 + */
852 + public function setup_optin_checkout_filter() {
853 +
854 + if ( _is_wcf_doing_optin_ajax() ) {
855 + /* Modify the optin order received url to go next step */
856 + remove_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'redirect_to_thankyou_page' ), 10, 2 );
857 + add_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'redirect_optin_to_next_step' ), 10, 2 );
858 + }
859 + }
860 +
861 + /**
862 + * Redirect to thank page if upsell not exists
863 + *
864 + * @param string $order_receive_url url.
865 + * @param object $order order object.
866 + * @return string $order_receive_url next step URL.
867 + * @since 1.0.0
868 + */
869 + public function redirect_optin_to_next_step( $order_receive_url, $order ) {
870 +
871 + /* Only for optin page */
872 + wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ );
873 + wcf()->logger->log( 'Only for optin page' );
874 +
875 + if ( _is_wcf_doing_optin_ajax() ) {
876 +
877 + $optin_id = wcf()->utils->get_optin_id_from_post_data();
878 +
879 + if ( ! $optin_id ) {
880 + $optin_id = wcf()->utils->get_optin_id_from_order( $order );
881 + }
882 + } else {
883 + $optin_id = wcf()->utils->get_optin_id_from_order( $order );
884 + }
885 +
886 + wcf()->logger->log( 'Optin ID : ' . $optin_id );
887 +
888 + if ( $optin_id ) {
889 +
890 + $wcf_step_obj = wcf_get_step( $optin_id );
891 + $next_step_id = $wcf_step_obj->get_direct_next_step_id();
892 +
893 + if ( $next_step_id ) {
894 +
895 + $order_receive_url = get_permalink( $next_step_id );
896 + $query_param = array(
897 + 'wcf-key' => $order->get_order_key(),
898 + 'wcf-order' => $order->get_id(),
899 + );
900 +
901 + if ( 'yes' === wcf()->options->get_optin_meta_value( $optin_id, 'wcf-optin-pass-fields' ) ) {
902 +
903 + $fields_string = wcf()->options->get_optin_meta_value( $optin_id, 'wcf-optin-pass-specific-fields' );
904 +
905 + $fields = array_map( 'trim', explode( ',', $fields_string ) );
906 +
907 + if ( is_array( $fields ) ) {
908 +
909 + foreach ( $fields as $in => $key ) {
910 + switch ( $key ) {
911 + case 'first_name':
912 + $query_param[ $key ] = $order->get_billing_first_name();
913 + break;
914 + case 'last_name':
915 + $query_param[ $key ] = $order->get_billing_last_name();
916 + break;
917 + case 'email':
918 + $query_param[ $key ] = $order->get_billing_email();
919 + break;
920 + default:
921 + $query_param[ $key ] = $order->get_meta( '_billing_' . $key, true );
922 + break;
923 + }
924 + }
925 + }
926 + }
927 +
928 + // Add any existing URL parameters at the end of the URL before redirecting.
929 + $query_param = wcf()->utils->may_be_append_query_string( $query_param );
930 +
931 + $order_receive_url = add_query_arg(
932 + $query_param,
933 + $order_receive_url
934 + );
935 + }
936 + }
937 +
938 + wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ );
939 +
940 + return $order_receive_url;
315 941 }
316 942 }
317 943
318 944 /**