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' ); /** * Compliance Badge Shortcode — [accessiq_badge level="AA" style="dark"] * Outputs an inline WCAG compliance badge, optionally linked to the statement page. */ add_shortcode( 'accessiq_badge', 'accessiq_compliance_badge' ); function accessiq_compliance_badge( $atts ) { $atts = shortcode_atts( array( 'level' => 'AA', 'style' => 'dark' ), $atts, 'accessiq_badge' ); $level = in_array( $atts['level'], array( 'A', 'AA', 'AAA' ), true ) ? $atts['level'] : 'AA'; $dark = ( 'dark' === $atts['style'] ); $bg = $dark ? '#0f172a' : '#ffffff'; $border = '#10d98e'; $text = $dark ? '#e2e8f0' : '#0f172a'; $stmt_page_id = (int) get_option( 'accessiq_statement_page_id', 0 ); $stmt_url = $stmt_page_id ? get_permalink( $stmt_page_id ) : ''; $badge = sprintf( '' . '' . 'WCAG 2.1 %4$s %5$s', esc_attr( $bg ), esc_attr( $border ), esc_attr( $text ), esc_html( $level ), esc_html__( 'Compliant', 'accessiq' ) ); if ( $stmt_url ) { return '' . $badge . ''; } return $badge; } /** * Render the Upgrade to Pro admin page. */ function accessiq_render_upgrade_page() { $upgrade_url = function_exists( 'ap_fs' ) ? ap_fs()->get_upgrade_url() : 'https://accessiq.thereadscope.com/#pricing'; ?>
'Unauthorized.' ), 403 ); } update_option( 'accessiq_upgrade_notice_dismissed', true ); wp_send_json_success(); } ); /** * 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, dismissed_at DATETIME DEFAULT NULL, dismiss_note VARCHAR(255) DEFAULT NULL, PRIMARY KEY (id), KEY scan_id (scan_id), KEY dismissed (dismissed_at) ) {$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' ), 'profiles_enabled' => true, 'enabled_profiles' => array( 'vision', 'adhd', 'seizure', 'dyslexia', 'keyboard' ), '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_AutoFix::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();