__( 'Keyboard navigation', 'accessible-forms-by-accessible-org' ), 'screen_reader' => __( 'Screen reader', 'accessible-forms-by-accessible-org' ), 'zoom' => __( 'Zoom, text size and narrow layout', 'accessible-forms-by-accessible-org' ) ); } public static function records( $id ) { $value = get_post_meta( $id, self::META, true ); if ( ! is_array( $value ) ) { return array(); } $valid = array_filter( $value, static function ( $item ) { if ( ! is_array( $item ) || ! is_array( $item['tests'] ?? null ) ) { return false; } foreach ( array( 'browser', 'technology', 'notes', 'version', 'context' ) as $key ) { if ( ! is_string( $item[ $key ] ?? null ) ) { return false; } } foreach ( array( 'actor', 'page', 'time' ) as $key ) { if ( ! is_int( $item[ $key ] ?? null ) || $item[ $key ] < 0 ) { return false; } } foreach ( self::tests() as $key => $label ) { if ( ! in_array( $item['tests'][ $key ] ?? null, array( 'pass', 'problem', 'not_tested' ), true ) ) { return false; } } return strlen( $item['context'] ) === 64 && strlen( $item['version'] ) === 24; } ); return array_slice( array_values( $valid ), 0, self::MAX_RECORDS ); } /** Record a report only against the current saved draft, with edit permission. */ public static function record( $id, array $schema, array $raw ) { if ( 'accessible_form' !== get_post_type( $id ) || ! current_user_can( 'edit_post', $id ) ) { return new \WP_Error( 'forbidden', __( 'You cannot record tests for this form.', 'accessible-forms-by-accessible-org' ) ); } $plain = static function ( $key, $max ) use ( $raw ) { return wp_html_excerpt( sanitize_textarea_field( is_scalar( $raw[ $key ] ?? null ) ? (string) $raw[ $key ] : '' ), $max, '' ); }; $page = absint( $raw['page'] ?? 0 ); if ( $page && ! TestPreview::can_use_page( $page ) ) { return new \WP_Error( 'page', __( 'Draft saved. Choose a test page you can edit, then record the tests again.', 'accessible-forms-by-accessible-org' ) ); } $tests = array(); foreach ( self::tests() as $key => $label ) { $value = is_string( $raw[ $key ] ?? null ) ? $raw[ $key ] : ''; if ( ! in_array( $value, array( 'not_tested', 'pass', 'problem' ), true ) ) { return new \WP_Error( 'outcome', __( 'Draft saved. Choose a result for each manual test.', 'accessible-forms-by-accessible-org' ) ); } $tests[ $key ] = $value; } $browser = $plain( 'browser', 200 ); $technology = $plain( 'technology', 200 ); $notes = $plain( 'notes', 1500 ); if ( '' === $browser || count( array_filter( $tests, static function ( $value ) { return 'not_tested' !== $value; } ) ) === 0 ) { return new \WP_Error( 'incomplete', __( 'Draft saved. Enter the browser and version, and record at least one test you performed. Leave other tests as Not tested.', 'accessible-forms-by-accessible-org' ) ); } if ( 'not_tested' !== $tests['screen_reader'] && '' === $technology ) { return new \WP_Error( 'technology', __( 'Draft saved. Enter the screen reader and version used for the reported screen reader test.', 'accessible-forms-by-accessible-org' ) ); } if ( in_array( 'problem', $tests, true ) && '' === $notes ) { return new \WP_Error( 'notes', __( 'Draft saved. Describe the accessibility problem so it can be fixed and tested again.', 'accessible-forms-by-accessible-org' ) ); } $lease = Locks::acquire( 'accessibility-review:' . $id ); if ( ! $lease ) { return new \WP_Error( 'busy', __( 'Draft saved. Another review is being recorded. Try again.', 'accessible-forms-by-accessible-org' ) ); } try { wp_cache_delete( $id, 'post_meta' ); if ( Schema::validate( $schema )[1] || Revisions::version( Schema::get( $id, true ) ?: array() ) !== Revisions::version( $schema ) ) { return new \WP_Error( 'changed', __( 'The form changed. Test the current draft before recording a review.', 'accessible-forms-by-accessible-org' ) ); } $record = array( 'time' => time(), 'actor' => get_current_user_id(), 'page' => $page, 'version' => Revisions::version( $schema ), 'context' => LiveCheck::context( $id, $page, $schema ), 'browser' => $browser, 'technology' => $technology, 'tests' => $tests, 'notes' => $notes ); $records = self::records( $id ); array_unshift( $records, $record ); if ( ! Persistence::meta( $id, self::META, array_slice( $records, 0, self::MAX_RECORDS ) ) ) { return new \WP_Error( 'storage', __( 'Draft saved, but the test record could not be saved. Try recording it again.', 'accessible-forms-by-accessible-org' ) ); } Audit::record( 'accessibility_tests_recorded', $id ); return true; } finally { Locks::release( $lease ); } } public static function stale( $id, array $schema, array $record ) { $page = absint( $record['page'] ?? 0 ); return ( $page && ! TestPreview::can_use_page( $page ) ) || ! hash_equals( (string) ( $record['context'] ?? '' ), LiveCheck::context( $id, $page, $schema ) ); } public static function status( $id, array $schema, array $record ) { if ( self::stale( $id, $schema, $record ) ) { return __( 'Review needs repeating: the form, page or site configuration changed.', 'accessible-forms-by-accessible-org' ); } if ( in_array( 'problem', (array) ( $record['tests'] ?? array() ), true ) ) { return __( 'Manual tests report a problem. Fix it and record a new test.', 'accessible-forms-by-accessible-org' ); } if ( ! empty( $record['page'] ) && count( $record['tests'] ?? array() ) === count( self::tests() ) && ! in_array( 'not_tested', $record['tests'], true ) ) { return __( 'Manual tests reported as passed for this revision and page. This is an author report, not certification.', 'accessible-forms-by-accessible-org' ); } return __( 'Manual review is incomplete: some tests or the intended page still need checking.', 'accessible-forms-by-accessible-org' ); } public static function panel( $id, array $schema ) { $records = self::records( $id ); $record = $records[0] ?? array(); $kept_key = self::META . '_' . $id . '_' . get_current_user_id(); $kept = get_transient( $kept_key ); $kept = is_array( $kept ) ? $kept : array(); delete_transient( $kept_key ); $catalog = TestPreview::page_options(); $selected_page = absint( $kept['page'] ?? 0 ); if ( $selected_page && TestPreview::can_use_page( $selected_page ) && ! in_array( $selected_page, array_column( $catalog['items'], 'id' ), true ) ) { $catalog['items'][] = array( 'id' => $selected_page, 'title' => get_the_title( $selected_page ) . ' · #' . $selected_page ); } echo '

' . esc_html__( 'Accessibility test record', 'accessible-forms-by-accessible-org' ) . '

' . esc_html( $record ? self::status( $id, $schema, $record ) : __( 'Manual tests not recorded. The required publication confirmation does not record a screen reader test.', 'accessible-forms-by-accessible-org' ) ) . '

'; echo '
' . esc_html__( 'Record tests you performed', 'accessible-forms-by-accessible-org' ) . '

' . esc_html__( 'Save your tested draft and record the results. Choose Not tested for work you have not done. Use sample answers; do not put visitor information in notes. The latest 10 reports are kept with the form. Recording tests does not publish it.', 'accessible-forms-by-accessible-org' ) . '

'; echo ''; foreach ( array( 'browser' => __( 'Browser and version used', 'accessible-forms-by-accessible-org' ), 'technology' => __( 'Screen reader and version used (when tested)', 'accessible-forms-by-accessible-org' ) ) as $key => $label ) { echo ''; } foreach ( self::tests() as $key => $label ) { echo ''; } echo '

'; if ( $records ) { echo '
' . esc_html__( 'Previous test reports', 'accessible-forms-by-accessible-org' ) . '
    '; foreach ( $records as $item ) { $actor = get_userdata( $item['actor'] ); $page_label = $item['page'] ? ( TestPreview::can_use_page( $item['page'] ) ? get_the_title( $item['page'] ) : __( 'Unavailable test page', 'accessible-forms-by-accessible-org' ) ) . ' · #' . $item['page'] : __( 'Plugin-only preview', 'accessible-forms-by-accessible-org' ); echo '
  1. ' . esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $item['time'] ) ) . ' · ' . esc_html( $actor ? $actor->display_name : __( 'Deleted user', 'accessible-forms-by-accessible-org' ) ) . '

    ' . esc_html( self::status( $id, $schema, $item ) ) . '

    ' . esc_html( $item['browser'] . ( $item['technology'] ? ' · ' . $item['technology'] : '' ) ) . '

    ' . esc_html( $page_label ) . '

      '; foreach ( self::tests() as $key => $label ) { echo '
    • ' . esc_html( $label . ': ' . ( array( 'pass' => __( 'Passed', 'accessible-forms-by-accessible-org' ), 'problem' => __( 'Problem found', 'accessible-forms-by-accessible-org' ), 'not_tested' => __( 'Not tested', 'accessible-forms-by-accessible-org' ) )[ $item['tests'][ $key ] ?? 'not_tested' ] ) ) . '
    • '; } echo '

    ' . esc_html( $item['notes'] ) . '

  2. '; } echo '
'; } echo '
'; } }