15 )); if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) { if (!$wp_filesystem->is_dir($target_dir)) { $wp_filesystem->mkdir($target_dir, FS_CHMOD_DIR); } $saved = $wp_filesystem->put_contents($target_dir . $local_filename, wp_remote_retrieve_body($response)); if (!$saved) { $all_extensions_downloaded = false; break; } } else { $all_extensions_downloaded = false; break; } } if ($all_extensions_downloaded) { $download_successful = true; $downloaded_locale = $attempt_locale; break; } } // Determine notice type and save to transient if ($download_successful) { update_option('aiflt_translation_status_' . $locale, 'verified'); $notice_type = ($downloaded_locale === $locale) ? 'exact_success' : 'fallback_success'; set_transient('aiflt_lang_notice', array( 'type' => $notice_type, 'original' => $locale, 'fallback' => $downloaded_locale ), 60); } else { update_option('aiflt_translation_status_' . $locale, 'failed'); set_transient('aiflt_lang_notice', array( 'type' => 'failed', 'original' => $locale, ), 300); } } function aiflt_get_fallback_locale($locale) { $base_lang = substr($locale, 0, 2); // Comprehensive fallback map based on standard WordPress locales $fallbacks = array( 'ar' => 'ar', // Arabic 'cs' => 'cs_CZ', // Czech 'de' => 'de_DE', // German 'el' => 'el', // Greek 'en' => 'en_US', // English 'es' => 'es_ES', // Spanish 'fa' => 'fa_IR', // Persian 'fr' => 'fr_FR', // French 'hu' => 'hu_HU', // Hungarian 'id' => 'id_ID', // Indonesian 'it' => 'it_IT', // Italian 'ja' => 'ja_JP', // Japanese 'ko' => 'ko_KR', // Korean 'ms' => 'ms_MY', // Malay 'nl' => 'nl_NL', // Dutch 'pl' => 'pl_PL', // Polish 'pt' => 'pt_BR', // Brazil 'ro' => 'ro_RO', // Romanian 'ru' => 'ru_RU', // Russian 'sv' => 'sv', // Swedish 'th' => 'th_TH', // Thai 'tl' => 'tl_PH', // Filipino 'tr' => 'tr_TR', // Turkish 'zh' => 'zh_CN' // Chinese (Simplified) ); if (isset($fallbacks[$base_lang]) && $fallbacks[$base_lang] !== $locale) { return $fallbacks[$base_lang]; } return false; } // add_action('admin_notices', 'aiflt_display_language_download_notice'); function aiflt_display_language_download_notice() { // Only show to users who can manage the site if (!current_user_can('manage_options')) { return; } $notice = get_transient('aiflt_lang_notice'); if (!$notice) { return; } // Clear the transient immediately so it only shows once delete_transient('aiflt_lang_notice'); $type = $notice['type']; $original = esc_html($notice['original']); if ($type === 'exact_success') { echo '
'; echo '

' . esc_html(__('AI Flash Tune', 'ai-flash-tune')) . ': ' . sprintf(esc_html(__('Language files for %s successfully downloaded.', 'ai-flash-tune')), '' . $original . '') . '

'; echo '
'; } elseif ($type === 'fallback_success') { $fallback = esc_html($notice['fallback']); echo '
'; echo '

' . esc_html(__('AI Flash Tune', 'ai-flash-tune')) . ': ' . sprintf(esc_html(__('Alternate language file downloaded. We tried to find %1$s, but downloaded %2$s as a fallback.', 'ai-flash-tune')), '' . $original . '', '' . $fallback . '') . '

