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 +694 -65 1.0.4 → 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,26 +38,26 @@
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 */
42 48 add_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'redirect_to_thankyou_page' ), 10, 2 );
43 -
44 - add_filter( 'woocommerce_get_cancel_order_url_raw', array( $this, 'redirect_to_checkout_on_cancel' ), 10 );
45 -
46 49 }
47 50
48 51 /**
49 52 * Redirect to thank page if upsell not exists
50 53 *
51 - * @param string $order_recieve_url url.
54 + * @param string $order_receive_url url.
52 55 * @param object $order order object.
56 + * @return string $order_receive_url next step URL.
53 57 * @since 1.0.0
54 58 */
55 - function redirect_to_thankyou_page( $order_recieve_url, $order ) {
59 + public function redirect_to_thankyou_page( $order_receive_url, $order ) {
56 60
57 61 /* Only for thank you page */
58 62 wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ );
59 63 wcf()->logger->log( 'Only for thank you page' );
@@ -64,12 +68,12 @@
64 68
65 69 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
66 70
67 71 if ( ! $checkout_id ) {
68 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
72 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
69 73 }
70 74 } else {
71 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
75 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
72 76 }
73 77
74 78 wcf()->logger->log( 'Checkout ID : ' . $checkout_id );
75 79
@@ -75,20 +79,25 @@
75 79
76 80 if ( $checkout_id ) {
77 81
78 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 );
79 84
80 85 if ( $thankyou_step_id ) {
81 86
82 - $order_recieve_url = get_permalink( $thankyou_step_id );
83 -
84 - $order_recieve_url = add_query_arg(
87 + $query_param = wcf()->utils->may_be_append_query_string(
88 + // Default query string args.
85 89 array(
90 + 'wcf-key' => $order->get_order_key(),
86 91 'wcf-order' => $order->get_id(),
87 - 'wcf-key' => $order->get_order_key(),
88 - ),
89 - $order_recieve_url
92 + )
90 93 );
94 +
95 + $order_receive_url = add_query_arg(
96 + $query_param,
97 + get_permalink( $thankyou_step_id )
98 + );
99 +
91 100 }
92 101 }
93 102 }
94 103
@@ -93,9 +102,9 @@
93 102 }
94 103
95 104 wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ );
96 105
97 - return $order_recieve_url;
106 + return $order_receive_url;
98 107 }
99 108
100 109 /**
101 110 * Cancel and redirect to checkout
@@ -102,9 +111,9 @@
102 111 *
103 112 * @param string $return_url url.
104 113 * @since 1.0.0
105 114 */
106 - function redirect_to_checkout_on_cancel( $return_url ) {
115 + public function redirect_to_checkout_on_cancel( $return_url ) {
107 116
108 117 if ( _is_wcf_doing_checkout_ajax() ) {
109 118
110 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
@@ -109,12 +118,12 @@
109 118
110 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
111 120
112 121 if ( ! $checkout_id ) {
113 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
122 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
114 123 }
115 124 } else {
116 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
125 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
117 126 }
118 127
119 128 if ( $checkout_id ) {
120 129
@@ -135,43 +144,56 @@
135 144 * Remove theme styles.
136 145 *
137 146 * @since 1.0.0
138 147 */
139 - function remove_theme_styles() {
148 + public function remove_theme_styles() {
140 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 +
141 160 // get all styles data.
142 161 global $wp_styles;
143 162 global $wp_scripts;
144 163
145 - $get_stylesheet = get_stylesheet();
146 - $get_template = get_template();
164 + $get_stylesheet = 'themes/' . get_stylesheet() . '/';
165 + $get_template = 'themes/' . get_template() . '/';
147 166
148 - if ( Cartflows_Compatibility::get_instance()->is_divi_enabled() ) {
149 - return;
150 - }
167 + $remove_styles = apply_filters( 'cartflows_remove_theme_styles', true );
151 168
152 - $get_stylesheet = 'themes/' . get_stylesheet();
153 - $get_template = 'themes/' . get_template();
169 + if ( $remove_styles ) {
154 170
155 - // loop over all of the registered scripts..
156 - foreach ( $wp_styles->registered as $handle => $data ) {
171 + // loop over all of the registered scripts..
172 + foreach ( $wp_styles->registered as $handle => $data ) {
157 173
158 - 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 ) ) {
159 175
160 - // remove it.
161 - wp_deregister_style( $handle );
162 - wp_dequeue_style( $handle );
176 + // remove it.
177 + wp_deregister_style( $handle );
178 + wp_dequeue_style( $handle );
179 + }
163 180 }
164 181 }
165 182
166 - // loop over all of the registered scripts.
167 - foreach ( $wp_scripts->registered as $handle => $data ) {
183 + $remove_scripts = apply_filters( 'cartflows_remove_theme_scripts', true );
168 184
169 - if ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) {
185 + if ( $remove_scripts ) {
170 186
171 - // remove it.
172 - wp_deregister_script( $handle );
173 - 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 + }
174 196 }
175 197 }
176 198 }
177 199
@@ -181,9 +203,9 @@
181 203 * @param array $woo_styles new styles array.
182 204 * @since 1.0.0
183 205 * @return array.
184 206 */
185 - function woo_default_css( $woo_styles ) {
207 + public function woo_default_css( $woo_styles ) {
186 208
187 209 $woo_styles = array(
188 210 'woocommerce-layout' => array(
189 211 'src' => plugins_url( 'assets/css/woocommerce-layout.css', WC_PLUGIN_FILE ),
@@ -215,11 +237,22 @@
215 237 * Init Actions.
216 238 *
217 239 * @since 1.0.0
218 240 */
219 - function init_actions() {
241 + public function init_actions() {
220 242
221 - $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 + }
222 255 }
223 256
224 257 /**
225 258 * Set flow session.
@@ -225,28 +258,83 @@
225 258 * Set flow session.
226 259 *
227 260 * @since 1.0.0
228 261 */
229 - function set_flow_session() {
262 + public function set_flow_session() {
230 263
231 - if ( wcf()->utils->is_step_post_type() ) {
264 + global $wp;
232 265
233 - add_action( 'wp_head', array( $this, 'noindex_flow' ) );
266 + add_action( 'wp_head', array( $this, 'noindex_flow' ) );
234 267
235 - wcf()->utils->do_not_cache();
268 + wcf()->utils->do_not_cache();
269 +
270 + if ( _is_wcf_thankyou_type() ) {
271 + /*
272 + Set key to support pixel
273 + */
274 + //phpcs:disable WordPress.Security.NonceVerification
275 + if ( isset( $_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;
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 + }
236 294 }
237 295 }
238 296
239 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 + /**
240 321 * Add noindex, nofollow.
241 322 *
242 323 * @since 1.0.0
243 324 */
244 - function noindex_flow() {
325 + public function noindex_flow() {
245 326
246 327 $common = Cartflows_Helper::get_common_settings();
247 328
248 - 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 ) ) {
249 337 echo '<meta name="robots" content="noindex,nofollow">';
250 338 }
251 339 }
252 340
@@ -254,62 +342,603 @@
254 342 * WP Actions.
255 343 *
256 344 * @since 1.0.0
257 345 */
258 - function wp_actions() {
346 + public function wp_actions() {
259 347
260 348 if ( wcf()->utils->is_step_post_type() ) {
261 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 +
262 354 /* CSS Compatibility for All theme */
263 355 add_filter( 'woocommerce_enqueue_styles', array( $this, 'woo_default_css' ), 9999 );
264 356
265 357 add_action( 'wp_enqueue_scripts', array( $this, 'remove_theme_styles' ), 9999 );
358 + add_action( 'wp_enqueue_scripts', array( $this, 'global_flow_scripts' ), 20 );
266 359
267 - 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 + }
268 373 }
374 + }
269 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' ) );
270 384 }
271 385
272 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 + /**
273 404 * Global flow scripts.
274 405 *
275 406 * @since 1.0.0
276 407 */
277 - function global_flow_scripts() {
408 + public function global_flow_scripts() {
278 409
279 - global $post;
410 + global $post, $wcf_step;
280 411
281 - $flow = get_post_meta( $post->ID, 'wcf-flow-id', true );
282 - $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();
283 415 $next_step_link = '';
284 416 $compatibility = Cartflows_Compatibility::get_instance();
417 + $is_checkout = _is_wcf_checkout_type();
418 + $is_optin = _is_wcf_optin_type();
285 419
286 420 if ( _is_wcf_landing_type() ) {
287 421
288 - $next_step_id = wcf()->utils->get_next_step_id( $flow, $current_step );
422 + $next_step_id = $wcf_step->get_direct_next_step_id();
289 423 $next_step_link = get_permalink( $next_step_id );
290 424 }
291 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 +
292 435 $localize = array(
293 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
294 - 'is_pb_preview' => $compatibility->is_page_builder_preview(),
295 - 'current_flow' => $flow,
296 - 'current_step' => $current_step,
297 - '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,
298 454 );
299 455
300 - 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 + }
301 464
302 - wp_enqueue_style( 'wcf-frontend-global', CARTFLOWS_URL . 'assets/css/frontend.css', array(), CARTFLOWS_VER );
303 - wp_style_add_data( 'wcf-frontend-global', 'rtl', 'replace' );
465 + $localize = apply_filters( 'global_cartflows_js_localize', $localize );
304 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 +
305 493 wp_enqueue_script(
306 494 'wcf-frontend-global',
307 - CARTFLOWS_URL . 'assets/js/frontend.js',
308 - array( 'jquery' ),
495 + wcf()->utils->get_js_url( 'frontend' ),
496 + array( 'jquery', 'jquery-cookie' ),
309 497 CARTFLOWS_VER,
310 - true
498 + false
311 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;
312 941 }
313 942 }
314 943
315 944 /**