query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 'DELETE FROM `' . esc_sql( $wpdb->prefix . 'accessiq_clicks' ) . '` WHERE created_at < NOW() - INTERVAL %d DAY', $days ) ); $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 'DELETE FROM `' . esc_sql( $wpdb->prefix . 'accessiq_sessions' ) . '` WHERE created_at < NOW() - INTERVAL %d DAY', $days ) ); $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 'DELETE FROM `' . esc_sql( $wpdb->prefix . 'accessiq_page_views' ) . '` WHERE created_at < NOW() - INTERVAL %d DAY', $days ) ); } add_action( 'accessiq_cleanup_old_data', 'accessiq_run_analytics_cleanup' ); /** * Main Plugin Class */ final class AccessIQ { private static $instance = null; public static function instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } private function __construct() { add_action( 'plugins_loaded', array( $this, 'init' ) ); } public static function activate() { self::create_tables(); flush_rewrite_rules(); if ( ! get_option( 'accessiq_settings' ) ) { add_option( 'accessiq_settings', self::default_settings() ); } if ( ! wp_next_scheduled( 'accessiq_cleanup_old_data' ) ) { wp_schedule_event( time(), 'daily', 'accessiq_cleanup_old_data' ); } set_transient( 'accessiq_show_welcome', true, 30 ); } private static function create_tables() { global $wpdb; $charset = $wpdb->get_charset_collate(); require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $issues_table = $wpdb->prefix . 'accessiq_issues'; dbDelta( "CREATE TABLE IF NOT EXISTS {$issues_table} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, scan_id BIGINT(20) UNSIGNED NOT NULL, wcag_id VARCHAR(20) NOT NULL, level VARCHAR(5) NOT NULL, element TEXT NOT NULL, issue_type VARCHAR(100) NOT NULL, severity VARCHAR(20) NOT NULL DEFAULT 'error', suggestion TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY scan_id (scan_id) ) {$charset};" ); $clicks_table = $wpdb->prefix . 'accessiq_clicks'; dbDelta( "CREATE TABLE IF NOT EXISTS {$clicks_table} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, page_url VARCHAR(255) NOT NULL, x_percent DECIMAL(5,2) NOT NULL, y_percent DECIMAL(5,2) NOT NULL, element VARCHAR(255) NOT NULL, device_type VARCHAR(50) NOT NULL, session_id VARCHAR(100) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (id), KEY session_id (session_id) ) {$charset};" ); $sessions_table = $wpdb->prefix . 'accessiq_sessions'; dbDelta( "CREATE TABLE IF NOT EXISTS {$sessions_table} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_id VARCHAR(100) NOT NULL, page_url VARCHAR(255) NOT NULL, time_spent INT(11) UNSIGNED DEFAULT 0 NOT NULL, is_bounce TINYINT(1) DEFAULT 1 NOT NULL, referrer VARCHAR(255) DEFAULT '' NOT NULL, device_type VARCHAR(50) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (id), UNIQUE KEY session_id (session_id) ) {$charset};" ); $pv_table = $wpdb->prefix . 'accessiq_page_views'; dbDelta( "CREATE TABLE IF NOT EXISTS {$pv_table} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_id VARCHAR(64) NOT NULL DEFAULT '', page_url VARCHAR(500) NOT NULL DEFAULT '', page_title VARCHAR(300) NOT NULL DEFAULT '', time_spent INT(11) NOT NULL DEFAULT 0, scroll_depth TINYINT(3) NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY session_id (session_id), KEY created_at (created_at) ) {$charset};" ); $dashboard_table = $wpdb->prefix . 'accessiq_dashboard'; dbDelta( "CREATE TABLE IF NOT EXISTS {$dashboard_table} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT(20) UNSIGNED NOT NULL, widget_key VARCHAR(100) NOT NULL, position INT(11) NOT NULL DEFAULT 0, is_visible TINYINT(1) NOT NULL DEFAULT 1, config LONGTEXT NOT NULL, PRIMARY KEY (id), KEY user_id (user_id) ) {$charset};" ); } public static function default_settings() { return array( 'widget_enabled' => true, 'widget_position' => 'bottom-right', 'widget_color' => '#1d4ed8', 'show_on_mobile' => true, 'widget_features' => array( 'font_size', 'contrast', 'dyslexia_font', 'keyboard_nav' ), 'scanner_enabled' => true, 'scan_level' => 'AA', 'notify_email' => get_option( 'admin_email' ), 'analytics_enable_click_tracking' => true, 'analytics_enable_behavior_tracking' => true, 'analytics_exclude_logged_in' => false, 'analytics_exclude_admin' => true, 'analytics_replace_dashboard' => false, 'analytics_dashboard_roles' => array( 'administrator' ), 'analytics_data_retention' => 90, ); } public function register_cpt() { register_post_type( ACCESSIQ_SCAN_CPT, array( 'public' => false, 'show_ui' => false, 'show_in_rest' => false, 'supports' => array( 'title', 'custom-fields' ), 'labels' => array( 'name' => 'AccessIQ Scans' ), ) ); } public function init() { // DB upgrade check — runs on every plugins_loaded, no-ops when version matches. if ( get_option( 'accessiq_db_version', '1.0' ) !== ACCESSIQ_DB_VERSION ) { self::create_tables(); update_option( 'accessiq_db_version', ACCESSIQ_DB_VERSION ); } add_action( 'init', array( $this, 'register_cpt' ) ); AccessIQ_Settings::instance(); AccessIQ_Widget::instance(); AccessIQ_Scanner::instance(); AccessIQ_Scheduler::instance(); $accessiq_heatmap = new AccessIQ_Heatmap_Tracker(); $accessiq_heatmap->init(); $accessiq_behavior = new AccessIQ_Behavior_Tracker(); $accessiq_behavior->init(); $accessiq_dashboard = new AccessIQ_Dashboard_Builder(); $accessiq_dashboard->init(); if ( is_admin() ) { $accessiq_analytics_menu = new AccessIQ_Analytics_Menu(); $accessiq_analytics_menu->init(); } } } AccessIQ::instance();