opts = get_option( 'accessiq_settings', AccessIQ::default_settings() ); add_action( 'admin_menu', array( $this, 'add_menu' ) ); add_action( 'admin_init', array( $this, 'maybe_redirect_to_welcome' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); add_action( 'wp_ajax_accessiq_save', array( $this, 'ajax_save' ) ); } public function maybe_redirect_to_welcome() { if ( ! get_transient( 'accessiq_show_welcome' ) ) { return; } // WordPress multisite activation sets 'activate-multi' with no nonce. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['activate-multi'] ) ) { // No nonce available from WP core multisite activation flow. delete_transient( 'accessiq_show_welcome' ); return; } delete_transient( 'accessiq_show_welcome' ); wp_safe_redirect( admin_url( 'admin.php?page=accessiq-about' ) ); exit; } public function add_menu() { $icon = ACCESSIQ_URL . 'admin/images/menu-icon.png'; add_menu_page( 'AccessIQ', 'AccessIQ', 'manage_options', 'accessiq', array( $this, 'render_page' ), $icon, 30 ); add_submenu_page( 'accessiq', 'Overview', '🏠 Overview', 'manage_options', 'accessiq', array( $this, 'render_page' ) ); } /** * Chart data for Overview (matches dashboard.php logic). * * @return array{score:int,gradeColor:string,devices:array} */ private function get_dashboard_chart_payload() { global $wpdb; $last = get_posts( array( 'post_type' => ACCESSIQ_SCAN_CPT, 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', ) ); $score = 0; if ( ! empty( $last ) ) { $score = (int) get_post_meta( $last[0]->ID, '_accessiq_score', true ); } if ( $score >= 70 ) { $grade_color = '#57f1db'; } elseif ( $score >= 50 ) { $grade_color = '#fbbf24'; } else { $grade_color = '#f87171'; } $sessions_tbl = $wpdb->prefix . 'accessiq_sessions'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $rows = $wpdb->get_results( 'SELECT COALESCE(device_type, \'Desktop\') AS device, COUNT(*) AS total FROM `' . esc_sql( $sessions_tbl ) . '` GROUP BY device_type ORDER BY total DESC' ); $devices = array( 'Desktop' => 0, 'Mobile' => 0, 'Tablet' => 0 ); foreach ( (array) $rows as $row ) { $key = ucfirst( strtolower( trim( $row->device ) ) ); if ( isset( $devices[ $key ] ) ) { $devices[ $key ] = (int) $row->total; } } return array( 'score' => $score, 'gradeColor' => $grade_color, 'devices' => $devices, ); } public function enqueue( $hook ) { wp_enqueue_style( 'accessiq-admin-sidebar', ACCESSIQ_URL . 'admin/css/admin.css', array(), ACCESSIQ_VERSION ); wp_enqueue_script( 'accessiq-sidebar', ACCESSIQ_URL . 'admin/js/sidebar.js', array( 'jquery' ), ACCESSIQ_VERSION, true ); if ( strpos( $hook, 'accessiq' ) === false ) { return; } wp_enqueue_style( 'accessiq-google-fonts', 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;0,9..40,800;1,9..40,400&family=DM+Mono:wght@400;500&family=Manrope:wght@400;500;600;700;800&display=swap', array(), ACCESSIQ_VERSION ); $admin_deps = array( 'jquery' ); if ( 'toplevel_page_accessiq' === $hook ) { wp_enqueue_script( 'accessiq-chartjs', ACCESSIQ_URL . 'admin/js/chart.min.js', array(), '4.4.8', true ); $admin_deps[] = 'accessiq-chartjs'; } wp_enqueue_script( 'accessiq-admin', ACCESSIQ_URL . 'admin/js/admin.js', $admin_deps, ACCESSIQ_VERSION, true ); $d = ( 'toplevel_page_accessiq' === $hook ) ? $this->get_dashboard_chart_payload() : array( 'score' => 0, 'gradeColor' => '#10d98e', 'devices' => array( 'Desktop' => 0, 'Mobile' => 0, 'Tablet' => 0 ), ); $chart_data = array( 'score' => (int) $d['score'], 'gradeColor' => sanitize_hex_color( $d['gradeColor'] ) ?: '#10d98e', 'desk' => (int) ( $d['devices']['Desktop'] ?? 0 ), 'mob' => (int) ( $d['devices']['Mobile'] ?? 0 ), 'tab' => (int) ( $d['devices']['Tablet'] ?? 0 ), ); wp_localize_script( 'accessiq-admin', 'accessiqAdmin', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'accessiq_nonce' ), 'save_scan_nonce' => wp_create_nonce( 'accessiq_save_scan' ), 'settings_nonce' => wp_create_nonce( 'accessiq_settings_nonce' ), 'settings' => $this->opts, 'scan_url' => admin_url( 'admin.php?page=accessiq-scanner' ), 'chart_data' => $chart_data, ) ); } public function ajax_save() { check_ajax_referer( 'accessiq_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die(); } $s = $this->opts; $s['widget_enabled'] = ( isset( $_POST['widget_enabled'] ) && sanitize_text_field( wp_unslash( $_POST['widget_enabled'] ) ) === 'true' ); $s['widget_position'] = sanitize_text_field( wp_unslash( $_POST['widget_position'] ?? 'bottom-right' ) ) ?: 'bottom-right'; $s['widget_color'] = sanitize_hex_color( wp_unslash( $_POST['widget_color'] ?? '#1d4ed8' ) ) ?: '#1d4ed8'; $s['show_on_mobile'] = ( isset( $_POST['show_on_mobile'] ) && sanitize_text_field( wp_unslash( $_POST['show_on_mobile'] ) ) === 'true' ); $s['widget_features'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_POST['widget_features'] ?? array() ) ); $s['scanner_enabled'] = ( isset( $_POST['scanner_enabled'] ) && sanitize_text_field( wp_unslash( $_POST['scanner_enabled'] ) ) === 'true' ); $scan_level_raw = isset( $_POST['scan_level'] ) ? sanitize_text_field( wp_unslash( $_POST['scan_level'] ) ) : 'AA'; $s['scan_level'] = in_array( $scan_level_raw, array( 'A', 'AA' ), true ) ? $scan_level_raw : 'AA'; $s['notify_email'] = sanitize_email( wp_unslash( $_POST['notify_email'] ?? get_option( 'admin_email' ) ) ); update_option( 'accessiq_settings', $s ); $this->opts = $s; $accessiq_click_raw = isset( $_POST['analytics_enable_click_tracking'] ) ? sanitize_text_field( wp_unslash( $_POST['analytics_enable_click_tracking'] ) ) : 'false'; $accessiq_behavior_raw = isset( $_POST['analytics_enable_behavior_tracking'] ) ? sanitize_text_field( wp_unslash( $_POST['analytics_enable_behavior_tracking'] ) ) : 'false'; $accessiq_excl_raw = isset( $_POST['analytics_exclude_logged_in'] ) ? sanitize_text_field( wp_unslash( $_POST['analytics_exclude_logged_in'] ) ) : 'false'; $accessiq_replace_raw = isset( $_POST['analytics_replace_dashboard'] ) ? sanitize_text_field( wp_unslash( $_POST['analytics_replace_dashboard'] ) ) : 'false'; update_option( 'accessiq_analytics_enable_click_tracking', 'true' === $accessiq_click_raw ); update_option( 'accessiq_analytics_enable_behavior_tracking', 'true' === $accessiq_behavior_raw ); update_option( 'accessiq_analytics_exclude_logged_in', 'true' === $accessiq_excl_raw ); update_option( 'accessiq_analytics_replace_dashboard', 'true' === $accessiq_replace_raw ); $accessiq_retention_raw = isset( $_POST['analytics_data_retention'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['analytics_data_retention'] ) ) ) : 90; update_option( 'accessiq_analytics_data_retention', in_array( $accessiq_retention_raw, array( 30, 60, 90, 180 ), true ) ? $accessiq_retention_raw : 90 ); do_action( 'accessiq_settings_saved' ); wp_send_json_success( array( 'message' => __( 'Settings saved!', 'accessiq' ) ) ); } public function render_page() { include ACCESSIQ_DIR . 'admin/views/dashboard.php'; } public function render_scanner_page() { include ACCESSIQ_DIR . 'admin/views/scanner.php'; } public function render_reports_page() { include ACCESSIQ_DIR . 'admin/views/reports.php'; } public function render_settings_page() { include ACCESSIQ_DIR . 'admin/views/settings.php'; } public function get( $key, $default = null ) { return $this->opts[ $key ] ?? $default; } }