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 | admin-core/inc/admin-menu.php +130 -66 3.0.1 → 3.3.0 View file →
@@ -222,8 +222,12 @@
222 222 // Ignoring nonce verification as using SuperGlobal variables on WordPress hooks.
223 223 if ( isset( $_GET['page'] ) && ( 'cartflows' === sanitize_text_field( $_GET['page'] ) || false !== strpos( sanitize_text_field( $_GET['page'] ), 'cartflows_' ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
224 224
225 225 add_action( 'admin_enqueue_scripts', array( $this, 'styles_scripts' ) );
226 +
227 + // Twemoji rewrites emoji text nodes inside our React tree, detaching nodes React still tracks and crashing the editor.
228 + remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
229 + remove_action( 'admin_print_styles', 'print_emoji_styles' );
226 230 }
227 231 }
228 232
229 233 /**
@@ -276,8 +280,22 @@
276 280 $capability,
277 281 'admin.php?page=' . $this->menu_slug . '&path=analytics'
278 282 );
279 283
284 + // Persistent re-entry point until the guided setup is finished.
285 + if ( current_user_can( 'manage_options' ) && '1' !== get_option( 'wcf_setup_complete', false ) ) {
286 + $exit_step = get_option( 'wcf_exit_setup_step', '' );
287 + $resume_link = 'index.php?page=cartflows-onboarding' . ( ! empty( $exit_step ) && is_string( $exit_step ) ? '&step=' . sanitize_key( $exit_step ) : '' );
288 +
289 + add_submenu_page(
290 + $parent_slug,
291 + __( 'Resume Setup', 'cartflows' ),
292 + __( 'Resume Setup', 'cartflows' ),
293 + $capability,
294 + $resume_link
295 + );
296 + }
297 +
280 298 if ( current_user_can( 'cartflows_manage_settings' ) ) {
281 299
282 300 add_submenu_page(
283 301 $parent_slug,
@@ -354,10 +372,8 @@
354 372 if ( $this->is_current_page( 'cartflows' ) ) {
355 373 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/settings-app.php';
356 374 } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-edit-flow' ) ) ) {
357 375 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/editor-app.php';
358 - } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-log' ) ) ) {
359 - include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/debugger.php';
360 376 } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-license' ) ) && _is_cartflows_pro() ) {
361 377 do_action( 'cartflows_admin_log', 'wcf-license' );
362 378 } else {
363 379 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/404-error.php';
@@ -432,20 +448,21 @@
432 448
433 449 $localize = apply_filters(
434 450 'cartflows_admin_localized_vars',
435 451 array(
436 - 'current_user' => ! empty( wp_get_current_user()->user_firstname ) ? wp_get_current_user()->user_firstname : wp_get_current_user()->display_name,
437 - 'cf_pro_status' => $cf_pro_status,
438 - 'cf_pro_type' => 'free',
439 - 'cf_pro_type_inactive' => $cf_pro_type_inactive,
440 - 'woocommerce_status' => $this->get_plugin_status( 'woocommerce/woocommerce.php' ),
441 - 'default_page_builder' => $page_builder,
442 - 'required_plugins' => \Cartflows_Helper::get_plugins_groupby_page_builders(),
443 - 'required_plugins_data' => $this->get_required_plugins_data(),
444 - 'is_any_required_plugins_missing' => $this->get_any_required_plugins_status(),
445 - 'admin_base_slug' => $this->menu_slug,
446 - 'admin_base_url' => admin_url(),
447 - 'title_length' => apply_filters(
452 + 'current_user' => ! empty( wp_get_current_user()->user_firstname ) ? wp_get_current_user()->user_firstname : wp_get_current_user()->display_name,
453 + 'cf_pro_status' => $cf_pro_status,
454 + 'cf_pro_type' => 'free',
455 + 'cf_pro_type_inactive' => $cf_pro_type_inactive,
456 + 'woocommerce_status' => $this->get_plugin_status( 'woocommerce/woocommerce.php' ),
457 + 'default_page_builder' => $page_builder,
458 + 'required_plugins' => \Cartflows_Helper::get_plugins_groupby_page_builders(),
459 + 'required_plugins_data' => $this->get_required_plugins_data(),
460 + 'is_any_required_plugins_missing' => $this->get_any_required_plugins_status(),
461 + 'admin_base_slug' => $this->menu_slug,
462 + 'admin_base_url' => admin_url(),
463 + 'is_onboarding_completed' => '1' === get_option( 'wcf_setup_complete', false ),
464 + 'title_length' => apply_filters(
448 465 'cartflows_flows_steps_title_length',
449 466 array(
450 467 'max' => 50,
451 468 'display_length' => 40,
@@ -450,37 +467,43 @@
450 467 'max' => 50,
451 468 'display_length' => 40,
452 469 )
453 470 ),
454 - 'plugin_dir' => CARTFLOWS_URL,
455 - 'admin_url' => admin_url( 'admin.php' ),
456 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
457 - 'is_rtl' => is_rtl(),
458 - 'home_slug' => $this->menu_slug,
459 - 'is_pro' => _is_cartflows_pro(),
460 - 'is_file_mod_disabled' => function_exists( 'wcf_file_mod_disabled' ) ? wcf_file_mod_disabled() : false, // Keep false by default if function not exists.
461 - 'page_builder' => $page_builder,
462 - 'page_builder_name' => $page_builder_name,
463 - 'global_checkout' => \Cartflows_Helper::get_common_setting( 'global_checkout' ),
464 - 'flows_count' => 1, // Removing the flow count condition.
465 - 'currentFlowSteps' => $current_flow_steps,
471 + 'plugin_dir' => CARTFLOWS_URL,
472 + 'admin_url' => admin_url( 'admin.php' ),
473 + 'ajax_url' => admin_url( 'admin-ajax.php' ),
474 + 'is_rtl' => is_rtl(),
475 + 'home_slug' => $this->menu_slug,
476 + 'is_pro' => _is_cartflows_pro(),
477 + 'is_file_mod_disabled' => function_exists( 'wcf_file_mod_disabled' ) ? wcf_file_mod_disabled() : false, // Keep false by default if function not exists.
478 + 'page_builder' => $page_builder,
479 + 'page_builder_name' => $page_builder_name,
480 + 'global_checkout' => \Cartflows_Helper::get_common_setting( 'global_checkout' ),
481 + 'flows_count' => 1, // Removing the flow count condition.
482 + 'currentFlowSteps' => $current_flow_steps,
466 483 // Delete this code after 3 major update. Added in 1.10.4.
467 - 'license_status' => \_is_cartflows_pro_license_activated(),
468 - 'license_popup_url' => admin_url( 'admin.php?page=cartflows&path=settings&tab=license' ),
469 - 'store_checkout_show_product_tab' => \Cartflows_Helper::display_product_tab_in_store_checkout(),
470 - 'cf_domain_url' => CARTFLOWS_DOMAIN_URL,
471 - 'cf_upgrade_to_pro_url' => \Cartflows_Helper::get_upgrade_to_pro_link(),
472 - 'logo_url' => esc_url_raw( CARTFLOWS_URL . 'assets/images/cartflows-logo.svg' ),
473 - 'create_product_src' => $product_src,
474 - 'cf_font_family' => AdminHelper::get_font_family(),
475 - 'flows_and_steps' => ! empty( $flows_and_steps ) ? $flows_and_steps : '',
476 - 'store_checkout_flows_and_steps' => \Cartflows_Helper::get_instance()->get_flows_and_steps( '', 'store-checkout' ),
477 - 'woo_currency' => function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '',
478 - 'template_library_url' => wcf()->get_site_url(),
479 - 'image_placeholder' => esc_url_raw( CARTFLOWS_URL . 'admin-core/assets/images/image-placeholder.png' ),
480 - 'google_fonts' => \CartFlows_Font_Families::get_google_fonts(),
481 - 'system_fonts' => \CartFlows_Font_Families::get_system_fonts(),
482 - 'font_weights' => array(
484 + 'license_status' => \_is_cartflows_pro_license_activated(),
485 + 'license_popup_url' => admin_url( 'admin.php?page=cartflows&path=settings&tab=license' ),
486 + 'store_checkout_show_product_tab' => \Cartflows_Helper::display_product_tab_in_store_checkout(),
487 + 'cf_domain_url' => CARTFLOWS_DOMAIN_URL,
488 + 'cf_upgrade_to_pro_url' => \Cartflows_Helper::get_upgrade_to_pro_link(),
489 + 'logo_url' => esc_url_raw( CARTFLOWS_URL . 'assets/images/cartflows-logo.svg' ),
490 + 'create_product_src' => $product_src,
491 + 'cf_font_family' => AdminHelper::get_font_family(),
492 + 'flows_and_steps' => ! empty( $flows_and_steps ) ? $flows_and_steps : '',
493 + 'store_checkout_flows_and_steps' => \Cartflows_Helper::get_instance()->get_flows_and_steps( '', 'store-checkout' ),
494 + 'woo_currency' => function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '',
495 + 'woo_currency_pos' => function_exists( 'wc' ) ? get_option( 'woocommerce_currency_pos', 'left' ) : 'left',
496 + 'woo_decimal_sep' => function_exists( 'wc_get_price_decimal_separator' ) ? wc_get_price_decimal_separator() : '.',
497 + 'woo_thousand_sep' => function_exists( 'wc_get_price_thousand_separator' ) ? wc_get_price_thousand_separator() : ',',
498 + 'woo_decimals' => function_exists( 'wc_get_price_decimals' ) ? wc_get_price_decimals() : 2,
499 + 'template_library_url' => wcf()->get_site_url(),
500 + 'image_placeholder' => esc_url_raw( CARTFLOWS_URL . 'admin-core/assets/images/image-placeholder.png' ),
501 + 'ai_connected' => class_exists( '\Cartflows_Ai_Auth' ) ? \Cartflows_Ai_Auth::get_instance()->get_auth_status() : false,
502 + 'can_manage_ai_auth' => current_user_can( 'manage_options' ),
503 + 'google_fonts' => \CartFlows_Font_Families::get_google_fonts(),
504 + 'system_fonts' => \CartFlows_Font_Families::get_system_fonts(),
505 + 'font_weights' => array(
483 506 '100' => __( 'Thin 100', 'cartflows' ),
484 507 '200' => __( 'Extra-Light 200', 'cartflows' ),
485 508 '300' => __( 'Light 300', 'cartflows' ),
486 509 '400' => __( 'Normal 400', 'cartflows' ),
@@ -489,30 +512,44 @@
489 512 '700' => __( 'Bold 700', 'cartflows' ),
490 513 '800' => __( 'Extra-Bold 800', 'cartflows' ),
491 514 '900' => __( 'Ultra-Bold 900', 'cartflows' ),
492 515 ),
493 - 'global_checkout_id' => $global_checkout_id ? absint( $global_checkout_id ) : '',
494 - 'flow_action' => $flow_action,
495 - 'step_action' => $step_action,
496 - 'old_global_checkout' => get_option( '_cartflows_old_global_checkout', false ),
497 - 'woopayments_status' => $this->get_plugin_status( 'woocommerce-payments/woocommerce-payments.php' ),
498 - 'ca_status' => $this->get_plugin_status( 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php' ),
499 - 'suretriggers_status' => $this->get_plugin_status( 'suretriggers/suretriggers.php' ),
500 - 'moderncart_status' => $this->get_plugin_status( 'modern-cart/modern-cart.php' ),
501 - 'cpsw_connection_status' => 'success' === get_option( 'cpsw_test_con_status', false ) || 'success' === get_option( 'cpsw_con_status', false ),
502 - 'current_user_can_manage_cartflows' => current_user_can( 'cartflows_manage_settings' ),
503 - 'is_set_report_email_ids' => get_option( 'cartflows_stats_report_email_ids', false ),
504 - 'cf_docs_data' => get_option( 'cartflows_docs_data', false ),
505 - 'woo_order_url' => $order_url,
506 - 'integrations' => $this->get_recommendation_integrations(),
507 - 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
508 - 'instant_checkout_notice_status' => \Cartflows_Helper::get_admin_settings_option( 'wcf-instant-checkout-notice-skipped', false, false ),
509 - 'cartflows_admin_notices' => $this->cartflows_admin_notices(),
510 - 'whats_new_rss_feed' => $this->get_whats_new_rss_feeds_data(),
511 - 'cartflows_current_version' => CARTFLOWS_VER,
512 - 'cartflows_previous_versions' => \Cartflows_Helper::get_rollback_versions_options(),
513 - 'rollback_url' => esc_url( add_query_arg( 'version', 'VERSION', wp_nonce_url( admin_url( 'admin-post.php?action=cartflows_rollback' ), 'cartflows_rollback' ) ) ),
514 - 'utm_param_pro_plans' => 'utm_source=carflows-dashboard&utm_medium=free-cartflows&utm_campaign=go-pro',
516 + 'global_checkout_id' => $global_checkout_id ? absint( $global_checkout_id ) : '',
517 + 'flow_action' => $flow_action,
518 + 'step_action' => $step_action,
519 + 'old_global_checkout' => get_option( '_cartflows_old_global_checkout', false ),
520 + 'woopayments_status' => $this->get_plugin_status( 'woocommerce-payments/woocommerce-payments.php' ),
521 + 'ca_status' => $this->get_plugin_status( 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php' ),
522 + 'suretriggers_status' => $this->get_plugin_status( 'suretriggers/suretriggers.php' ),
523 + 'moderncart_status' => $this->get_plugin_status( 'modern-cart/modern-cart.php' ),
524 + 'cpsw_connection_status' => 'success' === get_option( 'cpsw_test_con_status', false ) || 'success' === get_option( 'cpsw_con_status', false ),
525 + 'current_user_can_manage_cartflows' => current_user_can( 'cartflows_manage_settings' ),
526 + 'is_set_report_email_ids' => get_option( 'cartflows_stats_report_email_ids', false ),
527 + 'woo_order_url' => $order_url,
528 + 'integrations' => $this->get_recommendation_integrations(),
529 + 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
530 + 'instant_checkout_notice_status' => \Cartflows_Helper::get_admin_settings_option( 'wcf-instant-checkout-notice-skipped', false, false ),
531 + 'cartflows_admin_notices' => $this->cartflows_admin_notices(),
532 + 'whats_new_rss_feed' => $this->get_whats_new_rss_feeds_data(),
533 + 'cartflows_current_version' => CARTFLOWS_VER,
534 + 'cartflows_previous_versions' => \Cartflows_Helper::get_rollback_versions_options(),
535 + 'rollback_url' => esc_url( add_query_arg( 'version', 'VERSION', wp_nonce_url( admin_url( 'admin-post.php?action=cartflows_rollback' ), 'cartflows_rollback' ) ) ),
536 + 'utm_defaults' => \Cartflows_Helper::get_utm_defaults(),
537 + /**
538 + * Filters whether to hide already-added products from the checkout product search dropdown.
539 + *
540 + * When `true`, products already present in the current checkout step's product list are
541 + * filtered out of the search results, preventing duplicate selection through the UI.
542 + *
543 + * Note: this is a UI-level restriction only. The server-side save path (REST/AJAX) is
544 + * not gated by this filter, so duplicates submitted directly to the save endpoint will
545 + * still be persisted.
546 + *
547 + * @since x.x.x
548 + *
549 + * @param bool $restrict Whether to hide already-added products in the search dropdown. Default false.
550 + */
551 + 'restrict_duplicate_checkout_products' => apply_filters( 'cartflows_restrict_duplicate_checkout_products', false ),
515 552 )
516 553 );
517 554
518 555 // Load the codemirror for code editor.
@@ -792,8 +829,17 @@
792 829 array(),
793 830 CARTFLOWS_VER
794 831 );
795 832
833 + // Store Checkout renders the canvas inside this app too, so it needs the React Flow base styles.
834 + wp_register_style(
835 + 'wcf-canvas-base-style',
836 + $build_url . 'style-editor-app.css',
837 + array(),
838 + CARTFLOWS_VER
839 + );
840 + wp_enqueue_style( 'wcf-canvas-base-style' );
841 +
796 842 wp_enqueue_script( $handle );
797 843
798 844 wp_set_script_translations( $handle, 'cartflows' );
799 845
@@ -841,8 +887,17 @@
841 887 array(),
842 888 CARTFLOWS_VER
843 889 );
844 890
891 + wp_register_style(
892 + 'wcf-canvas-base-style',
893 + $build_url . 'style-editor-app.css',
894 + array(),
895 + CARTFLOWS_VER
896 + );
897 + // Enqueue the registered style.
898 + wp_enqueue_style( 'wcf-canvas-base-style' );
899 +
845 900 wp_enqueue_script( $handle );
846 901
847 902 wp_set_script_translations( $handle, 'cartflows' );
848 903
@@ -1149,10 +1204,19 @@
1149 1204
1150 1205 // Fetch the RSS feed from the URL. This saves us from the CORS issue.
1151 1206 $feed = wp_remote_retrieve_body( wp_safe_remote_get( 'https://cartflows.com/product/cartflows/feed/' ) ); // phpcs:ignore -- This is a valid use case cannot use VIP rules here.
1152 1207
1153 - // Security: Set proper content type header and strip script tags to prevent XSS.
1154 - echo $feed; // phpcs:ignore -- RSS feed content sanitized via wp_kses_post.
1208 + /*
1209 + * This is an RSS document, parsed as XML by the What's New client. It must not be
1210 + * run through wp_kses_post() — that strips the feed's own elements and leaves the
1211 + * client nothing to parse. Declaring the type is what stops a browser sniffing the
1212 + * response as HTML; the body itself is fetched over HTTPS from our own domain and
1213 + * this endpoint is behind a nonce and a capability check.
1214 + */
1215 + header( 'Content-Type: application/rss+xml; charset=' . get_option( 'blog_charset' ) );
1216 + header( 'X-Content-Type-Options: nosniff' );
1217 +
1218 + echo $feed; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- RSS/XML body, see above.
1155 1219 exit;
1156 1220 }
1157 1221 }
1158 1222