prefix . 'accessibility_checker'; $post_id = (int) $post_id; $siteid = get_current_blog_id(); $results = $wpdb->get_results( $wpdb->prepare( 'SELECT id, rule, ignre, object, ruletype, selector, ancestry, xpath, landmark, landmark_selector FROM %i where postid = %d and siteid = %d', $table_name, $post_id, $siteid ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Safe variable used for table name. if ( ! $results ) { return null; } return Helpers::filter_results_to_only_active_rules( $results ); } /** * AJAX handler function for frontend highlighting requests. */ public function ajax() { if ( ! check_ajax_referer( 'frontend-highlighter', 'nonce', false ) ) { wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) ); } if ( ! isset( $_REQUEST['post_id'] ) ) { $error = new \WP_Error( '-2', __( 'The id value was not set', 'accessibility-checker' ) ); wp_send_json_error( $error ); } $post_id = isset( $_REQUEST['post_id'] ) ? (int) $_REQUEST['post_id'] : 0; $post = get_post( $post_id ); if ( ! $post ) { wp_send_json_error( new \WP_Error( '-4', __( 'Post not found', 'accessibility-checker' ) ) ); } // A password-protected post blocks access regardless of read_post/public-viewable // checks below - those don't know about the password gate, so this must be checked // explicitly or a user who hasn't entered the password can still get highlighter data. if ( post_password_required( $post ) ) { wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) ); } // Check if the user has permission to view this post. if ( is_user_logged_in() ) { // For authenticated users, require both read_post and the dedicated // frontend highlighter capability - read_post alone is not sufficient, // or this endpoint would return highlighter data to any logged-in // reader regardless of what the Permissions tab granted (PRO-1290). if ( ! current_user_can( 'read_post', $post_id ) || ! ( function_exists( 'edac_user_can_use_frontend_highlighter' ) && edac_user_can_use_frontend_highlighter() ) ) { wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) ); } } elseif ( apply_filters( 'edac_filter_frontend_highlighter_visibility', false ) ) { // For unauthenticated users, only allow access to publicly viewable posts. if ( ! is_post_publicly_viewable( $post ) ) { wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) ); } } else { // Shouldn't ever reach this point but error just in case. wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) ); } $results = $this->get_issues( $post_id ); if ( ! $results ) { wp_send_json_error( new \WP_Error( '-3', __( 'Issue query returned no results', 'accessibility-checker' ) ) ); } $rules = edac_register_rules(); $issues = []; $fixes = []; foreach ( $results as $result ) { $array = []; $rule = edac_filter_by_value( $rules, 'slug', $result['rule'] ); // When rules are filtered out, they are not in the rules array and this can be empty. Skip when the rule // is empty to avoid php warnings and passing null values to the frontend highlighter. if ( ! $rule ) { continue; } $rule_type = ( true === (bool) $result['ignre'] ) ? 'ignored' : $rule[0]['rule_type']; $array['rule_type'] = $rule_type; $array['slug'] = $rule[0]['slug']; $array['rule_title'] = $rule[0]['title']; $array['wcag'] = $rule[0]['wcag'] ?? ''; $array['wcag_title'] = $this->get_wcag_title( $rule[0]['wcag'] ?? '' ); $array['summary'] = $rule[0]['summary']; $array['why_it_matters'] = wp_kses_post( $rule[0]['why_it_matters'] ?? '' ); $array['how_to_fix'] = wp_kses_post( $rule[0]['how_to_fix'] ?? '' ); $array['link'] = edac_link_wrapper( $rule[0]['info_url'], 'frontend-highlighter', $rule[0]['slug'], false ); $array['object'] = html_entity_decode( $result['object'], ENT_QUOTES | ENT_HTML5 ); $array['id'] = $result['id']; $array['ignored'] = $result['ignre']; $array['selector'] = $result['selector'] ?? ''; $array['ancestry'] = $result['ancestry'] ?? ''; $array['xpath'] = $result['xpath'] ?? ''; $array['severity'] = $rule[0]['severity'] ?? ''; $array['landmark'] = $result['landmark'] ?? ''; $array['landmark_selector'] = $result['landmark_selector'] ?? ''; $issues[] = $array; if ( ! isset( $fixes[ $rule[0]['slug'] ] ) ) { $fixes_for_rule = $rule[0]['fixes'] ?? []; foreach ( $fixes_for_rule as $fix_for_rule ) { $fix = FixesManager::get_instance()->get_fix( $fix_for_rule ); if ( $fix && method_exists( $fix, 'get_fields_array' ) ) { $fixes[ $rule[0]['slug'] ] = isset( $fixes[ $rule[0]['slug'] ] ) ? array_merge( $fixes[ $rule[0]['slug'] ], $fix->get_fields_array() ) : $fix->get_fields_array(); } } } } if ( ! $issues ) { wp_send_json_error( new \WP_Error( '-5', __( 'Object query returned no results', 'accessibility-checker' ) ) ); } // if we have fixes then create fields for each of the groups. if ( ! empty( $fixes ) ) { foreach ( $fixes as $key => $fix ) { // count the number of fields in the fix. $fields_count = count( $fix ); $itteration = 0; $fix_fields_markup = ''; foreach ( $fix as $index => $field ) { ++$itteration; $field_type = $field['type'] ?? 'checkbox'; ob_start(); if ( isset( $field['group_name'] ) ) { // if this is anything other than the first field in the group then close the fieldset. if ( 1 !== $itteration ) { ?>

$index, 'location' => 'frontend-highlighter', ], $field ) ); if ( $fields_count === $itteration ) { ?>
'; } } wp_send_json_success( wp_json_encode( [ 'issues' => $issues, 'fixes' => $fixes, ] ) ); } /** * Get the WCAG criterion title for a given WCAG number. * * @param string $wcag_number The WCAG number (e.g., '1.1.1'). * @return string The criterion title, or empty string if not found. */ private function get_wcag_title( $wcag_number ) { if ( ! $wcag_number ) { return ''; } static $wcag_lookup = null; if ( null === $wcag_lookup ) { $wcag_file = EDAC_PLUGIN_DIR . 'includes/wcag.php'; if ( ! file_exists( $wcag_file ) ) { return ''; } $wcag_data = include $wcag_file; $wcag_lookup = is_array( $wcag_data ) ? array_column( $wcag_data, 'title', 'number' ) : []; } return $wcag_lookup[ $wcag_number ] ?? ''; } }