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();