diff --git a/.github/workflows/deploy-on-release-to-dot-org.yml b/.github/workflows/deploy-on-release-to-dot-org.yml index 873f921a..5f2f941d 100644 --- a/.github/workflows/deploy-on-release-to-dot-org.yml +++ b/.github/workflows/deploy-on-release-to-dot-org.yml @@ -45,14 +45,3 @@ jobs: SVN_USERNAME: ${{ secrets.SVN_USERNAME }} SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} BUILD_DIR: ./dist/${{ github.event.repository.name }}/ - - - name: Upload release asset - if: github.event.release # can only run in context of a release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ${{ steps.dist-build.outputs.zip-path }} - asset_name: ${{ github.event.repository.name }}.zip - asset_content_type: application/zip diff --git a/accessibility-checker.php b/accessibility-checker.php index 4b306af5..8a6de117 100755 --- a/accessibility-checker.php +++ b/accessibility-checker.php @@ -10,7 +10,7 @@ * Plugin Name: Accessibility Checker * Plugin URI: https://a11ychecker.com * Description: Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance. - * Version: 1.11.2 + * Version: 1.12.0 * Author: Equalize Digital * Author URI: https://equalizedigital.com * License: GPL-2.0+ @@ -35,7 +35,7 @@ // Current plugin version. if ( ! defined( 'EDAC_VERSION' ) ) { - define( 'EDAC_VERSION', '1.11.2' ); + define( 'EDAC_VERSION', '1.12.0' ); } // Current database version. @@ -149,6 +149,17 @@ function edac_register_rules() { // If we got this far, this is the 1st time we called this function. // We need to load the rules from the filesystem, and apply any filters. $default_rules = include __DIR__ . '/includes/rules.php'; + /** + * Filter the default rules. + * + * Allows removing or adding rules. If you are adding a rule make + * sure you have added a function matching the pattern: + * `edac_rule_{$rule_id}`. + * + * @since 1.4.0 + * + * @param array $default_rules The default rules. + */ $default_rules = apply_filters( 'edac_filter_register_rules', $default_rules ); return $default_rules; diff --git a/admin/class-admin-notices.php b/admin/class-admin-notices.php index 13615b5a..0e876912 100644 --- a/admin/class-admin-notices.php +++ b/admin/class-admin-notices.php @@ -350,6 +350,13 @@ public function edac_review_notice_ajax() { * @return string */ public function edac_password_protected_notice_text() { + /** + * Filter the password protected notice text. + * + * @since 1.4.0 + * + * @param string $text The password protected notice text. + */ return apply_filters( 'edac_filter_password_protected_notice_text', sprintf( diff --git a/admin/class-ajax.php b/admin/class-ajax.php index a6c710fe..1fff9869 100644 --- a/admin/class-ajax.php +++ b/admin/class-ajax.php @@ -290,6 +290,13 @@ function ( $a, $b ) { $rules = array_merge( $error_rules, $warning_rules, $passed_rules ); if ( $rules ) { + /** + * Filters if a user can ignore issues. + * + * @since 1.4.0 + * + * @allowed bool True if allowed, false if not + */ $ignore_permission = apply_filters( 'edac_ignore_permission', true ); foreach ( $rules as $rule ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Using direct query for interacting with custom database, safe variable used for table name, caching not required for one time operation. @@ -509,6 +516,14 @@ public function readability() { } } + /** + * Filter the content used for reading grade readability analysis. + * + * @since 1.4.0 + * + * @param string $content The content to be filtered. + * @param int $post_id The post ID. + */ $content = apply_filters( 'edac_filter_readability_content', $content, $post_id ); $content = wp_filter_nohtml_kses( $content ); $content = str_replace( ']]>', ']]>', $content ); diff --git a/admin/class-insert-rule-data.php b/admin/class-insert-rule-data.php index 1c7c1da0..db15a381 100644 --- a/admin/class-insert-rule-data.php +++ b/admin/class-insert-rule-data.php @@ -110,7 +110,15 @@ public function insert( object $post, string $rule, string $ruletype, string $ru // Insert new records. if ( ! $results ) { - // filter post types. + /** + * Filter the rule data before inserting it into the database. + * + * This data will be sanitized after the filter is applied. + * + * @since 1.4.0 + * + * @param array $rule_data The rule data. + */ $rule_data = apply_filters( 'edac_filter_insert_rule_data', $rule_data ); // Sanitize rule data since it is filtered, and we can't be sure diff --git a/admin/class-meta-boxes.php b/admin/class-meta-boxes.php index a97b4f05..727ef192 100644 --- a/admin/class-meta-boxes.php +++ b/admin/class-meta-boxes.php @@ -54,8 +54,18 @@ public function register_meta_boxes(): void { * @return void */ public function render(): void { + /** + * Fires before the meta box is rendered. + * + * @since 1.10.0 + */ do_action( 'edac_before_meta_box' ); include_once plugin_dir_path( __DIR__ ) . 'partials/custom-meta-box.php'; + /** + * Fires after the meta box is rendered. + * + * @since 1.10.0 + */ do_action( 'edac_after_meta_box' ); } } diff --git a/admin/class-scans-stats.php b/admin/class-scans-stats.php index f90b4357..e7be48ef 100644 --- a/admin/class-scans-stats.php +++ b/admin/class-scans-stats.php @@ -317,6 +317,8 @@ function ( $item ) { $formatting['passed_percentage_formatted'] = Helpers::format_percentage( $data['passed_percentage'] ); $formatting['avg_issue_density_percentage_formatted'] = Helpers::format_percentage( $data['avg_issue_density_percentage'] ); + $formatting['cached_at_formatted'] = Helpers::format_date( $data['cached_at'], true ); + $data = array_merge( $data, $formatting ); if ( $data['posts_scanned'] > 0 ) { diff --git a/admin/class-welcome-page.php b/admin/class-welcome-page.php index 58414f5d..07e82939 100644 --- a/admin/class-welcome-page.php +++ b/admin/class-welcome-page.php @@ -207,7 +207,7 @@ public static function render_summary() {
0 ) : ?>
- +
diff --git a/admin/site-health/class-information.php b/admin/site-health/class-information.php index aba1cacc..54bb40ec 100644 --- a/admin/site-health/class-information.php +++ b/admin/site-health/class-information.php @@ -18,7 +18,7 @@ class Information { */ public function __construct() { } - + /** * Initialize class hooks. * @@ -63,7 +63,15 @@ private function get_edac_data() { $information[ $key ] = $class->get(); } - // Allow extensions to add their own debug information that's specific to EDAC. + /** + * Filter the debug information. + * + * Allows extensions to add their own debug information that's specific to EDAC. + * + * @since 1.6.10 + * + * @param array $information The debug information. + */ return apply_filters( 'edac_debug_information', $information ); } } diff --git a/changelog.txt b/changelog.txt index 825bdd58..036f0e41 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,39 @@ Newer versions can be found in readme.txt. += 1.9.3 = +* Updated: capability checks for the welcome page, dashboard widget, and admin notices + += 1.9.2 = +* Fixed: filtered rules are not passed to the frontend highlighter, avoiding 'null' state issues +* Updated: frontend highlighter buttons to be disabled until issues are confirmed +* Updated: frontend highlighter buttons to show only after we know there are issues to display +* Updated: frontend highlighter to not show buttons if none are returned + += 1.9.1 = +* Updated: `edac_include_rules_files to fire on init action to fix the `edac_filter_register_rules` filter timing + += 1.9.0 = +* Created: class that creates the accessibility statement on activation +* Removed: custom database query that checked for existing accessibility statement in exchange for the `get_page_by_path()` function +* Fixed: bug with trying to compare the simplified summary ordinal value and added fallback +* Removed: `wp_send_json_error()` from `simplified_summary` Ajax function when the simplified summary is empty +* Added: simplified summary grade*level, message, and icon logic to the `summary()` Ajax +* Fixed: issue with the submit button text showing as `Submit Query` in Firefox. +* Updated: missing transcript rule to skip certain types of links +* Added: missing UTM parameters to the welcome page URLs. +* Removed: legacy system information code +* Removed: cbschuld/browser.php composer package +* Added: class structure for site health +* Added: site health health information for free, pro, and audit history plugins +* Added: update database class +* Removed: `edac_before_page_render` functions from the main file +* Added: frontend validate class +* Added: frontend validate unit tests +* Removed: unused new window warning meta update functions +* Fixed: front end highlight focus issue +* Added: summary generator class to replace the `edac_summary()` function +* Deprecated: `edac_summary()` function + = 1.8.1 = * Fixed: false positives on the incorrect heading order rule * Added: fallback to determine ordinal when php intl extension is not installed diff --git a/includes/classes/class-rest-api.php b/includes/classes/class-rest-api.php index 6cc5a92f..e1a93e03 100644 --- a/includes/classes/class-rest-api.php +++ b/includes/classes/class-rest-api.php @@ -204,6 +204,16 @@ public function set_post_scan_results( $request ) { try { + /** + * Fires before the validation process starts. + * + * This is only running in the JS check context. + * + * @since 1.5.0 + * + * @param int $post_id The post ID. + * @param string $type The type of validation which is always 'js' in this path. + */ do_action( 'edac_before_validate', $post_id, 'js' ); $violations = $request['violations']; @@ -234,16 +244,48 @@ public function set_post_scan_results( $request ) { //phpcs:ignore Generic.Commenting.Todo.TaskFound, Squiz.PHP.CommentedOutCode.Found // TODO: add support storing $violation['selector'], $violation['tags']. + /** + * Fires before a rule is run against the content. + * + * This is only running in the JS check context. + * + * @since 1.5.0 + * + * @param int $post_id The post ID. + * @param string $rule_id The rule ID. + * @param string $type The type of validation which is always 'js' in this path. + */ do_action( 'edac_before_rule', $post_id, $rule_id, 'js' ); ( new Insert_Rule_Data() )->insert( $post, $rule_id, $impact, $html ); + /** + * Fires after a rule is run against the content. + * + * This is only running in the JS check context. + * + * @since 1.5.0 + * + * @param int $post_id The post ID. + * @param string $rule_id The rule ID. + * @param string $type The type of validation which is always 'js' in this path. + */ do_action( 'edac_after_rule', $post_id, $rule_id, 'js' ); } } } + /** + * Fires after the validation process is complete. + * + * This is only running in the JS check context. + * + * @since 1.5.0 + * + * @param int $post_id The post ID. + * @param string $type The type of validation which is always 'js' in this path. + */ do_action( 'edac_after_validate', $post_id, 'js' ); // remove corrected records. diff --git a/includes/classes/class-simplified-summary.php b/includes/classes/class-simplified-summary.php index 64b822e0..71d1fe71 100644 --- a/includes/classes/class-simplified-summary.php +++ b/includes/classes/class-simplified-summary.php @@ -58,9 +58,16 @@ public function output_simplified_summary( $content ) { */ public function simplified_summary_markup( $post ) { $simplified_summary = get_post_meta( $post, '_edac_simplified_summary', true ) - ? get_post_meta( $post, '_edac_simplified_summary', true ) + ? get_post_meta( $post, '_edac_simplified_summary', true ) : ''; + /** + * Filter the heading that gets output before the simplified summary inside an

tag. + * + * @since 1.4.0 + * + * @param string $simplified_summary_heading The simplified summary heading. + */ $simplified_summary_heading = apply_filters( 'edac_filter_simplified_summary_heading', esc_html__( 'Simplified Summary', 'accessibility-checker' ) diff --git a/includes/helper-functions.php b/includes/helper-functions.php index 64a3570a..edd5c4b3 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -286,6 +286,13 @@ function edac_custom_post_types() { * @return array */ function edac_post_types() { + /** + * Filter the post types that the plugin will check. + * + * @since 1.4.0 + * + * @param array $post_types post types. + */ $post_types = apply_filters( 'edac_filter_post_types', [ 'post', 'page' ] ); // remove duplicates. diff --git a/includes/options-page.php b/includes/options-page.php index 820d8831..b3c7360e 100644 --- a/includes/options-page.php +++ b/includes/options-page.php @@ -50,7 +50,13 @@ function edac_add_options_page() { return; } - // settings panel filter. + /** + * Filter the capability required to access the settings page. + * + * @since 1.4.0 + * + * @param string $settings_capability The capability required to access the settings page. + */ $settings_capability = apply_filters( 'edac_filter_settings_capability', 'manage_options' ); add_submenu_page( diff --git a/includes/rules/color_contrast_failure.php b/includes/rules/color_contrast_failure.php deleted file mode 100644 index 2c572b9a..00000000 --- a/includes/rules/color_contrast_failure.php +++ /dev/null @@ -1,724 +0,0 @@ -find( '*' ); - foreach ( $elements as $element ) { - - if ( isset( $element ) && stristr( $element->getAttribute( 'style' ), 'color:' ) && '' !== $element->innertext ) { - $foreground = ''; - $background = ''; - - // get background color. - preg_match( '/background-color:\s*(#(?:[0-9a-f]{2}){2,4}|#[0-9a-f]{3}|(?:rgba?|hsla?)\((?:\d+%?(?:deg|rad|grad|turn)?(?:,|\s)+){2,3}[\s\/]*[\d\.]+%?\)\s*(!important)*)/i', $element->getAttribute( 'style' ), $matches, PREG_OFFSET_CAPTURE ); - if ( isset( $matches[1][0] ) && '' !== $matches[1][0] ) { - $rules['background-color'] = $matches[1][0]; - } - - preg_match( '/background:\s*(rgb\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}\)|\#*[\w]{3,25}\s*(!important)*)/i', $element->getAttribute( 'style' ), $matches, PREG_OFFSET_CAPTURE ); - - if ( isset( $matches[1][0] ) && '' !== $matches[1][0] ) { - $rules['background'] = $matches[1][0]; - } - - // if no background color is set assume white. - $assumedbackground = '#ffffff'; - if ( ! isset( $rules ) && '' !== $assumedbackground ) { - $rules['background'] = $assumedbackground; - } - - if ( isset( $rules ) ) { - - // reverse array if background color is before background. - if ( strpos( $element->getAttribute( 'style' ), 'background-color:' ) > strpos( $element->getAttribute( 'style' ), 'background:' ) ) { - $rules = array_reverse( $rules ); - } - - $preference = edac_deteremine_hierarchy( $rules ); - - if ( 'background' === $preference ) { - $background = edac_check_color_match2( $rules['background'] ); - } elseif ( 'background-color' === $preference ) { - $background = $rules['background-color']; - } else { - return 1; - } - - // get foreground color. - preg_match( '/[\s|\"|\']*[^-]color:\s*(#(?:[0-9a-f]{2}){2,4}|#[0-9a-f]{3}|(?:rgba?|hsla?)\((?:\d+%?(?:deg|rad|grad|turn)?(?:,|\s)+){2,3}[\s\/]*[\d\.]+%?\)\s*(!important)*)/i', ' ' . $element->getAttribute( 'style' ), $matches, PREG_OFFSET_CAPTURE ); - - if ( isset( $matches[1][0] ) && '' !== $matches[1][0] ) { - $foreground = $matches[1][0]; - } - - // get font size. - $font_size = null; - - if ( stristr( $element->getAttribute( 'style' ), 'font-size:' ) ) { - - $fontsearchpatterns[] = '|font\-size:\s?([\d]+)pt|i'; - $fontsearchpatterns[] = '|font\-size:\s?([\d]+)px|i'; - $fontsearchpatterns[] = '|font:\s?[\w\s\d*\s]*([\d]+)pt|i'; - $fontsearchpatterns[] = '|font:\s?[\w\s\d*\s]*([\d]+)px|i'; - - // Get font size. - // 1 px = 0.75 point; 1 point = 1.333333 px. - foreach ( $fontsearchpatterns as $pattern ) { - if ( preg_match_all( $pattern, $element, $matches, PREG_PATTERN_ORDER ) ) { - $matchsize = count( $matches ); - for ( $i = 0; $i < $matchsize; $i++ ) { - if ( isset( $matches[0][ $i ] ) && '' !== $matches[0][ $i ] ) { - $absolute_fontsize_errorcode = htmlspecialchars( $matches[0][ $i ] ); - - preg_match_all( '!\d+!', $absolute_fontsize_errorcode, $matches ); - - // convert pixels to points. - if ( stristr( $absolute_fontsize_errorcode, 'px' ) === 'px' ) { - $font_size = implode( ' ', $matches[0] ) * 0.75; - } - - if ( stristr( $absolute_fontsize_errorcode, 'pt' ) === 'pt' ) { - $font_size = implode( ' ', $matches[0] ); - } - } - } - } - } - } - - // get font weight. - $font_bold = false; - - if ( - preg_match( '(bold|bolder|700|800|900)', stristr( $element->getAttribute( 'style' ), 'font-weight:' ) ) === 1 || - $element->find( 'b' ) || - $element->find( 'strong' ) - ) { - $font_bold = true; - } - - // ratio. - $ratio = 4.5; - if ( ( $font_size >= 14 && true === $font_bold ) || $font_size >= 18 ) { - $ratio = 3; - } - - if ( '' !== $foreground && 'initial' !== $background && 'inherit' !== $background && 'transparent' !== $background ) { - - if ( edac_coldiff( $foreground, $background, $ratio ) ) { - - $errors[] = $element->outertext; - } - } - } - } - } - - // check styles. - if ( $content['css_parsed'] ) { - $errors = array_merge( edac_check_contrast( $content ), $errors ); - } - - return $errors; -} - -/** - * Scan the content from a css file or style tag inside a post - * - * @param array $content to be checked. - * @return array - */ -function edac_check_contrast( $content ) { - $dom = $content['html']; - $errors = []; - $error_code = ''; - $css_array = $content['css_parsed']; - - foreach ( $css_array as $element => $rules ) { - - if ( array_key_exists( 'color', $rules ) ) { - - $background = ''; - $foreground = edac_replace_css_variables( $rules['color'], $css_array ); - - // determin which rule has preference if both background and background-color are present. - $preference = edac_deteremine_hierarchy( $rules ); - - if ( array_key_exists( 'background', $rules ) && 'background' === $preference ) { - $rules['background'] = edac_replace_css_variables( $rules['background'], $css_array ); - $background = edac_check_color_match2( $rules['background'] ); - } - - if ( array_key_exists( 'background-color', $rules ) && 'background-color' === $preference ) { - $rules['background-color'] = edac_replace_css_variables( $rules['background-color'], $css_array ); - $background = $rules['background-color']; - } - - // if background color not set exit. - if ( 'initial' === $background || 'inherit' === $background || 'transparent' === $background || '' === $background || '' === $foreground ) { - continue; - } - - // get font size. - $font_size = 0; - - if ( array_key_exists( 'font-size', $rules ) ) { - - $rules['font-size'] = edac_replace_css_variables( $rules['font-size'], $css_array ); - - $unit = str_replace( '.', '', preg_replace( '/\d/', '', $rules['font-size'] ) ); - $value = str_replace( $unit, '', $rules['font-size'] ); - - if ( 'px' === $unit ) { - $font_size = (float) $value * 0.75; - } - - if ( 'pt' === $unit ) { - $font_size = $value; - } - } - - // get font weight. - $font_bold = false; - - if ( array_key_exists( 'font-weight', $rules ) ) { - - $rules['font-weight'] = edac_replace_css_variables( $rules['font-weight'], $css_array ); - - if ( - 'bold' === $rules['font-weight'] || - 'bolder' === $rules['font-weight'] || - '700' === $rules['font-weight'] || - '800' === $rules['font-weight'] || - '900' === $rules['font-weight'] - ) { - $font_bold = true; - } - } - - // check for bold or strong tags within element. - $bold_elements = $dom->find( $element ); - if ( $bold_elements ) { - foreach ( $bold_elements as $bold_element ) { - if ( $bold_element->find( 'b' ) || $bold_element->find( 'strong' ) ) { - $font_bold = true; - } - } - } - - // ratio. - $ratio = 4.5; - if ( ( $font_size >= 14 && true === $font_bold ) || $font_size >= 18 ) { - $ratio = 3; - } - - if ( edac_coldiff( $foreground, $background, $ratio ) ) { - - $error_code = $element . ' { '; - foreach ( $rules as $key => $value ) { - $error_code .= $key . ': ' . $value . '; '; - } - $error_code .= '}'; - - $els = $dom->find( '*[' . $element . ']' ); - if ( $els ) { - foreach ( $els as $el ) { - $errors[] = $el->outertext . ' ' . $error_code; - } - } - } - } - } - return $errors; -} - -/** - * Determine rule hierarchy - * - * @param array $rules array of rules. - * @return string - */ -function edac_deteremine_hierarchy( $rules ) { - $first = ''; - $rules = array_reverse( $rules ); - - foreach ( $rules as $key => $value ) { - if ( ( 'background' === $key || 'background-color' === $key ) && '' !== $value ) { - $first = $key; - break; - } - } - - // if background color has preference and is marked important then return background color. - if ( 'background-color' === $first && stristr( $rules['background-color'], '!important' ) ) { - return 'background-color'; - } - - // if background has preference and is not marked important but background color is then return background color. - if ( - 'background' === $first && ! stristr( $rules['background'], '!important' ) - && ( array_key_exists( 'background-color', $rules ) && stristr( $rules['background-color'], '!important' ) ) - ) { - return 'background-color'; - } - - // if background color has preference but is not marked important but background is and has a color value then return background. - if ( - 'background-color' === $first && ! stristr( $rules['background-color'], '!important' ) - && ( array_key_exists( 'background', $rules ) - && stristr( $rules['background'], '!important' ) - && edac_check_color_match2( $rules['background'] ) ) - ) { - return 'background'; - } - - if ( 'background' === $first && edac_check_color_match2( $rules['background'] ) ) { - return 'background'; - } - if ( array_key_exists( 'background-color', $rules ) ) { - return 'background-color'; - } - - return $first; -} - -/** - * Check the color contrast - * - * @param string $foreground color hex. - * @param string $background color hex. - * @param int $ratio number. - * @return bool - */ -function edac_coldiff( $foreground, $background, $ratio ) { - - // convert color names to hex. - $foreground = trim( edac_convert_color_names( $foreground ) ); - $background = trim( edac_convert_color_names( $background ) ); - - // convert hex to rgb. - $color1 = edac_hex_to_rgb( $foreground ); - $color2 = edac_hex_to_rgb( $background ); - - $dif = edac_test_color_diff( $color1, $color2 ); - - // failed. - if ( $dif < $ratio ) { - if ( $dif < $ratio ) { - return 1; - } - } - - return 0; -} - -/** - * Test color contrast - * - * @param array $color1 rgb color. - * @param array $color2 rgb color. - * @return int - */ -function edac_test_color_diff( $color1, $color2 ) { - $l1 = 0.2126 * pow( $color1['r'] / 255, 2.2 ) + - 0.7152 * pow( $color1['g'] / 255, 2.2 ) + - 0.0722 * pow( $color1['b'] / 255, 2.2 ); - - $l2 = 0.2126 * pow( $color2['r'] / 255, 2.2 ) + - 0.7152 * pow( $color2['g'] / 255, 2.2 ) + - 0.0722 * pow( $color2['b'] / 255, 2.2 ); - - return ( $l1 > $l2 ) - ? ( ( $l1 + 0.05 ) / ( $l2 + 0.05 ) ) + 0.05 - : ( ( $l2 + 0.05 ) / ( $l1 + 0.05 ) ) + 0.05; -} - -/** - * Convert hex to rgb - * - * @param string $hex code. - * @param boolean $alpha is alpha or not. - * @return string - */ -function edac_hex_to_rgb( $hex, $alpha = false ) { - $hex = str_replace( '#', '', $hex ); - $length = strlen( $hex ); - $rgb['r'] = hexdec( 6 === $length ? substr( $hex, 0, 2 ) : ( 3 === $length ? str_repeat( substr( $hex, 0, 1 ), 2 ) : 0 ) ); - $rgb['g'] = hexdec( 6 === $length ? substr( $hex, 2, 2 ) : ( 3 === $length ? str_repeat( substr( $hex, 1, 1 ), 2 ) : 0 ) ); - $rgb['b'] = hexdec( 6 === $length ? substr( $hex, 4, 2 ) : ( 3 === $length ? str_repeat( substr( $hex, 2, 1 ), 2 ) : 0 ) ); - if ( $alpha ) { - $rgb['a'] = $alpha; - } - return $rgb; -} - -/** - * Convert color names to hex - * - * @param string $color_name string of color name. - * @return string - */ -function edac_convert_color_names( $color_name ) { - - if ( stristr( $color_name, 'rgb(' ) ) { - $color_name = str_replace( 'rgb(', '', $color_name ); - $color_name = str_replace( ')', '', $color_name ); - $rgb = explode( ',', $color_name ); - return '#' . sprintf( '%02x', $rgb['0'] ) . sprintf( '%02x', $rgb['1'] ) . sprintf( '%02x', $rgb['2'] ); - } - - // standard 147 HTML color names. - $colors = [ - 'aliceblue' => 'F0F8FF', - 'antiquewhite' => 'FAEBD7', - 'aqua' => '00FFFF', - 'aquamarine' => '7FFFD4', - 'azure' => 'F0FFFF', - 'beige' => 'F5F5DC', - 'bisque' => 'FFE4C4', - 'black' => '000000', - 'blanchedalmond ' => 'FFEBCD', - 'blue' => '0000FF', - 'blueviolet' => '8A2BE2', - 'brown' => 'A52A2A', - 'burlywood' => 'DEB887', - 'cadetblue' => '5F9EA0', - 'chartreuse' => '7FFF00', - 'chocolate' => 'D2691E', - 'coral' => 'FF7F50', - 'cornflowerblue' => '6495ED', - 'cornsilk' => 'FFF8DC', - 'crimson' => 'DC143C', - 'cyan' => '00FFFF', - 'darkblue' => '00008B', - 'darkcyan' => '008B8B', - 'darkgoldenrod' => 'B8860B', - 'darkgray' => 'A9A9A9', - 'darkgreen' => '006400', - 'darkgrey' => 'A9A9A9', - 'darkkhaki' => 'BDB76B', - 'darkmagenta' => '8B008B', - 'darkolivegreen' => '556B2F', - 'darkorange' => 'FF8C00', - 'darkorchid' => '9932CC', - 'darkred' => '8B0000', - 'darksalmon' => 'E9967A', - 'darkseagreen' => '8FBC8F', - 'darkslateblue' => '483D8B', - 'darkslategray' => '2F4F4F', - 'darkslategrey' => '2F4F4F', - 'darkturquoise' => '00CED1', - 'darkviolet' => '9400D3', - 'deeppink' => 'FF1493', - 'deepskyblue' => '00BFFF', - 'dimgray' => '696969', - 'dimgrey' => '696969', - 'dodgerblue' => '1E90FF', - 'firebrick' => 'B22222', - 'floralwhite' => 'FFFAF0', - 'forestgreen' => '228B22', - 'fuchsia' => 'FF00FF', - 'gainsboro' => 'DCDCDC', - 'ghostwhite' => 'F8F8FF', - 'gold' => 'FFD700', - 'goldenrod' => 'DAA520', - 'gray' => '808080', - 'green' => '008000', - 'greenyellow' => 'ADFF2F', - 'grey' => '808080', - 'honeydew' => 'F0FFF0', - 'hotpink' => 'FF69B4', - 'indianred' => 'CD5C5C', - 'indigo' => '4B0082', - 'ivory' => 'FFFFF0', - 'khaki' => 'F0E68C', - 'lavender' => 'E6E6FA', - 'lavenderblush' => 'FFF0F5', - 'lawngreen' => '7CFC00', - 'lemonchiffon' => 'FFFACD', - 'lightblue' => 'ADD8E6', - 'lightcoral' => 'F08080', - 'lightcyan' => 'E0FFFF', - 'lightgoldenrodyellow' => 'FAFAD2', - 'lightgray' => 'D3D3D3', - 'lightgreen' => '90EE90', - 'lightgrey' => 'D3D3D3', - 'lightpink' => 'FFB6C1', - 'lightsalmon' => 'FFA07A', - 'lightseagreen' => '20B2AA', - 'lightskyblue' => '87CEFA', - 'lightslategray' => '778899', - 'lightslategrey' => '778899', - 'lightsteelblue' => 'B0C4DE', - 'lightyellow' => 'FFFFE0', - 'lime' => '00FF00', - 'limegreen' => '32CD32', - 'linen' => 'FAF0E6', - 'magenta' => 'FF00FF', - 'maroon' => '800000', - 'mediumaquamarine' => '66CDAA', - 'mediumblue' => '0000CD', - 'mediumorchid' => 'BA55D3', - 'mediumpurple' => '9370D0', - 'mediumseagreen' => '3CB371', - 'mediumslateblue' => '7B68EE', - 'mediumspringgreen' => '00FA9A', - 'mediumturquoise' => '48D1CC', - 'mediumvioletred' => 'C71585', - 'midnightblue' => '191970', - 'mintcream' => 'F5FFFA', - 'mistyrose' => 'FFE4E1', - 'moccasin' => 'FFE4B5', - 'navajowhite' => 'FFDEAD', - 'navy' => '000080', - 'oldlace' => 'FDF5E6', - 'olive' => '808000', - 'olivedrab' => '6B8E23', - 'orange' => 'FFA500', - 'orangered' => 'FF4500', - 'orchid' => 'DA70D6', - 'palegoldenrod' => 'EEE8AA', - 'palegreen' => '98FB98', - 'paleturquoise' => 'AFEEEE', - 'palevioletred' => 'DB7093', - 'papayawhip' => 'FFEFD5', - 'peachpuff' => 'FFDAB9', - 'peru' => 'CD853F', - 'pink' => 'FFC0CB', - 'plum' => 'DDA0DD', - 'powderblue' => 'B0E0E6', - 'purple' => '800080', - 'red' => 'FF0000', - 'rosybrown' => 'BC8F8F', - 'royalblue' => '4169E1', - 'saddlebrown' => '8B4513', - 'salmon' => 'FA8072', - 'sandybrown' => 'F4A460', - 'seagreen' => '2E8B57', - 'seashell' => 'FFF5EE', - 'sienna' => 'A0522D', - 'silver' => 'C0C0C0', - 'skyblue' => '87CEEB', - 'slateblue' => '6A5ACD', - 'slategray' => '708090', - 'slategrey' => '708090', - 'snow' => 'FFFAFA', - 'springgreen' => '00FF7F', - 'steelblue' => '4682B4', - 'tan' => 'D2B48C', - 'teal' => '008080', - 'thistle' => 'D8BFD8', - 'tomato' => 'FF6347', - 'turquoise' => '40E0D0', - 'violet' => 'EE82EE', - 'wheat' => 'F5DEB3', - 'white' => 'FFFFFF', - 'whitesmoke' => 'F5F5F5', - 'yellow' => 'FFFF00', - 'yellowgreen' => '9ACD32', - ]; - - $color_name = strtolower( $color_name ); - $color_name = trim( str_replace( '!important', '', $color_name ) ); - - if ( isset( $colors[ $color_name ] ) ) { - return ( '#' . $colors[ $color_name ] ); - } - return ( $color_name ); -} - -/** - * Check background style for color - * - * @param string $background_rule string of background. - * @return string - */ -function edac_check_color_match2( $background_rule ) { - - // standard 147 HTML color names. - $colors = [ - 'aliceblue' => 'F0F8FF', - 'antiquewhite' => 'FAEBD7', - 'aqua' => '00FFFF', - 'aquamarine' => '7FFFD4', - 'azure' => 'F0FFFF', - 'beige' => 'F5F5DC', - 'bisque' => 'FFE4C4', - 'black' => '000000', - 'blanchedalmond ' => 'FFEBCD', - 'blue' => '0000FF', - 'blueviolet' => '8A2BE2', - 'brown' => 'A52A2A', - 'burlywood' => 'DEB887', - 'cadetblue' => '5F9EA0', - 'chartreuse' => '7FFF00', - 'chocolate' => 'D2691E', - 'coral' => 'FF7F50', - 'cornflowerblue' => '6495ED', - 'cornsilk' => 'FFF8DC', - 'crimson' => 'DC143C', - 'cyan' => '00FFFF', - 'darkblue' => '00008B', - 'darkcyan' => '008B8B', - 'darkgoldenrod' => 'B8860B', - 'darkgray' => 'A9A9A9', - 'darkgreen' => '006400', - 'darkgrey' => 'A9A9A9', - 'darkkhaki' => 'BDB76B', - 'darkmagenta' => '8B008B', - 'darkolivegreen' => '556B2F', - 'darkorange' => 'FF8C00', - 'darkorchid' => '9932CC', - 'darkred' => '8B0000', - 'darksalmon' => 'E9967A', - 'darkseagreen' => '8FBC8F', - 'darkslateblue' => '483D8B', - 'darkslategray' => '2F4F4F', - 'darkslategrey' => '2F4F4F', - 'darkturquoise' => '00CED1', - 'darkviolet' => '9400D3', - 'deeppink' => 'FF1493', - 'deepskyblue' => '00BFFF', - 'dimgray' => '696969', - 'dimgrey' => '696969', - 'dodgerblue' => '1E90FF', - 'firebrick' => 'B22222', - 'floralwhite' => 'FFFAF0', - 'forestgreen' => '228B22', - 'fuchsia' => 'FF00FF', - 'gainsboro' => 'DCDCDC', - 'ghostwhite' => 'F8F8FF', - 'gold' => 'FFD700', - 'goldenrod' => 'DAA520', - 'gray' => '808080', - 'green' => '008000', - 'greenyellow' => 'ADFF2F', - 'grey' => '808080', - 'honeydew' => 'F0FFF0', - 'hotpink' => 'FF69B4', - 'indianred' => 'CD5C5C', - 'indigo' => '4B0082', - 'ivory' => 'FFFFF0', - 'khaki' => 'F0E68C', - 'lavender' => 'E6E6FA', - 'lavenderblush' => 'FFF0F5', - 'lawngreen' => '7CFC00', - 'lemonchiffon' => 'FFFACD', - 'lightblue' => 'ADD8E6', - 'lightcoral' => 'F08080', - 'lightcyan' => 'E0FFFF', - 'lightgoldenrodyellow' => 'FAFAD2', - 'lightgray' => 'D3D3D3', - 'lightgreen' => '90EE90', - 'lightgrey' => 'D3D3D3', - 'lightpink' => 'FFB6C1', - 'lightsalmon' => 'FFA07A', - 'lightseagreen' => '20B2AA', - 'lightskyblue' => '87CEFA', - 'lightslategray' => '778899', - 'lightslategrey' => '778899', - 'lightsteelblue' => 'B0C4DE', - 'lightyellow' => 'FFFFE0', - 'lime' => '00FF00', - 'limegreen' => '32CD32', - 'linen' => 'FAF0E6', - 'magenta' => 'FF00FF', - 'maroon' => '800000', - 'mediumaquamarine' => '66CDAA', - 'mediumblue' => '0000CD', - 'mediumorchid' => 'BA55D3', - 'mediumpurple' => '9370D0', - 'mediumseagreen' => '3CB371', - 'mediumslateblue' => '7B68EE', - 'mediumspringgreen' => '00FA9A', - 'mediumturquoise' => '48D1CC', - 'mediumvioletred' => 'C71585', - 'midnightblue' => '191970', - 'mintcream' => 'F5FFFA', - 'mistyrose' => 'FFE4E1', - 'moccasin' => 'FFE4B5', - 'navajowhite' => 'FFDEAD', - 'navy' => '000080', - 'oldlace' => 'FDF5E6', - 'olive' => '808000', - 'olivedrab' => '6B8E23', - 'orange' => 'FFA500', - 'orangered' => 'FF4500', - 'orchid' => 'DA70D6', - 'palegoldenrod' => 'EEE8AA', - 'palegreen' => '98FB98', - 'paleturquoise' => 'AFEEEE', - 'palevioletred' => 'DB7093', - 'papayawhip' => 'FFEFD5', - 'peachpuff' => 'FFDAB9', - 'peru' => 'CD853F', - 'pink' => 'FFC0CB', - 'plum' => 'DDA0DD', - 'powderblue' => 'B0E0E6', - 'purple' => '800080', - 'red' => 'FF0000', - 'rosybrown' => 'BC8F8F', - 'royalblue' => '4169E1', - 'saddlebrown' => '8B4513', - 'salmon' => 'FA8072', - 'sandybrown' => 'F4A460', - 'seagreen' => '2E8B57', - 'seashell' => 'FFF5EE', - 'sienna' => 'A0522D', - 'silver' => 'C0C0C0', - 'skyblue' => '87CEEB', - 'slateblue' => '6A5ACD', - 'slategray' => '708090', - 'slategrey' => '708090', - 'snow' => 'FFFAFA', - 'springgreen' => '00FF7F', - 'steelblue' => '4682B4', - 'tan' => 'D2B48C', - 'teal' => '008080', - 'thistle' => 'D8BFD8', - 'tomato' => 'FF6347', - 'turquoise' => '40E0D0', - 'violet' => 'EE82EE', - 'wheat' => 'F5DEB3', - 'white' => 'FFFFFF', - 'whitesmoke' => 'F5F5F5', - 'yellow' => 'FFFF00', - 'yellowgreen' => '9ACD32', - ]; - - $background_rule = strtolower( $background_rule ); - $background_rule = trim( str_replace( '!important', '', $background_rule ) ); - - $rules = explode( ' ', $background_rule ); - - foreach ( $rules as $rule ) { - - if ( array_key_exists( $rule, $colors ) ) { - return $colors[ $rule ]; - } - - if ( preg_match( '/(rgb\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}\)|\#[\w]{3,6})/i', $rule, $matches, PREG_OFFSET_CAPTURE ) ) { - return $matches[1][0]; - } - } - return ''; -} diff --git a/includes/validate.php b/includes/validate.php index 8e08068a..78dd12be 100644 --- a/includes/validate.php +++ b/includes/validate.php @@ -115,10 +115,28 @@ function edac_validate( $post_ID, $post, $action ) { return; } + /** + * Allows to hook in before the validation process starts for a post. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param string $action The action being performed. + */ do_action( 'edac_before_validate', $post_ID, $action ); // apply filters to content. $content = edac_get_content( $post ); + + /** + * Allows to hook in after the content has been retrieved for a post. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param array $content The content being retrieved. + * @param string $action The action being performed. + */ do_action( 'edac_after_get_content', $post_ID, $content, $action ); if ( ! $content['html'] ) { @@ -145,6 +163,15 @@ function edac_validate( $post_ID, $post, $action ) { if ( ( array_key_exists( 'ruleset', $rule ) && 'php' === $rule['ruleset'] ) || ( ! array_key_exists( 'ruleset', $rule ) && $rule['slug'] ) ) { + /** + * Allows to hook in before the rule has been run on the content. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param array $rule The rule being validated against the content. + * @param string $action The action being performed. + */ do_action( 'edac_before_rule', $post_ID, $rule, $action ); if ( EDAC_DEBUG === true ) { $rule_process_time = microtime( true ); @@ -152,6 +179,16 @@ function edac_validate( $post_ID, $post, $action ) { $errors = call_user_func( 'edac_rule_' . $rule['slug'], $content, $post ); if ( $errors && is_array( $errors ) ) { + /** + * Allows to hook in after the rule has been and get the errors list. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param array $rule The rule being validated against the content. + * @param array $errors The errors list generated by this rule from the content. + * @param string $action The action being performed. + */ do_action( 'edac_rule_errors', $post_ID, $rule, $errors, $action ); foreach ( $errors as $error ) { ( new Insert_Rule_Data() )->insert( $post, $rule['slug'], $rule['rule_type'], $object = $error ); @@ -161,6 +198,16 @@ function edac_validate( $post_ID, $post, $action ) { $time_elapsed_secs = microtime( true ) - $rule_process_time; $rule_performance_results[ $rule['slug'] ] = $time_elapsed_secs; } + + /** + * Allows to hook in after the rule has been run on the content. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param array $rule The rule being validated against the content. + * @param string $action The action being performed. + */ do_action( 'edac_after_rule', $post_ID, $rule, $action ); } } @@ -179,6 +226,14 @@ function edac_validate( $post_ID, $post, $action ) { // set post meta checked. update_post_meta( $post_ID, '_edac_post_checked', true ); + /** + * Allows to hook in after the validation process has completed for a post. + * + * @since 1.4.0 + * + * @param int $post_ID The ID of the post being saved. + * @param string $action The action being performed. + */ do_action( 'edac_after_validate', $post_ID, $action ); } @@ -292,9 +347,10 @@ function edac_get_content( $post ) { * * For site security it is not recommended to use this filter in production. * - * @param bool $no_verify_ssl The boolean to check. + * @since 1.4.0 + * + * @param bool $no_verify_ssl True if verify SSL should be disabled (as it must be in loopback connections), false if not. */ - $no_verify_ssl = apply_filters( 'edac_no_verify_ssl', $is_local_loopback ); if ( $no_verify_ssl ) { diff --git a/package.json b/package.json index 10daa09e..5e1bee1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "accessibility-checker", - "version": "1.11.2", + "version": "1.12.0", "description": "Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance.", "author": "Equalize Digital", "license": "GPL-2.0+", diff --git a/partials/custom-meta-box.php b/partials/custom-meta-box.php index 3bf7b5b8..f7cc0a49 100644 --- a/partials/custom-meta-box.php +++ b/partials/custom-meta-box.php @@ -7,13 +7,58 @@ ?>
-
    -
  • -
  • -
  • +

    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    -
    - - +
    + +
- diff --git a/partials/settings-page.php b/partials/settings-page.php index 28749166..0e83afc9 100644 --- a/partials/settings-page.php +++ b/partials/settings-page.php @@ -5,7 +5,13 @@ * @package Accessibility_Checker */ -// set up tab items and filter them. +/** + * Filter the settings tab items. + * + * @since 1.4.0 + * + * @param array $settings_tab_items The settings tab items as an array of arrays. Needs a 'slug', 'label', and 'order'. + */ $settings_tab_items = apply_filters( 'edac_filter_settings_tab_items', [ @@ -88,7 +94,19 @@ function ( $a, $b ) {

- +
diff --git a/readme.txt b/readme.txt index e132babd..46147899 100644 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: equalizedigital, alh0319, stevejonesdev Tags: accessibility, accessible, wcag, ada, WP accessibility Requires at least: 6.2 -Tested up to: 6.5.2 -Stable tag: 1.11.2 +Tested up to: 6.5.3 +Stable tag: 1.12.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -171,6 +171,11 @@ No, Accessibility Checker runs completely on your server and does not require yo == Changelog == += 1.12.0 = +* Fixed: Use the last generation time in summary widgets rather than last completed scan time +* Improved: More accessible panels in the editor +* Improved: Filter and action docs added/improved + = 1.11.2 * Fixed: Avoid displaying `0th` for readability score * Removed: Some custom WP Playground detection code @@ -208,39 +213,5 @@ No, Accessibility Checker runs completely on your server and does not require yo * Created: Class to handle data purging and cleanup * Deprecated: `edac_delete_post`, `edac_delete_post_meta`, `edac_delete_cpt_posts` functions -= 1.9.3 = -* Updated: capability checks for the welcome page, dashboard widget, and admin notices - -= 1.9.2 = -* Fixed: filtered rules are not passed to the frontend highlighter, avoiding 'null' state issues -* Updated: frontend highlighter buttons to be disabled until issues are confirmed -* Updated: frontend highlighter buttons to show only after we know there are issues to display -* Updated: frontend highlighter to not show buttons if none are returned - -= 1.9.1 = -* Updated: `edac_include_rules_files to fire on init action to fix the `edac_filter_register_rules` filter timing - -= 1.9.0 = -* Created: class that creates the accessibility statement on activation -* Removed: custom database query that checked for existing accessibility statement in exchange for the `get_page_by_path()` function -* Fixed: bug with trying to compare the simplified summary ordinal value and added fallback -* Removed: `wp_send_json_error()` from `simplified_summary` Ajax function when the simplified summary is empty -* Added: simplified summary grade*level, message, and icon logic to the `summary()` Ajax -* Fixed: issue with the submit button text showing as `Submit Query` in Firefox. -* Updated: missing transcript rule to skip certain types of links -* Added: missing UTM parameters to the welcome page URLs. -* Removed: legacy system information code -* Removed: cbschuld/browser.php composer package -* Added: class structure for site health -* Added: site health health information for free, pro, and audit history plugins -* Added: update database class -* Removed: `edac_before_page_render` functions from the main file -* Added: frontend validate class -* Added: frontend validate unit tests -* Removed: unused new window warning meta update functions -* Fixed: front end highlight focus issue -* Added: summary generator class to replace the `edac_summary()` function -* Deprecated: `edac_summary()` function - Older versions can be found in the plugins `changelog.txt`. diff --git a/src/admin/index.js b/src/admin/index.js index fef24af0..14cba42a 100644 --- a/src/admin/index.js +++ b/src/admin/index.js @@ -1,5 +1,10 @@ /* eslint-disable no-unused-vars */ +import { + clearAllTabsAndPanelState, + initSummaryTabKeyboardAndClickHandlers, +} from './summary/summary-tab-input-event-handlers'; + // eslint-disable-next-line camelcase const edacScriptVars = edac_script_vars; @@ -47,15 +52,20 @@ const edacScriptVars = edac_script_vars; } ); jQuery( window ).on( 'load', function() { - // Allow other js to trigger a tab refresh thru an event listener. Refactor. + // Allow other js to trigger a tab refresh through an event listener. Refactor. const refreshTabDetails = () => { // reset to first meta box tab - jQuery( '.edac-panel' ).hide(); - jQuery( '.edac-panel' ).removeClass( 'active' ); - jQuery( '.edac-tab a' ).removeClass( 'active' ); - jQuery( '#edac-summary' ).show(); - jQuery( '#edac-summary' ).addClass( 'active' ); - jQuery( '.edac-tab:first-child a' ).addClass( 'active' ); + clearAllTabsAndPanelState(); + + const summaryPanel = jQuery( '#edac-summary-panel' ); + jQuery( summaryPanel ) + .show() + .addClass( 'active' ); + const summaryTab = '#' + jQuery( summaryPanel ).attr( 'aria-labelledby' ); + jQuery( summaryTab ) + .addClass( 'active' ) + .attr( 'aria-selected', 'true' ) + .removeAttr( 'tabindex' ); edacDetailsAjax(); refreshSummaryAndReadability(); @@ -76,30 +86,6 @@ const edacScriptVars = edac_script_vars; } ); }; - jQuery( '.edac-tab' ).click( function( e ) { - e.preventDefault(); - const id = jQuery( 'a', this ).attr( 'href' ); - - jQuery( '.edac-panel' ).hide(); - jQuery( '.edac-panel' ).removeClass( 'active' ); - jQuery( '.edac-tab a' ) - .removeClass( 'active' ) - .attr( 'aria-current', false ); - jQuery( id ).show(); - jQuery( id ).addClass( 'active' ); - jQuery( 'a', this ).addClass( 'active' ).attr( 'aria-current', true ); - } ); - - // Details Tab on click Ajax - jQuery( '.edac-tab-details' ).click( function() { - edacDetailsAjax(); - } ); - - // Summary Tab on click Ajax - jQuery( '.edac-tab-summary' ).click( function() { - refreshSummaryAndReadability(); - } ); - /** * Ajax Summary * @param {Function} callback - Callback function to run after ajax is complete @@ -157,7 +143,7 @@ const edacScriptVars = edac_script_vars; if ( true === response.success ) { const responseJSON = jQuery.parseJSON( response.data ); - jQuery( '.edac-details' ).html( responseJSON ); + jQuery( '#edac-details-panel' ).html( responseJSON ); // Rule on click jQuery( '.edac-details-rule-title' ).click( function() { @@ -232,7 +218,7 @@ const edacScriptVars = edac_script_vars; if ( true === response.success ) { const responseJSON = jQuery.parseJSON( response.data ); - jQuery( '.edac-readability' ).html( responseJSON ); + jQuery( '#edac-readability-panel' ).html( responseJSON ); // Simplified Summary on click jQuery( '.edac-readability-simplified-summary' ).submit( @@ -580,17 +566,16 @@ const edacScriptVars = edac_script_vars; } ); } - if ( jQuery( '.edac-summary' ).length ) { + if ( jQuery( '#edac-summary-panel' ).length ) { refreshSummaryAndReadability(); - } - if ( jQuery( '.edac-details' ).length ) { edacDetailsAjax(); ignoreSubmit(); } + if ( jQuery( '.edac-details-rule-records-record-ignore' ).length ) { ignoreSubmit(); } - if ( jQuery( '.edac-readability' ).length ) { + if ( jQuery( '#edac-readability-panel' ).length ) { refreshSummaryAndReadability(); } @@ -644,6 +629,11 @@ const edacScriptVars = edac_script_vars; }( jQuery ) ); window.addEventListener( 'load', function() { + if ( document.getElementById( 'edac-tabs' ) ) { + // bind events for the summary metabox tabs and panels. + initSummaryTabKeyboardAndClickHandlers(); + } + if ( this.document.querySelector( '.edac-widget .edac-summary' ) ) { fillDashboardWidget(); } @@ -709,26 +699,10 @@ const fillDashboardWidget = () => { passedPercentageFormatted; } - // Set completedAt - const completedAt = data.stats.fullscan_completed_at; - const completedAtFormatted = - data.stats.fullscan_completed_at_formatted; - const completedAtEl = document.querySelector( - '#edac-summary-info-date' - ); - completedAtEl.textContent = completedAtFormatted; - - /* - const expires_at = data.stats.expires_at; - const now = Date.now(); - const mins_to_exp = Math.round((expires_at - Math.floor(now / 1000))/60); - const cache_hit = data.stats.cache_hit; - if(completedAtEl && completedAt){ - completedAtEl.textContent = completedAt; - completedAtEl.setAttribute('data-edac-cache-hit', cache_hit); - completedAtEl.setAttribute('data-edac-cache-mins-to-expiration', mins_to_exp + ' minutes'); - } - */ + // Set the summary cached at time for display in the dashboard widget. + const summaryCachedAt = data.stats.cached_at_formatted || data.stats.fullscan_completed_at_formatted; + const summaryCachedAtEl = document.getElementById( 'edac-summary-info-date' ); + summaryCachedAtEl.textContent = summaryCachedAt; // scanned const postsScanned = data.stats.posts_scanned; diff --git a/src/admin/sass/accessibility-checker-admin.scss b/src/admin/sass/accessibility-checker-admin.scss index 5a9cbabd..879092c0 100644 --- a/src/admin/sass/accessibility-checker-admin.scss +++ b/src/admin/sass/accessibility-checker-admin.scss @@ -63,6 +63,8 @@ .edac-tabs { margin-bottom: 0; + position: relative; + z-index: 2; } .edac-tab { @@ -70,7 +72,7 @@ margin-bottom: 0; vertical-align: bottom; - a { + > button { width: 100%; padding: 12px 18px; display: inline-block; @@ -79,14 +81,27 @@ border-bottom: none; text-decoration: none; font-size: 14px; + color: $color-blue; @include breakpoint(xs) { width: auto; } + &:hover, + &:focus { + color: $color-black; + cursor: pointer; + } + + &:focus-visible { + outline: 2px solid $color-blue; + outline-offset: 2px; + } + &.active { padding: 14px 18px; - background-color: $color-white; + color: $color-white; + background-color: $color-blue; } } } @@ -201,7 +216,7 @@ } @include breakpoint(xl) { - //transform: scale(1, 1); + //transform: scale(1, 1); } &:after { @@ -1883,4 +1898,4 @@ z-index: 9; } -} \ No newline at end of file +} diff --git a/src/admin/summary/summary-tab-input-event-handlers.js b/src/admin/summary/summary-tab-input-event-handlers.js new file mode 100644 index 00000000..ac36f759 --- /dev/null +++ b/src/admin/summary/summary-tab-input-event-handlers.js @@ -0,0 +1,104 @@ +/** + * Summary Tab Input Event Handlers + * + * @since 1.12.0 + */ + +/** + * Initialize the Summary Tab keyboard and click event handlers. + * + * Gets all tabs, adds click and keydown event listeners to each tab to support + * proper keyboard navigation and aria attributes. + * + * @since 1.12.0 + */ +export const initSummaryTabKeyboardAndClickHandlers = () => { + const tabs = document.querySelectorAll( '.edac-tab button' ); + + tabs.forEach( ( tab, index ) => { + tab.addEventListener( 'click', ( event ) => { + if ( + ! ( event.target instanceof HTMLButtonElement ) && + 'undefined' !== event.target.getAttribute( 'aria-controls' ) + ) { + return; + } + + const panel = document.querySelector( '#' + event.target.getAttribute( 'aria-controls' ) ); + if ( ! ( panel instanceof HTMLElement ) ) { + return; + } + + event.preventDefault(); + clearAllTabsAndPanelState(); + + panel.style.display = 'block'; + panel.classList.add( 'active' ); + event.target.classList.add( 'active' ); + event.target.setAttribute( 'aria-selected', true ); + event.target.removeAttribute( 'tabindex' ); + event.target.focus(); + } ); + + // all the events that result in true evaluations simply click the tab in question, + // because the tab click handler is already setup and not worth currently fully refactoring. + tab.addEventListener( 'keydown', ( event ) => { + if ( + ( event.key === 'Enter' || event.keyCode === 13 ) || + ( event.key === 'Space' || event.keyCode === 32 ) + ) { + tabs[ index ].click(); + } + + if ( event.key === 'ArrowRight' || event.keyCode === 39 ) { + let newTabIndex = index + 1; + if ( newTabIndex > tabs.length ) { + newTabIndex = 0; + } + tabs[ newTabIndex ].click(); + } + + if ( event.key === 'ArrowLeft' || event.keyCode === 37 ) { + let newTabIndex = index - 1; + if ( newTabIndex < 0 ) { + newTabIndex = tabs.length - 1; + } + tabs[ newTabIndex ].click(); + } + + if ( event.key === 'Home' || event.keyCode === 36 ) { + tabs[ 0 ].click(); + event.preventDefault(); + } + + if ( event.key === 'End' || event.keyCode === 35 ) { + tabs[ tabs.length - 1 ].click(); + event.preventDefault(); + } + } ); + } ); +}; + +/** + * Clear all tabs and panels state to inactive then set a default active tab and panel. + * + * @since 1.12.0 + */ +export const clearAllTabsAndPanelState = () => { + const panels = document.querySelectorAll( '.edac-panel' ); + if ( ! panels.length ) { + return; + } + + panels.forEach( ( panel ) => { + panel.style.display = 'none'; + panel.classList.remove( 'active' ); + panel.setAttribute( 'aria-selected', 'false' ); + const panelTab = document.querySelector( '#' + panel.getAttribute( 'aria-labelledby' ) ); + if ( panelTab ) { + panelTab.classList.remove( 'active' ); + panelTab.setAttribute( 'aria-selected', 'false' ); + panelTab.setAttribute( 'tabindex', '-1' ); + } + } ); +}; diff --git a/tests/phpunit/Admin/MetaBoxesTest.php b/tests/phpunit/Admin/MetaBoxesTest.php index 9feb4282..1c80f8e1 100644 --- a/tests/phpunit/Admin/MetaBoxesTest.php +++ b/tests/phpunit/Admin/MetaBoxesTest.php @@ -61,7 +61,10 @@ public function test_render(): void { $meta_boxes = new Meta_Boxes(); $meta_boxes->render(); - $this->expectOutputRegex( '/^
/' ); + $this->expectOutputRegex( '/^
expectOutputRegex( '/role="tablist"/' ); + $this->expectOutputRegex( '/role="tab"/' ); + $this->expectOutputRegex( '/role="tabpanel"/' ); } /**