publish; $accessiq_last_scan = get_posts( array( 'post_type' => ACCESSIQ_SCAN_CPT, 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', ) ); $accessiq_last_scan_date = ! empty( $accessiq_last_scan ) ? human_time_diff( get_post_time( 'U', false, $accessiq_last_scan[0] ), current_time( 'timestamp' ) ) . ' ' . __( 'ago', 'accessiq' ) : __( 'Never', 'accessiq' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested $accessiq_last_scan_errors = 0; $accessiq_last_scan_warns = 0; if ( ! empty( $accessiq_last_scan ) ) { $accessiq_issues_table = $wpdb->prefix . 'accessiq_issues'; $accessiq_scan_id = (int) $accessiq_last_scan[0]->ID; // Use severity column if it exists (added in DB v1.2), fall back to level. $accessiq_has_severity = (bool) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '" . esc_sql( $accessiq_issues_table ) . "' AND COLUMN_NAME = 'severity'" ); if ( $accessiq_has_severity ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $accessiq_last_scan_errors = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_issues_table ) . '` WHERE scan_id = %d AND severity = %s', $accessiq_scan_id, 'error' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $accessiq_last_scan_warns = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_issues_table ) . '` WHERE scan_id = %d AND severity = %s', $accessiq_scan_id, 'warning' ) ); } else { // Fallback: Level A = critical errors, AA/AAA = warnings. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $accessiq_last_scan_errors = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_issues_table ) . '` WHERE scan_id = %d AND level = %s', $accessiq_scan_id, 'A' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $accessiq_last_scan_warns = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_issues_table ) . '` WHERE scan_id = %d AND level != %s', $accessiq_scan_id, 'A' ) ); } // ── Debug: log actual issue_type values (temporary — remove after confirming) ── if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $accessiq_debug_types = $wpdb->get_results( 'SELECT issue_type, wcag_id, COUNT(*) AS total FROM `' . esc_sql( $accessiq_issues_table ) . '` GROUP BY issue_type, wcag_id ORDER BY total DESC LIMIT 20' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r error_log( 'AccessIQ issue_types in DB: ' . print_r( $accessiq_debug_types, true ) ); } } $accessiq_widget_enabled = (bool) $accessiq_settings->get( 'widget_enabled', true ); // ── InsightForge stats (last 7 days) ──────────────────────────── $accessiq_clicks_table = $wpdb->prefix . 'accessiq_clicks'; $accessiq_sessions_table = $wpdb->prefix . 'accessiq_sessions'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_if_clicks = (int) $wpdb->get_var( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_clicks_table ) . '` WHERE created_at >= NOW() - INTERVAL 7 DAY' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_if_sessions = (int) $wpdb->get_var( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_sessions_table ) . '` WHERE created_at >= NOW() - INTERVAL 7 DAY' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_if_bounces = (int) $wpdb->get_var( 'SELECT COUNT(id) FROM `' . esc_sql( $accessiq_sessions_table ) . '` WHERE created_at >= NOW() - INTERVAL 7 DAY AND is_bounce = 1' ); $accessiq_if_bounce_rate = $accessiq_if_sessions > 0 ? round( ( $accessiq_if_bounces / $accessiq_if_sessions ) * 100 ) : 0; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_if_top_url = $wpdb->get_var( 'SELECT page_url FROM `' . esc_sql( $accessiq_clicks_table ) . '` WHERE created_at >= NOW() - INTERVAL 7 DAY GROUP BY page_url ORDER BY COUNT(id) DESC LIMIT 1' ); $accessiq_if_top_page = $accessiq_if_top_url ? preg_replace( '#^https?://[^/]+#', '', $accessiq_if_top_url ) : '—'; $accessiq_if_top_page = mb_strlen( $accessiq_if_top_page ) > 32 ? mb_substr( $accessiq_if_top_page, 0, 32 ) . '…' : $accessiq_if_top_page; // ── Avg time on page (last 7 days) ─────────────────────────────── // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_avg_time_raw = (int) $wpdb->get_var( 'SELECT AVG(time_spent) FROM `' . esc_sql( $accessiq_sessions_table ) . '` WHERE created_at >= NOW() - INTERVAL 7 DAY AND time_spent > 0' ); if ( $accessiq_avg_time_raw > 0 ) { $accessiq_avg_m = (int) floor( $accessiq_avg_time_raw / 60 ); $accessiq_avg_s = $accessiq_avg_time_raw % 60; $accessiq_avg_time_fmt = $accessiq_avg_m . 'm ' . str_pad( (string) $accessiq_avg_s, 2, '0', STR_PAD_LEFT ) . 's'; } else { $accessiq_avg_time_fmt = '—'; } // ── Overview: latest scan score + grade + violations by category ── $accessiq_score = 0; if ( ! empty( $accessiq_last_scan ) ) { $accessiq_score = (int) get_post_meta( $accessiq_last_scan[0]->ID, '_accessiq_score', true ); } if ( $accessiq_score >= 90 ) { $accessiq_grade = 'Excellent'; $accessiq_grade_color = '#10d98e'; $accessiq_grade_color_stitch = '#57f1db'; } elseif ( $accessiq_score >= 70 ) { $accessiq_grade = 'Good'; $accessiq_grade_color = '#10d98e'; $accessiq_grade_color_stitch = '#57f1db'; } elseif ( $accessiq_score >= 50 ) { $accessiq_grade = 'Fair'; $accessiq_grade_color = '#fbbf24'; $accessiq_grade_color_stitch = '#fbbf24'; } else { $accessiq_grade = 'Poor'; $accessiq_grade_color = '#f87171'; $accessiq_grade_color_stitch = '#f87171'; } // Bounce rate color (Card B) if ( $accessiq_if_bounce_rate > 70 ) { $accessiq_bounce_color = '#f87171'; } elseif ( $accessiq_if_bounce_rate > 40 ) { $accessiq_bounce_color = '#fbbf24'; } else { $accessiq_bounce_color = '#57f1db'; } $accessiq_issues_tbl = $wpdb->prefix . 'accessiq_issues'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_issues_by_type = $wpdb->get_results( 'SELECT CASE WHEN issue_type LIKE \'%alt%\' OR issue_type LIKE \'%image%\' THEN \'Images\' WHEN issue_type LIKE \'%contrast%\' OR wcag_id = \'1.4.3\' OR wcag_id = \'1.4.11\' THEN \'Contrast\' WHEN issue_type LIKE \'%heading%\' OR wcag_id = \'1.3.1\' THEN \'Headings\' WHEN issue_type LIKE \'%link%\' OR issue_type LIKE \'%descriptive%\' OR wcag_id = \'2.4.4\' OR wcag_id = \'2.4.6\' THEN \'Links\' WHEN issue_type LIKE \'%aria%\' OR issue_type LIKE \'%button%\' OR issue_type LIKE \'%duplicate%\' OR issue_type LIKE \'%accessible%\' OR issue_type LIKE \'%role%\' OR wcag_id IN (\'4.1.1\', \'4.1.2\') THEN \'ARIA\' WHEN issue_type LIKE \'%label%\' OR issue_type LIKE \'%form%\' OR issue_type LIKE \'%input%\' OR wcag_id = \'1.3.5\' THEN \'Forms\' ELSE \'Other\' END AS category, COUNT(*) AS total FROM `' . esc_sql( $accessiq_issues_tbl ) . '` GROUP BY category ORDER BY total DESC LIMIT 6' ); $accessiq_total_issues_all = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . esc_sql( $accessiq_issues_tbl ) . '`' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared // ── Device breakdown (sessions by device) ─────────────────────────── $accessiq_sessions_tbl = $wpdb->prefix . 'accessiq_sessions'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_device_data = $wpdb->get_results( 'SELECT COALESCE(device_type, \'Desktop\') AS device, COUNT(*) AS total FROM `' . esc_sql( $accessiq_sessions_tbl ) . '` GROUP BY device_type ORDER BY total DESC' ); $accessiq_devices = array( 'Desktop' => 0, 'Mobile' => 0, 'Tablet' => 0 ); foreach ( (array) $accessiq_device_data as $accessiq_d ) { $accessiq_key = ucfirst( strtolower( trim( $accessiq_d->device ) ) ); if ( isset( $accessiq_devices[ $accessiq_key ] ) ) { $accessiq_devices[ $accessiq_key ] = (int) $accessiq_d->total; } } $accessiq_total_sessions = array_sum( $accessiq_devices ); // ── Card D: Recent 5 scans ─────────────────────────────────────── $accessiq_card_d_scans = get_posts( array( 'post_type' => ACCESSIQ_SCAN_CPT, 'posts_per_page' => 5, 'orderby' => 'date', 'order' => 'DESC', ) ); // ── Violations tab: last 50 issues from DB ───────────────────────── $accessiq_issues_table = $wpdb->prefix . 'accessiq_issues'; $accessiq_violations_sql = $wpdb->prepare( 'SELECT i.id, i.scan_id, i.wcag_id, i.level, i.element, i.issue_type, i.suggestion, p.meta_value AS page_url FROM `' . esc_sql( $accessiq_issues_table ) . '` i LEFT JOIN `' . esc_sql( $wpdb->postmeta ) . '` p ON i.scan_id = p.post_id AND p.meta_key = %s ORDER BY i.id DESC LIMIT 50', '_accessiq_url' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $accessiq_dashboard_issues = $wpdb->get_results( $accessiq_violations_sql ); ?>
| page_url ?? '—'; $accessiq_path = wp_parse_url( $accessiq_url, PHP_URL_PATH ); echo esc_html( $accessiq_path ? $accessiq_path : $accessiq_url ); ?> | array( 'CRITICAL', 'rgba(248,113,113,0.15)', '#f87171' ), 'warning' => array( 'SERIOUS', 'rgba(251,191,36,0.15)', '#fbbf24' ), ); $accessiq_sev = $accessiq_severity_map[ $accessiq_row_type ] ?? array( 'MODERATE', 'rgba(167,139,250,0.15)', '#a78bfa' ); ?> |