| @@ -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'; |
| @@ -443,8 +459,9 @@ | ||
| 443 | 459 | 'required_plugins_data' => $this->get_required_plugins_data(), |
| 444 | 460 | 'is_any_required_plugins_missing' => $this->get_any_required_plugins_status(), |
| 445 | 461 | 'admin_base_slug' => $this->menu_slug, |
| 446 | 462 | 'admin_base_url' => admin_url(), |
| 463 | + 'is_onboarding_completed' => '1' === get_option( 'wcf_setup_complete', false ), | |
| 447 | 464 | 'title_length' => apply_filters( |
| 448 | 465 | 'cartflows_flows_steps_title_length', |
| 449 | 466 | array( |
| 450 | 467 | 'max' => 50, |
| @@ -474,10 +491,16 @@ | ||
| 474 | 491 | 'cf_font_family' => AdminHelper::get_font_family(), |
| 475 | 492 | 'flows_and_steps' => ! empty( $flows_and_steps ) ? $flows_and_steps : '', |
| 476 | 493 | 'store_checkout_flows_and_steps' => \Cartflows_Helper::get_instance()->get_flows_and_steps( '', 'store-checkout' ), |
| 477 | 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, | |
| 478 | 499 | 'template_library_url' => wcf()->get_site_url(), |
| 479 | 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' ), | |
| 480 | 503 | 'google_fonts' => \CartFlows_Font_Families::get_google_fonts(), |
| 481 | 504 | 'system_fonts' => \CartFlows_Font_Families::get_system_fonts(), |
| 482 | 505 | 'font_weights' => array( |
| 483 | 506 | '100' => __( 'Thin 100', 'cartflows' ), |
| @@ -500,9 +523,8 @@ | ||
| 500 | 523 | 'moderncart_status' => $this->get_plugin_status( 'modern-cart/modern-cart.php' ), |
| 501 | 524 | 'cpsw_connection_status' => 'success' === get_option( 'cpsw_test_con_status', false ) || 'success' === get_option( 'cpsw_con_status', false ), |
| 502 | 525 | 'current_user_can_manage_cartflows' => current_user_can( 'cartflows_manage_settings' ), |
| 503 | 526 | 'is_set_report_email_ids' => get_option( 'cartflows_stats_report_email_ids', false ), |
| 504 | - 'cf_docs_data' => get_option( 'cartflows_docs_data', false ), | |
| 505 | 527 | 'woo_order_url' => $order_url, |
| 506 | 528 | 'integrations' => $this->get_recommendation_integrations(), |
| 507 | 529 | 'plugin_installer_nonce' => wp_create_nonce( 'updates' ), |
| 508 | 530 | 'instant_checkout_notice_status' => \Cartflows_Helper::get_admin_settings_option( 'wcf-instant-checkout-notice-skipped', false, false ), |
| @@ -510,9 +532,9 @@ | ||
| 510 | 532 | 'whats_new_rss_feed' => $this->get_whats_new_rss_feeds_data(), |
| 511 | 533 | 'cartflows_current_version' => CARTFLOWS_VER, |
| 512 | 534 | 'cartflows_previous_versions' => \Cartflows_Helper::get_rollback_versions_options(), |
| 513 | 535 | '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', | |
| 536 | + 'utm_defaults' => \Cartflows_Helper::get_utm_defaults(), | |
| 515 | 537 | /** |
| 516 | 538 | * Filters whether to hide already-added products from the checkout product search dropdown. |
| 517 | 539 | * |
| 518 | 540 | * When `true`, products already present in the current checkout step's product list are |
| @@ -807,8 +829,17 @@ | ||
| 807 | 829 | array(), |
| 808 | 830 | CARTFLOWS_VER |
| 809 | 831 | ); |
| 810 | 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 | + | |
| 811 | 842 | wp_enqueue_script( $handle ); |
| 812 | 843 | |
| 813 | 844 | wp_set_script_translations( $handle, 'cartflows' ); |
| 814 | 845 | |
| @@ -1173,10 +1204,19 @@ | ||
| 1173 | 1204 | |
| 1174 | 1205 | // Fetch the RSS feed from the URL. This saves us from the CORS issue. |
| 1175 | 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. |
| 1176 | 1207 | |
| 1177 | - // Security: Set proper content type header and strip script tags to prevent XSS. | |
| 1178 | - 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. | |
| 1179 | 1219 | exit; |
| 1180 | 1220 | } |
| 1181 | 1221 | } |
| 1182 | 1222 | |