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 +548 -66 1.1.16 → 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
@@ -35,24 +39,25 @@
35 39 /* Set / Destroy Flow Sessions. Set data */
36 40 add_action( 'wp', array( $this, 'init_actions' ), 1 );
37 41
38 42 add_action( 'init', array( $this, 'debug_data_setting_actions' ) );
43 + add_action( 'init', array( $this, 'setup_optin_checkout_filter' ) );
39 44 /* Enqueue global required scripts */
40 45 add_action( 'wp', array( $this, 'wp_actions' ), 55 );
41 46
42 47 /* Modify the checkout order received url to go thank you page in our flow */
43 48 add_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'redirect_to_thankyou_page' ), 10, 2 );
44 -
45 49 }
46 50
47 51 /**
48 52 * Redirect to thank page if upsell not exists
49 53 *
50 - * @param string $order_recieve_url url.
54 + * @param string $order_receive_url url.
51 55 * @param object $order order object.
56 + * @return string $order_receive_url next step URL.
52 57 * @since 1.0.0
53 58 */
54 - function redirect_to_thankyou_page( $order_recieve_url, $order ) {
59 + public function redirect_to_thankyou_page( $order_receive_url, $order ) {
55 60
56 61 /* Only for thank you page */
57 62 wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ );
58 63 wcf()->logger->log( 'Only for thank you page' );
@@ -63,12 +68,12 @@
63 68
64 69 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
65 70
66 71 if ( ! $checkout_id ) {
67 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
72 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
68 73 }
69 74 } else {
70 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
75 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
71 76 }
72 77
73 78 wcf()->logger->log( 'Checkout ID : ' . $checkout_id );
74 79
@@ -74,20 +79,25 @@
74 79
75 80 if ( $checkout_id ) {
76 81
77 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 );
78 84
79 85 if ( $thankyou_step_id ) {
80 86
81 - $order_recieve_url = get_permalink( $thankyou_step_id );
82 -
83 - $order_recieve_url = add_query_arg(
87 + $query_param = wcf()->utils->may_be_append_query_string(
88 + // Default query string args.
84 89 array(
85 90 'wcf-key' => $order->get_order_key(),
86 91 'wcf-order' => $order->get_id(),
87 - ),
88 - $order_recieve_url
92 + )
89 93 );
94 +
95 + $order_receive_url = add_query_arg(
96 + $query_param,
97 + get_permalink( $thankyou_step_id )
98 + );
99 +
90 100 }
91 101 }
92 102 }
93 103
@@ -92,9 +102,9 @@
92 102 }
93 103
94 104 wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ );
95 105
96 - return $order_recieve_url;
106 + return $order_receive_url;
97 107 }
98 108
99 109 /**
100 110 * Cancel and redirect to checkout
@@ -101,9 +111,9 @@
101 111 *
102 112 * @param string $return_url url.
103 113 * @since 1.0.0
104 114 */
105 - function redirect_to_checkout_on_cancel( $return_url ) {
115 + public function redirect_to_checkout_on_cancel( $return_url ) {
106 116
107 117 if ( _is_wcf_doing_checkout_ajax() ) {
108 118
109 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
@@ -108,12 +118,12 @@
108 118
109 119 $checkout_id = wcf()->utils->get_checkout_id_from_post_data();
110 120
111 121 if ( ! $checkout_id ) {
112 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
122 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
113 123 }
114 124 } else {
115 - $checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
125 + $checkout_id = wcf()->utils->get_checkout_id_from_order( $order );
116 126 }
117 127
118 128 if ( $checkout_id ) {
119 129
@@ -134,14 +144,20 @@
134 144 * Remove theme styles.
135 145 *
136 146 * @since 1.0.0
137 147 */
138 - function remove_theme_styles() {
148 + public function remove_theme_styles() {
139 149
140 150 if ( Cartflows_Compatibility::get_instance()->is_compatibility_theme_enabled() ) {
141 151 return;
142 152 }
143 153
154 + $page_template = Cartflows_Helper::get_current_page_template();
155 +
156 + if ( ! _wcf_supported_template( $page_template ) ) {
157 + return;
158 + }
159 +
144 160 // get all styles data.
145 161 global $wp_styles;
146 162 global $wp_scripts;
147 163
@@ -154,9 +170,9 @@
154 170
155 171 // loop over all of the registered scripts..
156 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 176 // remove it.
161 177 wp_deregister_style( $handle );
162 178 wp_dequeue_style( $handle );
@@ -170,9 +186,9 @@
170 186
171 187 // loop over all of the registered scripts.
172 188 foreach ( $wp_scripts->registered as $handle => $data ) {
173 189
174 - if ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) {
190 + if ( ! empty( $data->src ) && ( strpos( $data->src, $get_template ) !== false || strpos( $data->src, $get_stylesheet ) !== false ) ) {
175 191
176 192 // remove it.
177 193 wp_deregister_script( $handle );
178 194 wp_dequeue_script( $handle );
@@ -178,9 +194,8 @@
178 194 wp_dequeue_script( $handle );
179 195 }
180 196 }
181 197 }
182 -
183 198 }
184 199
185 200 /**
186 201 * Update main order data in transient.
@@ -188,9 +203,9 @@
188 203 * @param array $woo_styles new styles array.
189 204 * @since 1.0.0
190 205 * @return array.
191 206 */
192 - function woo_default_css( $woo_styles ) {
207 + public function woo_default_css( $woo_styles ) {
193 208
194 209 $woo_styles = array(
195 210 'woocommerce-layout' => array(
196 211 'src' => plugins_url( 'assets/css/woocommerce-layout.css', WC_PLUGIN_FILE ),
@@ -222,11 +237,22 @@
222 237 * Init Actions.
223 238 *
224 239 * @since 1.0.0
225 240 */
226 - function init_actions() {
241 + public function init_actions() {
227 242
228 - $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 + }
229 255 }
230 256
231 257 /**
232 258 * Set flow session.
@@ -232,41 +258,83 @@
232 258 * Set flow session.
233 259 *
234 260 * @since 1.0.0
235 261 */
236 - function set_flow_session() {
262 + public function set_flow_session() {
237 263
238 - if ( wcf()->utils->is_step_post_type() ) {
264 + global $wp;
239 265
240 - add_action( 'wp_head', array( $this, 'noindex_flow' ) );
266 + add_action( 'wp_head', array( $this, 'noindex_flow' ) );
241 267
242 - wcf()->utils->do_not_cache();
268 + wcf()->utils->do_not_cache();
243 269
244 - /* 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
245 275 if ( isset( $_GET['wcf-key'] ) ) {
246 - $_GET['key'] = $_GET['wcf-key'];
247 - $_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;
248 281 }
249 282
250 283 if ( isset( $_GET['wcf-order'] ) ) {
251 - $_GET['order'] = $_GET['wcf-order'];
252 - $_REQUEST['order'] = $_GET['wcf-order'];
253 - $_GET['order-received'] = $_GET['wcf-order'];
254 - $_REQUEST['order-received'] = $_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 );
255 293 }
256 294 }
257 295 }
258 296
259 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 + /**
260 321 * Add noindex, nofollow.
261 322 *
262 323 * @since 1.0.0
263 324 */
264 - function noindex_flow() {
325 + public function noindex_flow() {
265 326
266 327 $common = Cartflows_Helper::get_common_settings();
267 328
268 - 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 ) ) {
269 337 echo '<meta name="robots" content="noindex,nofollow">';
270 338 }
271 339 }
272 340
@@ -274,12 +342,16 @@
274 342 * WP Actions.
275 343 *
276 344 * @since 1.0.0
277 345 */
278 - function wp_actions() {
346 + public function wp_actions() {
279 347
280 348 if ( wcf()->utils->is_step_post_type() ) {
281 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 +
282 354 /* CSS Compatibility for All theme */
283 355 add_filter( 'woocommerce_enqueue_styles', array( $this, 'woo_default_css' ), 9999 );
284 356
285 357 add_action( 'wp_enqueue_scripts', array( $this, 'remove_theme_styles' ), 9999 );
@@ -305,9 +377,9 @@
305 377 * Debug Data Setting Actions.
306 378 *
307 379 * @since 1.1.14
308 380 */
309 - function debug_data_setting_actions() {
381 + public function debug_data_setting_actions() {
310 382
311 383 add_filter( 'cartflows_load_min_assets', array( $this, 'allow_load_minify' ) );
312 384 }
313 385
@@ -315,9 +387,9 @@
315 387 * Get/Set the allow minify option.
316 388 *
317 389 * @since 1.1.14
318 390 */
319 - function allow_load_minify() {
391 + public function allow_load_minify() {
320 392 $debug_data = Cartflows_Helper::get_debug_settings();
321 393 $allow_minified = $debug_data['allow_minified_files'];
322 394 $allow_minify = false;
323 395
@@ -332,42 +404,99 @@
332 404 * Global flow scripts.
333 405 *
334 406 * @since 1.0.0
335 407 */
336 - function global_flow_scripts() {
408 + public function global_flow_scripts() {
337 409
338 - global $post;
410 + global $post, $wcf_step;
339 411
340 - $flow = get_post_meta( $post->ID, 'wcf-flow-id', true );
341 - $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();
342 415 $next_step_link = '';
343 416 $compatibility = Cartflows_Compatibility::get_instance();
417 + $is_checkout = _is_wcf_checkout_type();
418 + $is_optin = _is_wcf_optin_type();
344 419
345 420 if ( _is_wcf_landing_type() ) {
346 421
347 - $next_step_id = wcf()->utils->get_next_step_id( $flow, $current_step );
422 + $next_step_id = $wcf_step->get_direct_next_step_id();
348 423 $next_step_link = get_permalink( $next_step_id );
349 424 }
350 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 +
351 435 $localize = array(
352 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
353 - 'is_pb_preview' => $compatibility->is_page_builder_preview(),
354 - 'current_theme' => $compatibility->get_current_theme(),
355 - 'current_flow' => $flow,
356 - 'current_step' => $current_step,
357 - '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,
358 454 );
359 455
360 - 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 + }
361 464
465 + $localize = apply_filters( 'global_cartflows_js_localize', $localize );
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 +
362 491 wp_enqueue_style( 'wcf-frontend-global', wcf()->utils->get_css_url( 'frontend' ), array(), CARTFLOWS_VER );
363 492
364 493 wp_enqueue_script(
365 494 'wcf-frontend-global',
366 495 wcf()->utils->get_js_url( 'frontend' ),
367 - array( 'jquery' ),
496 + array( 'jquery', 'jquery-cookie' ),
368 497 CARTFLOWS_VER,
369 - true
498 + false
370 499 );
371 500 }
372 501
373 502 /**
@@ -374,23 +503,119 @@
374 503 * Custom Script in head.
375 504 *
376 505 * @since 1.0.0
377 506 */
378 - function custom_script_option() {
507 + public function custom_script_option() {
379 508
380 - /* Add custom script to header in frontend. */
381 - $script = $this->get_custom_script();
382 - if ( '' !== $script ) {
383 - if ( false === strpos( $script, '<script' ) ) {
384 - $script = '<script>' . $script . '</script>';
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
385 546 }
386 - echo '<!-- Custom CartFlows Script -->';
387 - echo $script;
388 - echo '<!-- End Custom CartFlows Script -->';
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;
389 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;
390 600 }
391 601
392 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 + /**
393 618 * Override woo templates.
394 619 *
395 620 * @param string $template new Template full path.
396 621 * @param string $template_name Template name.
@@ -397,9 +622,9 @@
397 622 * @param string $template_path Template Path.
398 623 * @since 1.1.5
399 624 * @return string.
400 625 */
401 - function override_woo_template( $template, $template_name, $template_path ) {
626 + public function override_woo_template( $template, $template_name, $template_path ) {
402 627
403 628 global $woocommerce;
404 629
405 630 $_template = $template;
@@ -422,9 +647,9 @@
422 647 *
423 648 * @since 1.1.5
424 649 * @return void.
425 650 */
426 - function remove_flatsome_action() {
651 + public function remove_flatsome_action() {
427 652
428 653 // Remove action where flatsome dequeued the woocommerce's default styles.
429 654 remove_action( 'wp_enqueue_scripts', 'flatsome_woocommerce_scripts_styles', 98 );
430 655 }
@@ -435,28 +660,285 @@
435 660 * @since 1.1.5
436 661 * @param array $classes classes.
437 662 * @return array $classes classes.
438 663 */
439 - function add_cartflows_lite_version_to_body( $classes ) {
664 + public function add_cartflows_lite_version_to_body( $classes ) {
440 665
441 666 $classes[] = 'cartflows-' . CARTFLOWS_VER;
442 667
443 668 return $classes;
669 + }
444 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;
445 685 }
446 686
447 687 /**
688 + * Replace the dynamic vars in the custom script.
689 + *
690 + }
691 +
692 + /**
448 693 * Get custom script data.
449 694 *
450 695 * @since 1.0.0
451 696 */
452 - function get_custom_script() {
697 + public function get_flow_custom_script() {
453 698
454 699 global $post;
455 700
456 - $script = get_post_meta( $post->ID, 'wcf-custom-script', true );
701 + $step_id = $post->ID;
457 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 +
458 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;
459 941 }
460 942 }
461 943
462 944 /**