'; echo '
'; } } /** * Check if WooCommerce is active. * * @return bool */ function aiflt_is_woocommerce_active() { return class_exists('WooCommerce'); } function aiflt_get_woocommerce_status() { $wc_plugin_path = WP_PLUGIN_DIR . '/woocommerce/woocommerce.php'; $aiflt_install_url = admin_url('plugin-install.php?s=woocommerce&tab=search&type=term'); $activate_url = wp_nonce_url(admin_url('plugins.php?action=activate&plugin=woocommerce%2Fwoocommerce.php'), 'activate-plugin_woocommerce/woocommerce.php'); if (file_exists($wc_plugin_path)) { return [ 'status' => 'installed', 'button_text' => esc_html__('Activate WooCommerce Now', 'ai-flash-tune'), 'action_url' => esc_url($activate_url), 'notice_text' => esc_html__('WooCommerce is installed but not currently active. Please activate it to enable this page.', 'ai-flash-tune') ]; } else { return [ 'status' => 'not_installed', 'button_text' => esc_html__('Install WooCommerce Now', 'ai-flash-tune'), 'action_url' => esc_url($aiflt_install_url), 'notice_text' => esc_html__('This page requires WooCommerce to be installed and active. Please install it to continue.', 'ai-flash-tune') ]; } } /** * Display a custom, well-designed admin notice if WooCommerce is not active. */ function aiflt_woocommerce_missing_notice() { $aiflt_install_url = admin_url('plugin-install.php?s=woocommerce&tab=search&type=term'); ?>
MODIFIED: Only add WooCommerce-dependent hooks if it's active if (aiflt_is_woocommerce_active()) { add_action('woocommerce_thankyou', array('AIFLT_Funnel_Analyzer_Logic', 'log_checkout_attempt'), 10, 1); add_action('woocommerce_order_status_processing', array('AIFLT_Funnel_Analyzer_Logic', 'log_confirmed_sale'), 10, 1); add_action('woocommerce_order_status_completed', array('AIFLT_Funnel_Analyzer_Logic', 'log_confirmed_sale'), 10, 1); add_action('woocommerce_order_status_cancelled', array('AIFLT_Funnel_Analyzer_Logic', 'remove_confirmed_sale'), 10, 1); add_action('woocommerce_order_status_failed', array('AIFLT_Funnel_Analyzer_Logic', 'remove_confirmed_sale'), 10, 1); add_action('woocommerce_order_status_refunded', array('AIFLT_Funnel_Analyzer_Logic', 'remove_confirmed_sale'), 10, 1); add_action('wp_enqueue_scripts', 'aiflt_enqueue_frontend_scripts'); // ---> Add the server-side hook to reliably track all 'add to cart' actions. // 1. Hook for Item Addition (The original hook) add_action('woocommerce_add_to_cart', array('AIFLT_Funnel_Analyzer_Logic', 'log_add_to_cart_action'), 10, 6); // 2. Hook for Item Quantity Update (New hook for the same function) add_action('woocommerce_after_cart_item_quantity_update', array('AIFLT_Funnel_Analyzer_Logic', 'log_add_to_cart_action'), 10, 3); } // Add AJAX hooks (these are safe as their callbacks will also have checks) add_action('wp_ajax_aiflt_track_funnel_page', array('AIFLT_Funnel_Analyzer_Logic', 'track_funnel_page_callback')); add_action('wp_ajax_nopriv_aiflt_track_funnel_page', array('AIFLT_Funnel_Analyzer_Logic', 'track_funnel_page_callback')); add_action('wp_ajax_aiflt_save_page_speed', array('AIFLT_Funnel_Analyzer_Logic', 'save_page_speed_callback')); add_action('wp_ajax_nopriv_aiflt_save_page_speed', array('AIFLT_Funnel_Analyzer_Logic', 'save_page_speed_callback')); add_action('wp_ajax_aiflt_log_behavior', array('AIFLT_Funnel_Analyzer_Logic', 'log_behavior_callback')); add_action('wp_ajax_nopriv_aiflt_log_behavior', array('AIFLT_Funnel_Analyzer_Logic', 'log_behavior_callback')); add_action('wp_ajax_aiflt_track_simple_behavior', array('AIFLT_Funnel_Analyzer_Logic','ajax_track_simple_behavior')); add_action('wp_ajax_nopriv_aiflt_track_simple_behavior', array('AIFLT_Funnel_Analyzer_Logic','ajax_track_simple_behavior')); add_action('wp_ajax_aiflt_get_ai_analysis', array('AIFLT_Funnel_Analyzer_Logic', 'get_ai_analysis_callback')); add_action('wp_ajax_aiflt_get_funnel_data', array('AIFLT_Funnel_Analyzer_Logic', 'get_funnel_data_callback')); add_action('wp_ajax_aiflt_save_settings', array('AIFLT_Settings_Logic', 'save_settings_callback')); // --- NEW: Add AJAX action for capturing guest emails --- add_action('wp_ajax_nopriv_aiflt_capture_guest_email', array('AIFLT_Funnel_Analyzer_Logic', 'ajax_capture_guest_email')); add_action('wp_ajax_aiflt_capture_guest_email', array('AIFLT_Funnel_Analyzer_Logic', 'ajax_capture_guest_email')); // ---> ADD THIS NEW AJAX ACTION add_action('wp_ajax_aiflt_manage_addon', array('AIFLT_License_Manager', 'install_and_activate_addon_ajax')); add_action('wp_ajax_aiflt_export_privacy_data', array('AIFLT_Privacy_Logic', 'ajax_export_data')); add_action('wp_ajax_aiflt_get_segmented_funnel_data', ['AIFLT_Funnel_Analyzer_Logic', 'ajax_get_segmented_data']); add_action('wp_ajax_aiflt_test_ai_provider', 'aiflt_ajax_test_ai_provider'); } function aiflt_ajax_test_ai_provider() { check_ajax_referer('aiflt-test-ai-provider-nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Permission denied.', 'ai-flash-tune')]); } if (!class_exists('AIFLT_AI_Service')) { wp_send_json_error(['message' => __('AI service is not loaded.', 'ai-flash-tune')]); } $result = AIFLT_AI_Service::test_provider(); if (in_array($result['status'], ['success', 'local'], true)) { wp_send_json_success($result); } wp_send_json_error($result); } // ---> MODIFIED: Removed the gate. The plugin will now always initialize. add_action('plugins_loaded', 'aiflt_initialize_plugin'); add_filter('cron_schedules', 'aiflt_add_weekly_cron_schedule'); /** * Detect AI Flash Tune admin screens for plugin-specific admin chrome. * * This is intentionally page-slug based so it also covers add-on screens * registered under the AI Flash Tune menu. * * @return bool */ function aiflt_is_plugin_admin_page() { if (!is_admin()) { return false; } $core_pages = [ 'ai_flash_tune', 'ai-flash-tune', 'funnelanalysis', 'products', 'product-detail', 'abandonedcarts', 'aiflt-setup-wizard', 'aiflt-growth-tools', 'aiflt-ai-usage-reports', 'aiflt_settings', 'privacy-tools', 'addon-manager', 'aiflt-help', 'upgrade-to-pro', ]; $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; if ($page !== '') { if (in_array($page, $core_pages, true) || strpos($page, 'aift') === 0) { return true; } } if (function_exists('get_current_screen')) { $screen = get_current_screen(); if ($screen && !empty($screen->id)) { $screen_id = (string) $screen->id; $screen_fragments = [ 'toplevel_page_ai_flash_tune', 'toplevel_page_ai-flash-tune', 'ai_flash_tune_page_', 'ai-flash-tune_page_', '_page_funnelanalysis', '_page_products', '_page_product-detail', '_page_abandonedcarts', '_page_aiflt-setup-wizard', '_page_aiflt-growth-tools', '_page_aiflt-ai-usage-reports', '_page_aiflt_settings', '_page_privacy-tools', '_page_addon-manager', '_page_aiflt-help', '_page_upgrade-to-pro', '_page_aift', 'admin_page_aift', ]; foreach ($screen_fragments as $fragment) { if (strpos($screen_id, $fragment) !== false) { return true; } } } } return false; } /** * Replace the WordPress version footer on AI Flash Tune screens only. * * The standard admin footer shows the WordPress version. On branded AI Flash * Tune screens this looked like a plugin version mismatch, so we show the * current AI Flash Tune version instead. * * @param string $text Existing update footer text. * @return string */ function aiflt_admin_update_footer($text) { if (!aiflt_is_plugin_admin_page()) { return $text; } return sprintf( /* translators: %s: AI Flash Tune plugin version. */ esc_html__('AI Flash Tune %s', 'ai-flash-tune'), esc_html(AIFLT_VERSION) ); } add_filter('update_footer', 'aiflt_admin_update_footer', PHP_INT_MAX); /** * JavaScript fallback for admin footer text when WordPress/core/admin plugins * overwrite update_footer after our filter. * * @return void */ function aiflt_admin_footer_version_fallback() { if (!aiflt_is_plugin_admin_page()) { return; } $footer_text = sprintf( /* translators: %s: AI Flash Tune plugin version. */ __('AI Flash Tune %s', 'ai-flash-tune'), AIFLT_VERSION ); ?> WEEK_IN_SECONDS, 'display' => __('Once Weekly', 'ai-flash-tune'), ]; } return $schedules; } function aiflt_activate_plugin() { // Set a transient flag for the welcome redirect. set_transient('aiflt_welcome_redirect', true, 30); // Schedule the daily cleanup event if it's not already scheduled. if (!wp_next_scheduled('aiflt_daily_cleanup_event')) { wp_schedule_event(time(), 'daily', 'aiflt_daily_cleanup_event'); } if (!wp_next_scheduled('aiflt_weekly_license_check_event')) { wp_schedule_event(time() + HOUR_IN_SECONDS, 'weekly', 'aiflt_weekly_license_check_event'); } if (class_exists('AIFLT_World_Class_Tools')) { AIFLT_World_Class_Tools::maybe_create_tables(); AIFLT_World_Class_Tools::reschedule_weekly_email(); } global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // Create the funnel data table $aiflt_funnel_data_table = $wpdb->prefix . 'aiflt_funnel_data'; $sql_funnel_data = "CREATE TABLE $aiflt_funnel_data_table ( id bigint(20) NOT NULL AUTO_INCREMENT, session_id varchar(255) NOT NULL, user_id bigint(20) DEFAULT NULL, guest_email varchar(255) DEFAULT NULL, page_name varchar(50) NOT NULL, device_type varchar(20) NOT NULL, timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, cart_data longtext, page_speed float DEFAULT NULL, post_id bigint(20) DEFAULT NULL, order_id bigint(20) DEFAULT NULL, campaign_id varchar(100) DEFAULT NULL, ip_address VARCHAR(45) NULL, PRIMARY KEY (id), KEY session_id (session_id), KEY page_name (page_name), KEY timestamp (timestamp) ) $charset_collate;"; // Create the behavior data table $behavior_data_table = $wpdb->prefix . 'aiflt_behavior_data'; $sql_behavior_data = "CREATE TABLE $behavior_data_table ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_id VARCHAR(64) NOT NULL, user_id BIGINT(20) UNSIGNED NULL, product_id BIGINT(20) UNSIGNED DEFAULT 0, page_url TEXT NULL, behavior_type VARCHAR(50) NOT NULL, behavior_value TEXT NULL, traffic_source VARCHAR(50) DEFAULT 'Unknown', user_agent VARCHAR(255) NULL, ip_address VARCHAR(45) NULL, location_data TEXT NULL, timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY product_id (product_id), KEY behavior_type (behavior_type), KEY session_id (session_id) ) $charset_collate;"; add_option('aiflt_db_version', AIFLT_DB_VERSION); // Store activation timestamp if it doesn't exist if (false === get_option('aiflt_activation_time')) { add_option('aiflt_activation_time', time()); } require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql_funnel_data); dbDelta($sql_behavior_data); require_once AIFLT_PLUGIN_PATH . 'includes/AIFLT_License_Manager.php'; if (class_exists('AIFLT_License_Manager')) { AIFLT_License_Manager::maybe_create_activation_table(); } require_once AIFLT_PLUGIN_PATH . 'includes/AIFLT_AI_Usage_Log.php'; if (class_exists('AIFLT_AI_Usage_Log')) { AIFLT_AI_Usage_Log::maybe_create_table(); } require_once AIFLT_PLUGIN_PATH . 'includes/AIFLT_AI_Report_Log.php'; if (class_exists('AIFLT_AI_Report_Log')) { AIFLT_AI_Report_Log::maybe_create_table(); } require_once AIFLT_PLUGIN_PATH . 'includes/AIFLT_World_Class_Tools.php'; if (class_exists('AIFLT_World_Class_Tools')) { AIFLT_World_Class_Tools::maybe_create_tables(); AIFLT_World_Class_Tools::reschedule_weekly_email(); } update_option('aiflt_db_version', AIFLT_DB_VERSION); } function aiflt_deactivate_plugin() { // Clear the scheduled cron job on deactivation. $timestamp = wp_next_scheduled('aiflt_daily_cleanup_event'); if ($timestamp) { wp_unschedule_event($timestamp, 'aiflt_daily_cleanup_event'); } $weekly_timestamp = wp_next_scheduled('aiflt_weekly_summary_email_event'); if ($weekly_timestamp) { wp_unschedule_event($weekly_timestamp, 'aiflt_weekly_summary_email_event'); } $license_weekly_timestamp = wp_next_scheduled('aiflt_weekly_license_check_event'); if ($license_weekly_timestamp) { wp_unschedule_event($license_weekly_timestamp, 'aiflt_weekly_license_check_event'); } } function aiflt_welcome_redirect_handler() { // Check if our transient is set. if (get_transient('aiflt_welcome_redirect')) { // Delete the transient so this process only runs once. delete_transient('aiflt_welcome_redirect'); // Ensure the current user has permissions before redirecting. if (current_user_can('manage_options')) { // Perform a safe redirect to the main plugin dashboard. wp_safe_redirect(admin_url('admin.php?page=ai_flash_tune')); exit; } } } function aiflt_run_daily_cleanup_cron() { // --- REVISED: Read the correct individual database option --- $retention_days = get_option('aiflt_data_retention', 30); $retention_days = intval($retention_days); // A value of 0 means 'Forever', so we only run if it's a positive number. if ($retention_days > 0) { global $wpdb; $funnel_table = $wpdb->prefix . 'aiflt_funnel_data'; $behavior_table = $wpdb->prefix . 'aiflt_behavior_data'; // Calculate the cutoff date $cutoff_date = gmdate('Y-m-d H:i:s', strtotime("-$retention_days days")); // Delete old data from the funnel table $wpdb->query( $wpdb->prepare( "DELETE FROM {$funnel_table} WHERE timestamp < %s", $cutoff_date ) ); // Delete old data from the behavior table $wpdb->query( $wpdb->prepare( "DELETE FROM {$behavior_table} WHERE timestamp < %s", $cutoff_date ) ); } } /** * Register the core plugin settings and their sanitization callbacks. * Hooked to admin_init. */ function aiflt_register_core_settings() { // Register settings for the 'general' tab group // Each setting needs its own register_setting call. // The first argument is the 'option group' name used in settings_fields(). // The second argument is the actual 'option name' stored in wp_options. // The third argument is an array for arguments like sanitize_callback. register_setting('aiflt_settings_group_general', 'aiflt_ai_provider', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_key', 'default' => 'local' ]); register_setting('aiflt_settings_group_general', 'aiflt_ai_external_consent', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 0 ]); register_setting('aiflt_settings_group_general', 'aiflt_ai_aggregate_only', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 1 ]); register_setting('aiflt_settings_group_general', 'aiflt_free_ai_monthly_limit', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 20 ]); register_setting('aiflt_settings_group_general', 'aiflt_ai_api_key', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ]); register_setting('aiflt_settings_group_general', 'aiflt_zywrap_api_key', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ]); // Zywrap proxy endpoint is fixed in code: https://api.zywrap.com/v1/proxy // Developers can override it with AIFLT_ZYWRAP_PROXY_ENDPOINT or the aiflt_zywrap_proxy_endpoint filter. register_setting('aiflt_settings_group_general', 'aiflt_pro_license_key', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ]); register_setting('aiflt_settings_group_general', 'aiflt_ai_model', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => 'gpt-4o-mini' ]); register_setting('aiflt_settings_group_general', 'aiflt_ai_response_language', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => 'auto' ]); register_setting('aiflt_settings_group_general', 'aiflt_data_retention', [ 'type' => 'string', // Stored as string, but represents number or '0' 'sanitize_callback' => 'sanitize_text_field', 'default' => '30' ]); register_setting('aiflt_settings_group_general', 'aiflt_delete_data_on_uninstall', [ 'type' => 'integer', // Checkbox value is 1 or 0 'sanitize_callback' => 'absint', 'default' => 0 ]); register_setting('aiflt_settings_group_general', 'aiflt_anonymize_ip', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 1 ]); register_setting('aiflt_settings_group_general', 'aiflt_enable_tracking', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 1 ]); register_setting('aiflt_settings_group_general', 'aiflt_excluded_roles', [ 'type' => 'array', 'sanitize_callback' => 'aiflt_sanitize_roles_array', // Use your existing sanitize function 'default' => ['administrator', 'shop_manager'] ]); register_setting('aiflt_settings_group_general', 'aiflt_excluded_ips', [ 'type' => 'string', 'sanitize_callback' => 'aiflt_sanitize_ip_textarea', // Use your existing sanitize function 'default' => '' ]); // Hook for addons to register their settings (using their own option groups) do_action('aiflt_register_addon_settings'); } /** * Helper sanitize callback for roles array (already exists in ai-flash-tune.php) */ function aiflt_sanitize_roles_array($input) { $sanitized = []; if (is_array($input)) { foreach ($input as $role) { // Ensure only valid role keys are saved if (get_role($role)) { $sanitized[] = sanitize_key($role); } } } return $sanitized; } /** * Helper sanitize callback for IP textarea (already exists in ai-flash-tune.php) */ function aiflt_sanitize_ip_textarea($input) { $ips_array = explode("\n", trim($input)); $sanitized_ips = []; foreach ($ips_array as $ip) { $trimmed_ip = trim($ip); if (!empty($trimmed_ip)) { // Basic sanitization, could add IP validation if needed $sanitized_ips[] = sanitize_text_field($trimmed_ip); } } // Store as a single string separated by newlines return implode("\n", $sanitized_ips); } function aiflt_register_admin_menu() { add_menu_page( __('AI Flash Tune', 'ai-flash-tune'), __('AI Flash Tune', 'ai-flash-tune'), 'manage_options', 'ai_flash_tune', 'aiflt_render_dashboard_page', 'dashicons-chart-bar', // Using a standard dashicon instead of a separate image 25 ); add_submenu_page( 'ai_flash_tune', __('Revenue Leak Scan', 'ai-flash-tune'), __('Revenue Leak Scan', 'ai-flash-tune'), 'manage_options', 'ai_flash_tune', 'aiflt_render_dashboard_page' ); add_submenu_page( 'ai_flash_tune', __('Funnel Analysis', 'ai-flash-tune'), __('Funnel Analysis', 'ai-flash-tune'), 'manage_options', 'funnelanalysis', 'aiflt_render_funnel_analysis_page' ); add_submenu_page( 'ai_flash_tune', __('Product Performance', 'ai-flash-tune'), __('Product Performance', 'ai-flash-tune'), 'manage_options', 'products', 'aiflt_render_products_page' ); add_submenu_page( 'ai_flash_tune_hide', __('Product Detail', 'ai-flash-tune'), __('Product Detail', 'ai-flash-tune'), 'manage_options', 'product-detail', 'aiflt_render_product_detail_page' ); add_submenu_page( 'ai_flash_tune', __('Abandoned Carts', 'ai-flash-tune'), __('Abandoned Carts', 'ai-flash-tune'), 'manage_options', 'abandonedcarts', 'aiflt_render_abandoned_carts_page' ); // This action hook allows addons to register their own submenu pages. do_action('aiflt_register_submenu_pages'); add_submenu_page( 'ai_flash_tune', __('Setup Wizard', 'ai-flash-tune'), __('Setup Wizard', 'ai-flash-tune'), 'manage_options', 'aiflt-setup-wizard', 'aiflt_render_setup_wizard_page' ); add_submenu_page( 'ai_flash_tune', __('Agency & Growth Toolkit', 'ai-flash-tune'), __('Agency & Growth Toolkit', 'ai-flash-tune'), 'manage_options', 'aiflt-growth-tools', 'aiflt_render_growth_tools_page' ); add_submenu_page( 'ai_flash_tune', __('Reports & Exports', 'ai-flash-tune'), __('Reports & Exports', 'ai-flash-tune'), 'manage_options', 'aiflt-ai-usage-reports', 'aiflt_render_ai_usage_reports_page' ); add_submenu_page( 'ai_flash_tune', __('Settings & AI Keys', 'ai-flash-tune'), __('Settings & AI Keys', 'ai-flash-tune'), 'manage_options', 'aiflt_settings', 'aiflt_render_settings_page' ); // ---> ADD THIS NEW SUBMENU PAGE add_submenu_page( 'ai_flash_tune', __('Privacy Tools', 'ai-flash-tune'), __('Privacy Tools', 'ai-flash-tune'), 'manage_options', 'privacy-tools', 'aiflt_render_privacy_tools_page' ); add_submenu_page( 'ai_flash_tune', __('Modules & License', 'ai-flash-tune'), __('Modules & License', 'ai-flash-tune'), 'manage_options', 'addon-manager', 'aiflt_render_addon_manager_page' ); add_submenu_page( 'ai_flash_tune', __('Diagnostics & QA', 'ai-flash-tune'), __('Diagnostics & QA', 'ai-flash-tune'), 'manage_options', 'aiflt-help', 'aiflt_render_help_page' ); add_submenu_page( 'ai_flash_tune', __('Plans & Upgrade', 'ai-flash-tune'), __('Plans & Upgrade', 'ai-flash-tune'), 'manage_options', 'upgrade-to-pro', 'aiflt_render_upgrade_to_pro_page' ); } /** * Hide AI Flash Tune's native WordPress submenu entries from the left admin menu. * * AI Flash Tune uses a dedicated in-plugin sidebar for its real navigation. Keeping * WordPress' expanded native submenu visible creates a duplicated navigation stack * and can make screenshots look broken. The submenu pages remain registered and * accessible by direct admin.php?page=... URLs; this only removes the duplicate * items from the native WordPress left menu. */ function aiflt_hide_native_admin_submenu() { // Do not alter the global $submenu array. WordPress uses those registered // submenu entries for admin page access checks. Removing them can cause // valid admin.php?page=... URLs to show "Sorry, you are not allowed to // access this page." // // The duplicate native submenu is hidden visually through CSS that is // enqueued only on AI Flash Tune admin screens. This keeps all pages // registered and accessible while avoiding double navigation in the UI. return; } add_action('admin_menu', 'aiflt_hide_native_admin_submenu', 999); /** * Handles the manual triggering of cron jobs from the System Info page. */ function aiflt_handle_manual_cron_trigger() { if (isset($_GET['trigger_cron']) && isset($_GET['_wpnonce'])) { if (!current_user_can('manage_options')) { return; } if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'aiflt_trigger_cron')) { return; } $cron_type = sanitize_key(wp_unslash($_GET['trigger_cron'])); if ($cron_type === 'cleanup') { // Run the cleanup function manually aiflt_run_daily_cleanup_cron(); // Add a success notice and redirect add_action('admin_notices', function() { echo '

