table_name = $wpdb->prefix . 'ai_monitor_logs'; register_activation_hook(__FILE__, array($this, 'activate')); register_deactivation_hook(__FILE__, array($this, 'deactivate')); add_action('plugins_loaded', array($this, 'init')); add_action('admin_init', array($this, 'run_upgrade_routine')); add_action('admin_notices', array($this, 'display_upgrade_errors')); } public function init() { $this->load_dependencies(); add_action('init', array($this, 'track_visitor'), 1); add_action('almaweb_daily_cleanup', array($this, 'run_cleanup')); add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); } private function load_dependencies() { require_once ALMAWEB_PLUGIN_DIR . 'includes/class-ip-checker.php'; require_once ALMAWEB_PLUGIN_DIR . 'includes/class-bot-detector.php'; require_once ALMAWEB_PLUGIN_DIR . 'includes/class-referrer-detector.php'; require_once ALMAWEB_PLUGIN_DIR . 'includes/class-database.php'; require_once ALMAWEB_PLUGIN_DIR . 'includes/class-business-kpis.php'; require_once ALMAWEB_PLUGIN_DIR . 'admin/class-admin-dashboard.php'; require_once ALMAWEB_PLUGIN_DIR . 'admin/class-admin-settings.php'; } public function activate() { // Pour les nouvelles installations, on crée la table avec la version la plus récente du schéma $this->create_complete_database_table(); $this->set_default_options(); $this->schedule_cleanup(); flush_rewrite_rules(); } public function deactivate() { wp_clear_scheduled_hook('almaweb_daily_cleanup'); flush_rewrite_rules(); } /** * Crée la table de base de données avec le schéma complet pour les nouvelles installations. */ public function create_complete_database_table() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} ( id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, user_agent varchar(500) NOT NULL, ip_address varchar(100) NOT NULL, bot_name varchar(200) DEFAULT NULL, bot_type varchar(100) DEFAULT NULL, bot_family varchar(100) DEFAULT NULL, bot_intention varchar(50) DEFAULT NULL, is_ai_bot tinyint(1) DEFAULT 0, is_stealth tinyint(1) DEFAULT 0, detection_method varchar(50) DEFAULT 'user_agent', request_url text NOT NULL, request_method varchar(10) NOT NULL, referer text, country varchar(100), city varchar(100), action_type varchar(50) DEFAULT 'visit', page_visited text, time_spent int(11) DEFAULT 0, http_accept text, http_accept_language varchar(100), http_accept_encoding varchar(100), visited_at datetime DEFAULT '0000-00-00 00:00:00', ai_referrer_source varchar(200) DEFAULT NULL, ai_referrer_type varchar(50) DEFAULT NULL, ai_referrer_family varchar(100) DEFAULT NULL, is_from_ai tinyint(1) DEFAULT 0, session_id varchar(100) DEFAULT NULL, session_duration int(11) DEFAULT 0, pages_in_session int(11) DEFAULT 1, is_bounce tinyint(1) DEFAULT 1, is_return_visitor tinyint(1) DEFAULT 0, has_conversion tinyint(1) DEFAULT 0, conversion_type varchar(50) DEFAULT NULL, conversion_value decimal(10,2) DEFAULT NULL, PRIMARY KEY (id), KEY ip_address (ip_address), KEY bot_name (bot_name), KEY is_ai_bot (is_ai_bot), KEY is_stealth (is_stealth), KEY detection_method (detection_method), KEY visited_at (visited_at), KEY bot_type (bot_type), KEY bot_family (bot_family), KEY bot_intention (bot_intention), KEY ai_referrer_source (ai_referrer_source), KEY ai_referrer_family (ai_referrer_family), KEY is_from_ai (is_from_ai), KEY session_id (session_id), KEY has_conversion (has_conversion) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); update_option('almaweb_db_version', ALMAWEB_VERSION); } private function set_default_options() { if (!get_option('almaweb_settings')) { $default_options = ['track_enabled' => true, 'track_registered_users' => false, 'retention_days' => 90]; add_option('almaweb_settings', $default_options); } } public function run_upgrade_routine() { $db_version = get_option('almaweb_db_version', '1.0.0'); if (version_compare($db_version, ALMAWEB_VERSION, '<')) { if (version_compare($db_version, '1.1.0', '<')) { $this->run_manual_schema_update_to_1_1_0(); } update_option('almaweb_db_version', ALMAWEB_VERSION); } } public function display_upgrade_errors() { $error = get_transient('almaweb_upgrade_error'); if ($error) { echo '

AlmaWeb AI Visitor Analytics Upgrade Error:
' . esc_html($error) . '

'; delete_transient('almaweb_upgrade_error'); } } private function run_manual_schema_update_to_1_1_0() { global $wpdb; $table_name = $this->table_name; $wpdb->suppress_errors(false); $columns_to_add = [ 'ai_referrer_source' => "VARCHAR(200) NULL DEFAULT NULL", 'ai_referrer_type' => "VARCHAR(50) NULL DEFAULT NULL", 'ai_referrer_family' => "VARCHAR(100) NULL DEFAULT NULL", 'is_from_ai' => "TINYINT(1) NULL DEFAULT 0", 'session_id' => "VARCHAR(100) NULL DEFAULT NULL", 'session_duration' => "INT(11) NULL DEFAULT 0", 'pages_in_session' => "INT(11) NULL DEFAULT 1", 'is_bounce' => "TINYINT(1) NULL DEFAULT 1", 'is_return_visitor' => "TINYINT(1) NULL DEFAULT 0", 'has_conversion' => "TINYINT(1) NULL DEFAULT 0", 'conversion_type' => "VARCHAR(50) NULL DEFAULT NULL", 'conversion_value' => "DECIMAL(10,2) NULL DEFAULT NULL" ]; $raw_columns_query = $wpdb->get_results("DESCRIBE {$table_name}"); if (!$raw_columns_query) { return; } $existing_columns = wp_list_pluck($raw_columns_query, 'Field'); foreach ($columns_to_add as $column_name => $column_definition) { if (!in_array($column_name, $existing_columns)) { $wpdb->query("ALTER TABLE {$table_name} ADD COLUMN `{$column_name}` {$column_definition}"); if (!empty($wpdb->last_error)) { set_transient('almaweb_upgrade_error', "Failed to add column '{$column_name}'. Error: " . $wpdb->last_error, 120); return; } } } $keys_to_add = ['ai_referrer_source', 'ai_referrer_family', 'is_from_ai', 'session_id', 'has_conversion']; foreach ($keys_to_add as $key_name) { $key_exists = $wpdb->get_var($wpdb->prepare("SHOW INDEX FROM {$table_name} WHERE Key_name = %s", $key_name)); if (!$key_exists) { $wpdb->query("ALTER TABLE {$table_name} ADD KEY `{$key_name}` (`{$key_name}`)"); if (!empty($wpdb->last_error)) { set_transient('almaweb_upgrade_error', "Failed to add index '{$key_name}'. Error: " . $wpdb->last_error, 120); return; } } } } public function track_visitor() { $settings = get_option('almaweb_settings'); if (empty($settings['track_enabled']) || ((defined('DOING_AJAX') && DOING_AJAX) || is_admin()) || (empty($settings['track_registered_users']) && is_user_logged_in())) { return; } $bot_detector = new Almaweb_Bot_Detector(); $visitor_data = $bot_detector->analyze_request(); $ai_referrer = false; $referrer_detector = new Almaweb_Referrer_Detector(); // 1. Détection via utm_source (prioritaire) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Tracking parameter, read-only $utm_source = isset($_GET['utm_source']) ? sanitize_text_field(wp_unslash($_GET['utm_source'])) : ''; if (!empty($utm_source)) { $ai_referrer = $referrer_detector->detect_ai_from_utm_source($utm_source); } // 2. Si non détecté via utm_source, détection via HTTP_REFERER if (!$ai_referrer) { $referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])) : ''; if (!empty($referer)) { $ai_referrer = $referrer_detector->detect_ai_referrer($referer); } } // Si un référent AI est détecté, enrichir les données du visiteur if ($ai_referrer) { $visitor_data = array_merge($visitor_data, [ 'ai_referrer_source' => $ai_referrer['name'], 'ai_referrer_type' => $ai_referrer['type'], 'ai_referrer_family' => $ai_referrer['family'], 'is_from_ai' => true, ]); } $database = new Almaweb_Database(); $database->log_visit($visitor_data); } public function add_admin_menu() { add_menu_page(__('AlmaWeb AI Visitor Analytics', 'almaweb-ai-visitor-analytics'), __('AI Analytics', 'almaweb-ai-visitor-analytics'), 'manage_options', 'almaweb-ai-visitor-analytics', array($this, 'render_dashboard'), 'dashicons-visibility', 30); add_submenu_page('almaweb-ai-visitor-analytics', __('Dashboard', 'almaweb-ai-visitor-analytics'), __('Dashboard', 'almaweb-ai-visitor-analytics'), 'manage_options', 'almaweb-ai-visitor-analytics', array($this, 'render_dashboard')); // Sous-menu Sources IA add_submenu_page( 'almaweb-ai-visitor-analytics', __('AI Sources', 'almaweb-ai-visitor-analytics'), __('AI Sources', 'almaweb-ai-visitor-analytics'), 'manage_options', 'almaweb-ai-visitor-analytics-referrers', array($this, 'render_referrers') ); add_submenu_page('almaweb-ai-visitor-analytics', __('Logs', 'almaweb-ai-visitor-analytics'), __('Logs', 'almaweb-ai-visitor-analytics'), 'manage_options', 'almaweb-ai-visitor-analytics-logs', array($this, 'render_logs')); add_submenu_page('almaweb-ai-visitor-analytics', __('Settings', 'almaweb-ai-visitor-analytics'), __('Settings', 'almaweb-ai-visitor-analytics'), 'manage_options', 'almaweb-ai-visitor-analytics-settings', array($this, 'render_settings')); } public function enqueue_admin_assets($hook) { if (strpos($hook, 'almaweb-ai-visitor-analytics') === false) return; wp_enqueue_style('almaweb-ai-visitor-analytics-admin', ALMAWEB_PLUGIN_URL . 'assets/css/admin.css', array(), ALMAWEB_VERSION); wp_enqueue_script('chartjs', ALMAWEB_PLUGIN_URL . 'assets/js/vendor/chart.min.js', array(), '4.5.1', true); wp_enqueue_script('almaweb-ai-visitor-analytics-admin', ALMAWEB_PLUGIN_URL . 'assets/js/admin.js', array('chartjs'), ALMAWEB_VERSION, true); } public function render_dashboard() { $dashboard = new Almaweb_Admin_Dashboard(); $dashboard->render(); $this->render_footer(); } public function render_logs() { require_once ALMAWEB_PLUGIN_DIR . 'admin/views/logs.php'; $this->render_footer(); } public function render_settings() { $settings = new Almaweb_Admin_Settings(); $settings->render(); $this->render_footer(); } public function render_referrers() { require_once ALMAWEB_PLUGIN_DIR . 'admin/views/referrers.php'; $this->render_footer(); } public function render_footer() { /* translators: %s: Link to AlmaWeb website. */ echo ''; } private function schedule_cleanup() { if (!wp_next_scheduled('almaweb_daily_cleanup')) wp_schedule_event(time(), 'daily', 'almaweb_daily_cleanup'); } public function run_cleanup() { $settings = get_option('almaweb_settings'); $retention_days = isset($settings['retention_days']) ? intval($settings['retention_days']) : 90; $database = new Almaweb_Database(); $database->cleanup_old_logs($retention_days); } } function almaweb_ai_visitor_analytics() { return almaweb_ai_visitor_analytics::get_instance(); } almaweb_ai_visitor_analytics();