' . esc_html__('The daily data cleanup job has been run successfully.', 'ai-flash-tune') . '

'; }); } // We can add more cron triggers here in the future } } // Callback functions to render the pages function aiflt_render_privacy_tools_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/privacy-tools.php'); } function aiflt_render_dashboard_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/dashboard.php'); } function aiflt_render_funnel_analysis_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/funnel-analysis.php'); } function aiflt_render_products_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/products.php'); } function aiflt_render_product_detail_page() { // CRITICAL FIX: Add explicit capability check here. if (!current_user_can('manage_options')) { wp_die(esc_html__('Sorry, you are not allowed to access this page.', 'ai-flash-tune')); } include(AIFLT_PLUGIN_PATH . 'includes/admin/product-detail.php'); } function aiflt_render_abandoned_carts_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/abandoned-carts.php'); } function aiflt_render_setup_wizard_page() { include_once(AIFLT_PLUGIN_PATH . 'includes/admin/setup-wizard.php'); } function aiflt_render_growth_tools_page() { include_once(AIFLT_PLUGIN_PATH . 'includes/admin/growth-tools.php'); } function aiflt_render_ai_usage_reports_page() { include_once(AIFLT_PLUGIN_PATH . 'includes/admin/ai-usage-reports.php'); } function aiflt_render_settings_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/settings.php'); } function aiflt_render_addon_manager_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/addon-manager.php'); } function aiflt_render_upgrade_to_pro_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/upgrade-to-pro.php'); } function aiflt_render_help_page() { include(AIFLT_PLUGIN_PATH . 'includes/admin/help.php'); } // Enqueue scripts and styles for the admin side add_action('admin_enqueue_scripts', 'aiflt_enqueue_admin_scripts'); function aiflt_enqueue_admin_scripts($hook) { // Get the current screen to check which page we are on $screen = get_current_screen(); $is_aiflt_page = false; // --- Define known addon slugs/page fragments --- // These should match the `$menu_slug` used in the add_submenu_page call for each addon $addon_slugs = [ 'aiftcr-email-log', 'aiftcr-recovery-center', 'aiftac-action-center', 'aiftpo-product-optimizer', 'aiftas-segmentation', 'aiftal-alert-center', 'aiftrp-reports', 'aiftco-optimizer-dashboard', 'aiftaa-analysis-dashboard', 'aiftss-suggestions-dashboard', 'aiftpd-doctor-dashboard', 'product-detail', ]; // --- Determine if we are on ANY AIFLT admin page (Core or Addon) --- if ($screen) { $screen_id = $screen->id; $core_slug_fragments = [ '_page_funnelanalysis', '_page_products', '_page_abandonedcarts', '_page_aiflt_settings', '_page_privacy-tools', '_page_addon-manager', '_page_aiflt-help', '_page_upgrade-to-pro', '_page_aiftal-alert-center', '_page_aiftas-segmentation', '_page_aiflt-growth-tools', '_page_aiftrp-reports', '_page_aiftco-optimizer-dashboard', '_page_aiftcr-recovery-center', '_page_aiftpo-product-optimizer', '_page_aiftac-action-center', '_page_aiflt-setup-wizard', 'ai_flash_tune_page_', 'ai-flash-tune_page_', ]; $is_core_page = ($screen_id === 'toplevel_page_ai_flash_tune'); foreach ($core_slug_fragments as $fragment) { if (strpos($screen_id, $fragment) !== false) { $is_core_page = true; break; } } $is_addon_page = false; foreach ($addon_slugs as $aiflt_slug) { // Check common addon patterns: admin_page_{slug} OR {parent_hook}_page_{slug} if ($screen_id === 'admin_page_' . $aiflt_slug || $screen_id === 'ai-flash-tune_page_' . $aiflt_slug) { $is_addon_page = true; break; } } $is_aiflt_page = $is_core_page || $is_addon_page; // error_log("AI Flash Tune Screen Check - ID: " . $screen_id . " | Is Core: " . ($is_core_page ? 'Yes' : 'No'). " | Is Addon: " . ($is_addon_page ? 'Yes' : 'No')); // Keep for debugging if needed } else { // Fallback check using $hook (less reliable) - Keep this broad $known_slug_fragments = ['ai_flash_tune', 'ai-flash-tune', 'aiflt_settings', 'funnelanalysis', 'products', 'product-detail', 'abandonedcarts', 'privacy-tools', 'addon-manager', 'aiflt-help', 'upgrade-to-pro']; $known_slug_fragments = array_merge($known_slug_fragments, $addon_slugs); // Include addon slugs in fallback foreach ($known_slug_fragments as $fragment) { if (strpos($hook, $fragment) !== false) { $is_aiflt_page = true; break; } } // error_log("AI Flash Tune Hook Check - Hook: " . $hook . " | Is AIFLT Page: " . ($is_aiflt_page ? 'Yes' : 'No')); } // --- STEP 1: Enqueue basic styles on ALL identified AIFLT pages --- if ($is_aiflt_page) { wp_enqueue_style('ai-flash-tune-styles', AIFLT_PLUGIN_URL . 'includes/css/ai-flash-tune-styles.css', [], AIFLT_VERSION); wp_enqueue_style('aift-fontawesome', AIFLT_PLUGIN_URL . 'includes/css/all.min.css', [], '5.15.4'); // Keep handle simple for potential sharing } else { // If it's definitely not our page, bail early return; } // --- STEP 2: Check WC dependency for JS and specific assets --- if (!aiflt_is_woocommerce_active()) { // If WC is not active, we've already enqueued basic styles (if applicable), so just return. return; } // --- STEP 3: Enqueue JS and specific assets ONLY on specific pages AND if WC is active --- // Define the specific screen IDs that need JS, flatpickr, etc. // **CRITICAL:** Combine CORE and ADDON screen IDs correctly. $aiflt_pages_needing_js = [ // Core Pages 'toplevel_page_ai-flash-tune', 'toplevel_page_ai_flash_tune', 'ai-flash-tune_page_funnelanalysis', '_page_funnelanalysis', 'ai-flash-tune_page_products', '_page_products', 'ai-flash-tune_page_product-detail', 'admin_page_product-detail', '_product-detail', 'ai-flash-tune_page_abandonedcarts', '_page_abandonedcarts', 'ai-flash-tune_page_aiflt_settings', '_page_aiflt_settings', 'ai-flash-tune_page_privacy-tools', '_page_privacy-tools', 'ai-flash-tune_page_addon-manager', '_page_addon-manager', 'ai-flash-tune_page_upgrade-to-pro', '_page_upgrade-to-pro', // Addon Pages (Use the correct pattern identified via debugging, likely admin_page_{slug} or ai-flash-tune_page_{slug}) // Adjust these based on EXACT screen IDs found during debugging 'ai-flash-tune_page_aiftcr-email-log', // Example assuming it's under main menu 'admin_page_aiftcr-email-log', // Alternative pattern if registered differently 'ai-flash-tune_page_aiftaa-analysis-dashboard', // Example assuming it's under main menu 'admin_page_aiftaa-analysis-dashboard', // Alternative pattern 'ai-flash-tune_page_aiftss-suggestions-dashboard', // Example assuming it's under main menu 'admin_page_aiftss-suggestions-dashboard', // Alternative pattern 'ai-flash-tune_page_aiftco-optimizer-dashboard', // Example assuming it's under main menu 'admin_page_aiftco-optimizer-dashboard', // Alternative pattern 'ai-flash-tune_page_aiftpd-doctor-dashboard', // Example assuming it's under main menu // 'admin_page_aiftpd-doctor-dashboard', // Alternative pattern // Add ALL other addon screen IDs here ]; // Check if the current screen ID is in the list of pages needing JS if ($screen && ( strpos($screen->id, '_page_products') != 0 || strpos($screen->id, '_page_ai-flash-tune') != 0 || strpos($screen->id, '_page_ai_flash_tune') != 0 || strpos($screen->id, '_page_funnelanalysis') != 0 || strpos($screen->id, '_page_product-detail') != 0 || strpos($screen->id, '_page_abandonedcarts') != 0 || strpos($screen->id, '_page_aiflt_settings') != 0 || strpos($screen->id, '_page_privacy-tools') != 0 || strpos($screen->id, '_page_aiftcr-email-log') != 0 || strpos($screen->id, '_page_aiftaa-analysis-dashboard') != 0 || strpos($screen->id, '_page_aiftss-suggestions-dashboard') != 0 || strpos($screen->id, '_page_aiftco-optimizer-dashboard') != 0 || strpos($screen->id, '_page_aiftpd-doctor-dashboard') != 0 || strpos($screen->id, '_page_aiftac-action-center') != 0 || strpos($screen->id, '_page_addon-manager') != 0 || strpos($screen->id, '_page_aiflt-help') != 0 || strpos($screen->id, '_page_upgrade-to-pro') != 0 || strpos($screen->id, '_page_aiftal-alert-center') != 0 || strpos($screen->id, '_page_aiftas-segmentation') != 0 || strpos($screen->id, '_page_aiflt-growth-tools') != 0 || strpos($screen->id, '_page_aiftrp-reports') != 0 || strpos($screen->id, '_page_aiftcr-recovery-center') != 0 || strpos($screen->id, '_page_aiftpo-product-optimizer') != 0 || strpos($screen->id, '_page_aiflt-setup-wizard') != 0 || strpos($screen->id, 'ai_flash_tune_page_') != 0 || strpos($screen->id, 'ai_flash_tune_page_') != 0 )) { // Enqueue Flatpickr for date range selection on relevant pages $flatpickr_pages = [ 'ai-flash-tune_page_products', // Use correct ID pattern 'ai-flash-tune_page_product-detail', // Use correct ID pattern 'admin_page_product-detail', // Use correct ID pattern 'ai-flash-tune_page_funnelanalysis', // Use correct ID pattern 'ai-flash-tune_page_abandonedcarts', // Use correct ID pattern 'ai-flash-tune_page_aiftcr-email-log', // Use correct ID pattern 'admin_page_aiftcr-email-log', // Use correct ID pattern ]; if (in_array($screen->id, $flatpickr_pages)) { wp_enqueue_script('aift-flatpickr-js', AIFLT_PLUGIN_URL .'includes/js/flatpickr.min.js', [], '4.6.13', true); wp_enqueue_style('aift-flatpickr-css', AIFLT_PLUGIN_URL .'includes/css/flatpickr.min.css', [], '4.6.13'); } // Enqueue the main jQuery script wp_enqueue_script('ai-flash-tune-main-js', AIFLT_PLUGIN_URL . 'includes/js/ai-flash-tune-main.js', ['jquery'], AIFLT_VERSION, true); // Prepare data to pass to JavaScript (wp_localize_script remains the same as previous step) $aiflt_start_date = isset($_GET['start_date']) ? sanitize_text_field(wp_unslash($_GET['start_date'])) : gmdate('Y-m-d', strtotime('-29 days')); $aiflt_end_date = isset($_GET['end_date']) ? sanitize_text_field(wp_unslash($_GET['end_date'])) : gmdate('Y-m-d'); $aiflt_current_device = isset($_GET['device']) ? sanitize_key(wp_unslash($_GET['device'])) : 'desktop'; $aiflt_current_device = in_array($aiflt_current_device, ['all', 'desktop', 'mobile'], true) ? $aiflt_current_device : 'desktop'; $activation_time = get_option('aiflt_activation_time', time()); $min_date = gmdate('Y-m-d', $activation_time); // Pass PHP data to the JavaScript file (using aiflt_ajax_object and corrected nonce actions) wp_localize_script('ai-flash-tune-main-js', 'aiflt_ajax_object', [ // Use the corrected object name here 'ajax_url' => admin_url('admin-ajax.php'), // Ensure ALL these nonce actions match the check_ajax_referer() calls 'tracking_nonce' => wp_create_nonce('aiflt-tracking-nonce'), // For frontend 'ai_analysis_nonce' => wp_create_nonce('aiflt-ai-analysis-nonce'), // Core AI Ask 'settings_nonce' => wp_create_nonce('aiflt-save-settings-nonce'), // Core Settings Save 'get_funnel_nonce' => wp_create_nonce('aiflt-get-funnel-nonce'), // Core Get Funnel Data 'addon_management_nonce' => wp_create_nonce('aiflt-addon-management-nonce'), // Addon Install/Activate // Keep addon nonces specific for clarity, assuming checks use these specific actions 'aiftaa_nonce' => wp_create_nonce('aiftaa-generate-report-nonce'), // AI Analysis Addon 'smart_suggestions_nonce' => wp_create_nonce('aiftss-get-suggestion-nonce'), // Smart Suggestions Addon 'aiftco_nonce' => wp_create_nonce('aiftco-get-analysis-nonce'), // Checkout Optimizer Addon 'aiftap_nonce' => wp_create_nonce('aiftap-get-prediction-nonce'), // Abandonment Predictor Addon 'aiftpd_nonce' => wp_create_nonce('aiftpd-get-diagnosis-nonce'), // Product Doctor Addon 'aiftce_nonce' => wp_create_nonce('aiftce-get-copy-suggestion-nonce'), // Copy Enhancer Addon 'aiftfs_nonce' => wp_create_nonce('aiftfs-get-segmented-nonce'), // Funnel Segmentation Addon 'aiftcr_test_email_nonce' => wp_create_nonce('aiftcr-test-email-nonce'), // Cart Recovery Test Email 'aiftac_nonce' => wp_create_nonce('aiftac-action-center-nonce'), 'aiftpo_nonce' => wp_create_nonce('aiftpo-product-optimizer-nonce'), 'aiftcr_nonce' => wp_create_nonce('aiftcr-recovery-center-nonce'), 'aiftas_nonce' => wp_create_nonce('aiftas-segmentation-nonce'), 'aiftal_nonce' => wp_create_nonce('aiftal-alert-center-nonce'), 'aiftrp_nonce' => wp_create_nonce('aiftrp-reports-nonce'), 'aiflt_test_ai_provider_nonce' => wp_create_nonce('aiflt-test-ai-provider-nonce'), 'privacy_nonce' => wp_create_nonce('aiflt_privacy_actions'), // <-- NEW NONCE // KEEP other localized variables 'current_device_type' => $aiflt_current_device, 'start_date' => $aiflt_start_date, 'end_date' => $aiflt_end_date, 'min_date_limit' => $min_date, // Pass the formatted date 'is_admin' => true, 'admin_base_url' => admin_url('admin.php'), 'i18n' => [ 'saving' => __('Saving...', 'ai-flash-tune'), 'saveSettings' => __('Save Settings', 'ai-flash-tune'), 'saveError' => __('Error: Could not save settings.', 'ai-flash-tune'), 'unknownError' => __('An unexpected error occurred. Please try again.', 'ai-flash-tune'), 'sending' => __('Sending...', 'ai-flash-tune'), // For test email // Dynamic Addon Action Verbs (for Modules & License) 'installing' => __('Installing', 'ai-flash-tune'), 'activating' => __('Activating', 'ai-flash-tune'), 'deactivating' => __('Deactivating', 'ai-flash-tune'), // Diagnosis & Suggestions (AIFT, AIFTSS, AIFTPD) 'diagnosis' => __('Diagnosis', 'ai-flash-tune'), 'confidence' => __('Confidence Score', 'ai-flash-tune'), 'whySuggestion' => __('Why this suggestion?', 'ai-flash-tune'), 'recommendations' => __('Recommendations', 'ai-flash-tune'), 'aiAnalyzingSuggestions' => __('Our AI is analyzing your data to generate suggestions...', 'ai-flash-tune'), 'aiDiagnosingProduct' => __('AI is diagnosing the product...', 'ai-flash-tune'), 'aiGeneratingCopy' => __('Generating copy...', 'ai-flash-tune'), // AI Analysis & Checkout (AIFTAA, AIFTCO) 'generating' => __('Generating...', 'ai-flash-tune'), 'analyzing' => __('Analyzing...', 'ai-flash-tune'), 'aiAnalyzingData' => __('Our AI is analyzing your data...', 'ai-flash-tune'), 'aiAnalyzingCheckout' => __('Our AI is analyzing your checkout process...', 'ai-flash-tune'), // Abandonment Predictor (AIFTAP) 'predicting' => __('Predicting...', 'ai-flash-tune'), 'aiAnalyzingPrediction' => __('AI is analyzing your data...', 'ai-flash-tune'), // Funnel Messages 'loadingFunnel' => __('Loading Funnel Data...', 'ai-flash-tune'), 'noFunnelData' => __('Funnel tracking is ready, but this range needs activity', 'ai-flash-tune'), 'noFunnelTraffic' => __('Run one guest test: visit a product page, add it to cart, open checkout, then refresh this report. Real shopper activity will appear here after it is recorded.', 'ai-flash-tune'), 'noFunnelRange' => __('No funnel events were recorded in this date range. Expand the date range or run a guest product → cart → checkout test to confirm tracking.', 'ai-flash-tune'), 'funnelError' => __('Error loading funnel data.', 'ai-flash-tune'), 'funnelNetworkError' => __('Error loading funnel data. Please check your network connection.', 'ai-flash-tune'), // Ask AI 'aiPromptEmpty' => __('Please enter a question for the AI.', 'ai-flash-tune'), 'aiThinking' => __('Thinking...', 'ai-flash-tune'), 'aiNetworkError' => __('Error: Could not retrieve AI response. Please check your network connection.', 'ai-flash-tune'), 'noSegmentData' => __('No data available for this segment in the selected date range.', 'ai-flash-tune'), 'segmentA' => __('Segment A', 'ai-flash-tune'), 'segmentB' => __('Segment B', 'ai-flash-tune'), 'getAiDiagnosis' => __('Get AI Diagnosis', 'ai-flash-tune'), 'landingPage' => __('Landing Page', 'ai-flash-tune'), 'productPage' => __('Product Page', 'ai-flash-tune'), 'addedToCart' => __('Added to Cart', 'ai-flash-tune'), 'checkoutPage' => __('Checkout Page', 'ai-flash-tune'), 'unknownError' => __('An unknown error occurred.', 'ai-flash-tune'), 'users' => __('Users', 'ai-flash-tune'), 'loadingSegmentedData...' => __('Loading segmented data...', 'ai-flash-tune'), 'dropOff' => __('Drop-Off', 'ai-flash-tune'), 'visitors' => __('Visitors', 'ai-flash-tune'), 'start' => __('Start', 'ai-flash-tune'), 'conversion' => __('Conversion', 'ai-flash-tune'), 'dropOffCartTooltip' => __('Percentage of users who left after adding to the Cart.', 'ai-flash-tune'), 'dropOffProductTooltip' => __('Percentage of users who left after showing Product Interest.', 'ai-flash-tune'), 'dropOffLandingTooltip' => __('Percentage of users who left after the Landing Page.', 'ai-flash-tune'), 'sales' => __('Sales', 'ai-flash-tune'), 'checkouts' => __('Checkouts', 'ai-flash-tune'), 'carts' => __('Carts', 'ai-flash-tune'), 'guestUsers' => __('Guest Users', 'ai-flash-tune'), 'registeredUsers' => __('Registered Users', 'ai-flash-tune'), 'desktop' => __('Desktop', 'ai-flash-tune'), 'mobile' => __('Mobile', 'ai-flash-tune'), 'purchaseComplete' => __('Purchase Complete', 'ai-flash-tune'), 'productInterest' => __('Product Interest', 'ai-flash-tune'), 'views' => __('Views', 'ai-flash-tune'), 'entries' => __('Entries', 'ai-flash-tune'), ], ]); } } // Enqueue scripts and styles for the front end add_action('wp_enqueue_scripts', 'aiflt_enqueue_frontend_scripts'); function aiflt_enqueue_frontend_scripts() { // --- REVISED: Load settings from their correct individual database options --- $settings = [ 'enable_tracking' => get_option('aiflt_enable_tracking', '1'), 'excluded_roles' => get_option('aiflt_excluded_roles', ['administrator', 'shop_manager']), 'excluded_ips' => get_option('aiflt_excluded_ips', ''), ]; // 1. Master switch check (This logic now works correctly) if (empty($settings['enable_tracking']) || $settings['enable_tracking'] !== '1') { return; } // 2. User role check if (is_user_logged_in()) { $user = wp_get_current_user(); $user_roles = (array) $user->roles; $excluded_roles = isset($settings['excluded_roles']) ? (array) $settings['excluded_roles'] : []; if (!empty(array_intersect($user_roles, $excluded_roles))) { return; // Exit if the user has an excluded role } } // 3. IP address check $user_ip = sanitize_text_field($_SERVER['REMOTE_ADDR']); $excluded_ips_raw = isset($settings['excluded_ips']) ? $settings['excluded_ips'] : ''; if (!empty($excluded_ips_raw)) { // Normalize line endings and create an array of IPs $excluded_ips = array_map('trim', explode("\n", str_replace("\r", "", $excluded_ips_raw))); if (in_array($user_ip, $excluded_ips)) { return; // Exit if the user's IP is in the exclusion list } } // If all checks pass, proceed to enqueue the script if (class_exists('WooCommerce')) { wp_enqueue_script('ai-flash-tune-frontend-js', AIFLT_PLUGIN_URL . 'includes/js/ai-flash-tune-main.js', array('jquery'), AIFLT_VERSION, true); // ---> Pass the consistent session ID to the JavaScript. wp_localize_script('ai-flash-tune-frontend-js', 'aiflt_ajax_object', array( 'ajax_url' => admin_url('admin-ajax.php'), 'tracking_nonce' => wp_create_nonce('aiflt-tracking-nonce'), // Verify this action name matches checks 'is_admin' => false, // Add this flag for frontend pages 'session_id' => AIFLT_Funnel_Analyzer_Logic::get_session_id(), 'cart_data' => AIFLT_Funnel_Analyzer_Logic::get_woocommerce_cart_data(), // Use simplified data for frontend )); } }