current_time( 'timestamp' ), 'results' => $results, ) ); add_post_meta( $post_id, '_tenon_test_hash', $hash ); } echo "
" . $format . '
'; } } /** * Get parameter driven arguments for A11y Check */ function am_get_arguments() { $permalink = get_the_permalink(); $level = isset( $_GET['tenon-level'] ) ? $_GET['tenon-level'] : 'AA'; $priority = isset( $_GET['tenon-priority'] ) ? $_GET['tenon-priority'] : '0'; $certainty = isset( $_GET['tenon-certainty'] ) ? $_GET['tenon-certainty'] : ''; switch ( $level ) { case 'A': case 'AA': case 'AAA': $level = $level; break; default: $level = 'AA'; } $priority = ( is_numeric( $priority ) ) ? $priority : 0; $certainty = ( is_numeric( $certainty ) ) ? $certainty : 20; $args = array( 'url' => $permalink, 'level' => $level, 'priority' => $priority, 'certainty' => $certainty, ); return apply_filters( 'access_monitor_defaults', $args ); } /** * Send test query to Tenon.io API * * @param array $post Posted data. * * @return mixed boolean/array results. */ function am_query_tenon( $post ) { // creates the $opts array from the $post data. // only sets items that are non-blank. This allows Tenon to revert to defaults. $expected_post = array( 'src', 'url', 'level', 'certainty', 'priority', 'docID', 'projectID', 'viewPortHeight', 'viewPortWidth', 'uaString', 'fragment', 'store' ); foreach ( $post as $k => $v ) { if ( in_array( $k, $expected_post, true ) ) { if ( strlen( trim( $v ) ) > 0 ) { $opts[ $k ] = $v; } } } $settings = get_option( 'am_settings' ); $key = ( is_multisite() && false !== (bool) get_site_option( 'tenon_multisite_key' ) ) ? get_site_option( 'tenon_multisite_key' ) : $settings['tenon_api_key']; if ( $key ) { $opts['key'] = $key; $tenon = new tenon( TENON_API_URL, $opts ); $tenon->submit( AM_DEBUG ); $body = $tenon->tenon_response['body']; $formatted = am_format_tenon( $body ); $object = json_decode( $body ); if ( property_exists( $object, 'resultSet' ) ) { $array = (array) $object; $results = $array['resultSet']; $errors = $array['clientScriptErrors']; } else { $results = array(); $errors = ''; } $grade = am_percentage( $object ); if ( false === $grade ) { if ( trim( $object->message ) === 'Bad Request - Either src or url parameter must be supplied' ) { $message = __( 'Save your post as a draft in order to test for accessibility.', 'access-monitor' ); } else { $info = $object->status; $message = $object->message . ': ' . $info; } $formatted = '

' . __( 'Tenon error:', 'access-monitor' ) . ' ' . $message . '' . '

'; $grade = 0; } return array( 'formatted' => $formatted, 'results' => $results, 'errors' => $errors, 'grade' => $grade, ); } else { return false; } } /** * Format result from Tenon for viewing. * * @param string $body JSON object received from Tenon. * * @return array formatted results. */ function am_format_tenon( $body ) { if ( false === $body ) { return __( 'No Tenon API Key provided', 'access-monitor' ); } $object = json_decode( $body ); if ( is_object( $object ) && property_exists( $object, 'resultSummary' ) ) { // Need to parse this into arrays due to WordPress code standards. $array = (array) $object; $summary = (array) $array['resultSummary']; $issues = (array) $summary['issues']; $errors = $issues['totalIssues']; } else { $errors = 0; } if ( property_exists( $object, 'resultSet' ) ) { $array = (array) $object; $results = $array['resultSet']; } else { $results = array(); } $return = am_format_tenon_array( $results, $errors ); return $return; } /** * Format results from tenon as HTML. * * @param array $results Array of result objects. * @param string $errors String describing errors. * * @return string HTML. */ function am_format_tenon_array( $results, $errors ) { // Translators: Number of issues identified. $return = '

' . sprintf( _n( '%s accessibility issue identified.', '%s accessibility issues identified.', $errors, 'access-monitor' ), "$errors" ) . '

'; $i = 0; if ( ! empty( $results ) ) { foreach ( $results as $result ) { $result = (array) $result; $i++; switch ( $result['certainty'] ) { case ( $result['certainty'] >= 80 ): $cert = 'high'; break; case ( $result['certainty'] >= 40 ): $cert = 'medium'; break; default: $cert = 'low'; } switch ( $result['priority'] ) { case ( $result['priority'] >= 80 ): $prio = 'high'; break; case ( $result['priority'] >= 40 ): $prio = 'medium'; break; default: $prio = 'low'; } $bpid = $result['bpID']; $tid = $result['tID']; $xpathid = md5( $result['xpath'] ); $href = esc_url( add_query_arg( array( 'bpID' => $bpid, 'tID' => $tid, ), 'http://tenon.io/bestpractice.php' ) ); $ref = '' . __( 'Read more:', 'access-monitor' ) . " $result[resultTitle]"; $standards = ''; foreach ( $result['standards'] as $guideline ) { $standards .= "
  • $guideline
  • "; } if ( '' !== $standards ) { $standards = '

    ' . __( 'Relevant Accessibility Standards', 'access-monitor' ) . "

    "; } $error_snippet = $result['errorSnippet']; $error_title = $result['errorTitle']; $error_desc = ( isset( $result['errorDescription'] ) ) ? $result['errorDescription'] : ''; $return .= "

    $i . $result[errorTitle]

    " . __( 'Certainty:', 'access-monitor' ) . " $result[certainty]%" . " " . __( 'Priority:', 'access-monitor' ) . " $result[priority]%" . "

    Error Source

    " . $error_snippet . "

    $error_desc $ref

    Xpath:

    $result[xpath]
    $standards

    Find error $i

    "; } } else { $return .= "

    Congratulations! Tenon didn't find any issues on this page.

    "; } return $return . '

    '; } add_action( 'admin_enqueue_scripts', 'am_admin_enqueue_scripts' ); /** * Enqueue JS for Access Monitor (admin). */ function am_admin_enqueue_scripts() { global $current_screen; if ( 'customize' === $current_screen->id || 'press-this' === $current_screen->id ) { // We don't want any of this on these screens. } else { // The customizer doesn't have an adminbar; so no reason to enqueue this. Also, it breaks the customizer. wp_enqueue_script( 'am.functions', plugins_url( 'js/jquery.ajax.js', __FILE__ ), array( 'jquery' ) ); wp_localize_script( 'am.functions', 'am_ajax_url', admin_url( 'admin-ajax.php' ) ); wp_localize_script( 'am.functions', 'am_ajax_action', 'am_ajax_query_tenon' ); wp_localize_script( 'am.functions', 'am_plugin_name', __( 'Access Monitor', 'access-monitor' ) ); wp_enqueue_script( 'am.view', plugins_url( 'js/view.tenon.js', __FILE__ ), array( 'jquery' ), '1.0.0', true ); wp_localize_script( 'am.view', 'ami18n', array( 'expand' => __( 'Expand', 'access-monitor' ), 'collapse' => __( 'Collapse', 'access-monitor' ), 'view' => __( 'View Error', 'access-monitor' ), 'updating' => __( 'Updating', 'access-monitor' ), 'completed' => __( 'Completed', 'access-monitor' ), ) ); wp_enqueue_style( 'am.styles', plugins_url( 'css/am-styles.css', __FILE__ ) ); } } add_action( 'wp_enqueue_scripts', 'am_wp_enqueue_scripts' ); /** * Enqueue scripts for Access Monitor (public). */ function am_wp_enqueue_scripts() { if ( ! is_admin() && isset( $_GET['tenon'] ) ) { wp_enqueue_style( 'am.styles', plugins_url( 'css/am-styles.css', __FILE__ ), array( 'dashicons' ) ); wp_enqueue_script( 'am.view', plugins_url( 'js/view.tenon.js', __FILE__ ), array( 'jquery' ), '1.0.0', true ); wp_localize_script( 'am.view', 'ami18n', array( 'expand' => __( 'Expand', 'access-monitor' ), 'collapse' => __( 'Collapse', 'access-monitor' ), ) ); } } add_action( 'wp_ajax_am_ajax_query_tenon', 'am_ajax_query_tenon' ); add_action( 'wp_ajax_nopriv_am_ajax_query_tenon', 'am_ajax_query_tenon' ); /** * AJAX query sending request to Tenon. */ function am_ajax_query_tenon() { if ( isset( $_REQUEST['tenon'] ) ) { $args = array( 'src' => stripslashes( $_REQUEST['tenon'] ) ); if ( isset( $_REQUEST['level'] ) ) { $args['level'] = $_REQUEST['level']; } if ( isset( $_REQUEST['fragment'] ) ) { $args['fragment'] = $_REQUEST['fragment']; } if ( isset( $_REQUEST['certainty'] ) ) { $args['certainty'] = $_REQUEST['certainty']; } if ( isset( $_REQUEST['priority'] ) ) { $args['priority'] = $_REQUEST['priority']; } $results = am_query_tenon( $args ); wp_send_json( array( 'response' => 1, 'results' => $results['results'], 'formatted' => $results['formatted'], 'grade' => $results['grade'], ) ); } } add_action( 'admin_footer', 'am_admin_footer' ); /** * Create admin footer container where JS results inserted. */ function am_admin_footer() { echo "
    "; } add_action( 'init', 'am_disable_admin_bar' ); /** * If Tenon is run on the front end, disable the admin bar so it isn't incorporated in tests. */ function am_disable_admin_bar() { if ( isset( $_GET['tenon'] ) ) { add_filter( 'show_admin_bar', '__return_false' ); } } add_action( 'admin_bar_menu', 'am_admin_bar', 200 ); /** * Add Tenon Accessibility Check to adminbar. */ function am_admin_bar() { $settings = get_option( 'am_settings' ); $api_key = $settings['tenon_api_key']; $multisite = get_site_option( 'tenon_multisite_key' ); if ( false !== (bool) $api_key || false !== (bool) $multisite ) { global $wp_admin_bar; if ( is_admin() ) { $url = '#tenon'; } else { global $post_id; $nonce = wp_create_nonce( 'public-tenon-query' ); $url = add_query_arg( 'tenon', $nonce, get_permalink( $post_id ) ); } $args = array( 'id' => 'tenonCheck', 'title' => __( 'A11y Check', 'access-monitor' ), 'href' => $url, ); $wp_admin_bar->add_node( $args ); } } add_action( 'init', 'am_posttypes' ); /** * Create Post Types used in Access Monitor. */ function am_posttypes() { $value = array( __( 'accessibility report', 'access-monitor' ), __( 'accessibility reports', 'access-monitor' ), __( 'Accessibility Report', 'access-monitor' ), __( 'Accessibility Reports', 'access-monitor' ), ); $labels = array( 'name' => $value[3], 'singular_name' => $value[2], 'add_new' => __( 'Add New', 'access-monitor' ), 'add_new_item' => __( 'Create New Accessibility Report', 'access-monitor' ), 'edit_item' => __( 'Modify Accessibility Report', 'access-monitor' ), 'new_item' => __( 'New Accessibility Report', 'access-monitor' ), 'view_item' => __( 'View Accessibility Report', 'access-monitor' ), 'search_items' => __( 'Search Accessibility Reports', 'access-monitor' ), 'not_found' => __( 'No accessibility reports found', 'access-monitor' ), 'not_found_in_trash' => __( 'No accessibility reports found in Trash', 'access-monitor' ), 'parent_item_colon' => '', ); $args = array( 'labels' => $labels, 'public' => false, 'show_ui' => true, 'show_in_menu' => true, 'menu_icon' => 'dashicons-universal-access', 'supports' => array( 'title' ), ); register_post_type( 'tenon-report', $args ); } add_action( 'admin_menu', 'am_add_outer_box' ); /** * Add meta boxes. */ function am_add_outer_box() { add_meta_box( 'am_report_div', __( 'Accessibility Report', 'access-monitor' ), 'am_add_inner_box', 'tenon-report', 'normal', 'high' ); add_meta_box( 'am_about_div', __( 'About this Report', 'access-monitor' ), 'am_add_about_box', 'tenon-report', 'side', 'high' ); add_meta_box( 'am_related_div', __( 'Related Reports', 'access-monitor' ), 'am_add_related_box', 'tenon-report', 'side', 'high' ); } add_action( 'add_meta_boxes', 'am_post_reports_data' ); /** * Add meta box for report data. * * @param string $type Post Type being viewed. */ function am_post_reports_data( $type ) { $types = get_post_types( array( 'public' => true ) ); if ( in_array( $type, $types, true ) ) { $settings = get_option( 'am_settings' ); $access_options_enabled = ( true === (bool) $settings['tenon_pre_publish'] ) ? true : false; if ( $access_options_enabled ) { // Disable Gutenberg if this option is enabled. add_meta_box( 'am_public_report', __( 'Accessibility Reports', 'access-monitor' ), 'am_show_public_report', $type, 'normal', 'high', array( '__block_editor_compatible_meta_box' => false ) ); } else { add_meta_box( 'am_public_report', __( 'Accessibility Reports', 'access-monitor' ), 'am_show_public_report', $type ); } } } /** * Create HTML to display report on accessibility testing for a post. */ function am_show_public_report() { global $post; $reports = get_post_meta( $post->ID, '_tenon_test_results' ); if ( empty( $reports ) ) { echo '

    ' . __( 'No manual accessibility tests have been run on this post.', 'access-monitor' ) . '

    '; } else { echo '

    ' . __( 'Only tests with changed results are shown. Duplicate results are not saved.', 'access-monitor' ) . '

    '; foreach ( $reports as $report ) { $ts = $report['date']; $date = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $report['date'] ); $grade = round( $report['results']['grade'], 2 ); $format = $report['results']['formatted']; echo "
    "; // Translators: Date of test, grade received. echo "

    " . sprintf( __( 'Test from %1$s (Grade: %2$s)', 'access-monitor' ), "$date", "$grade%" ) . '

    '; echo "'; echo "
    $format
    "; echo '
    '; } } } /** * Add record of JSON response from Tenon. */ function am_add_inner_box() { global $post; $content = stripslashes( $post->post_content ); echo '
    ' . $content . '
    '; } /** * Add list of related accessibility tests. */ function am_add_related_box() { global $post; $relatives = get_posts( array( 'post_type' => 'tenon-report', 'meta_key' => '_tenon_parent', 'meta_value' => $post->ID, ) ); $children = get_posts( array( 'post_type' => 'tenon-report', 'meta_key' => '_tenon_child', 'meta_value' => $post->ID, ) ); echo '

    ' . __( 'Child Reports', 'access-monitor' ) . '

    '; echo am_format_related_reports( $relatives, $post ); echo '

    ' . __( 'Parent Reports', 'access-monitor' ) . '

    '; echo am_format_related_reports( $children, $post ); } /** * Format related report data. * * @param array $relatives Array of post objects from query. * @param object $post Currently viewed post. * * @return string list of related posts. */ function am_format_related_reports( $relatives, $post ) { $related = ''; if ( ! empty( $relatives ) ) { foreach ( $relatives as $relative ) { $title = $relative->post_title; $id = $relative->ID; $link = get_edit_post_link( $id ); $date = get_the_time( 'M j, Y @ H:i', $id ); if ( $id !== $post->ID ) { $related .= "
  • $title: $date
  • "; } } $related = ""; } if ( empty( $relatives ) ) { $related = '

    ' . __( 'No related reports.', 'access-monitor' ) . '

    '; } return $related; } /** * About this post meta box. */ function am_add_about_box() { global $post; $urls = ''; $parameters = ''; $pages = get_post_meta( $post->ID, '_tenon_pages', true ); $params = get_post_meta( $post->ID, '_tenon_params', true ); $total = get_post_meta( $post->ID, '_tenon_total', true ); // Translators: Number of unique errors on this post. echo "

    " . sprintf( __( '%s unique errors', 'access-monitor' ), "$total" ) . '

    '; if ( is_array( $pages ) ) { foreach ( $pages as $url ) { $page = str_replace( array( 'http://', 'https://', 'http://www.', 'https://www.' ), '', $url ); $urls .= "
  • $page
  • "; } unset( $params['url'] ); foreach ( $params as $key => $value ) { $key = stripslashes( trim( $key ) ); $value = stripslashes( trim( $value ) ); if ( '' === $value ) { $value = '' . __( 'Default', 'access-monitor' ) . ''; } $label = ucfirst( $key ); $parameters .= "
  • $label: $value
  • "; } echo '

    ' . __( 'URLs Tested', 'access-monitor' ) . "

    "; echo '

    ' . __( 'Test Parameters', 'access-monitor' ) . "

    "; echo "

    " . __( 'Tenon Request Parameters', 'access-monitor' ) . '

    '; } else { echo '

    ' . __( 'No pages tested yet.', 'access-monitor' ) . '

    '; } } add_action( 'admin_menu', 'am_remove_menu_item' ); /** * Remove "Add New" from menu for Tenon Reports. */ function am_remove_menu_item() { global $submenu; unset( $submenu['edit.php?post_type=tenon-report'][10] ); // Removes 'Add New'. } /** * Insert a new report to WP database. * * @param string $name Name of report, if provided. * * @return Post ID. */ function am_set_report( $name = false ) { $date = date_i18n( 'Y-m-d H:i', current_time( 'timestamp' ) ); if ( ! $name ) { $name = $date; } else { $name = explode( ';', $name ); $name = $name[0]; $name .= '; ' . $date; } $report_id = wp_insert_post( array( 'post_content' => '', 'post_title' => $name, 'post_status' => 'draft', 'post_type' => 'tenon-report', ) ); return $report_id; } register_deactivation_hook( __FILE__, 'am_deactivate_cron' ); /** * Deactivate cron jobs on deactivation of plug-in. */ function am_deactivate_cron() { wp_clear_scheduled_hook( 'amcron' ); } add_action( 'amcron', 'am_schedule_report', 10, 4 ); /** * Execute scheduled reports. * * @param int $report_id Post containing report details. * @param array $pages Array of pages to test. * @param string $name name of report. * @param array $params report parameters. */ function am_schedule_report( $report_id, $pages, $name, $params ) { $new_report = am_generate_report( $name, $pages, $report_id, $params ); // 'none' to prevent this from being auto-scheduled again. $url = admin_url( "post.php?post=$new_report&action=edit" ); update_post_meta( $new_report, '_tenon_parent', $report_id ); add_post_meta( $report_id, '_tenon_child', $new_report ); wp_mail( apply_filters( 'am_cron_notification_email', get_option( 'admin_email' ), $name ), // Translators: Blog name. sprintf( __( 'Scheduled Accessibility Report on %s', 'access-monitor' ), get_option( 'blogname' ) ), // Translators: URL to view accessibility report. sprintf( __( 'View accessibility report: %s', 'access-monitor' ), $url ) ); } /** * Generate an accessibility report. * * @param string $name name of report. * @param mixed boolean/array $pages Array of pages to test. * @param mixed string/int $schedule Post ID containing report details or 'none'. * @param array $params report parameters. * * @return Report ID. */ function am_generate_report( $name, $pages = false, $schedule = 'none', $params = array() ) { $report_id = am_set_report( $name ); if ( is_array( $pages ) ) { $pages = $pages; } else { $pages = array( home_url() ); } if ( 'none' !== $schedule ) { if ( ! is_numeric( $schedule ) ) { $timestamp = ( 'weekly' === $schedule ) ? current_time( 'timestamp' ) + 60 * 60 * 24 * 7 : current_time( 'timestamp' ) + ( 60 * 60 * 24 * 30.5 ); $args = array( 'report_id' => $report_id, 'pages' => $pages, 'name' => $name, 'params' => $params, ); wp_schedule_event( $timestamp, $schedule, 'amcron', $args ); update_post_meta( $report_id, '_tenon_schedule', $schedule ); } else { update_post_meta( $report_id, '_tenon_schedule', $schedule ); } } foreach ( $pages as $page ) { if ( is_numeric( $page ) ) { $url = get_permalink( $page ); } else { $url = $page; } if ( esc_url( $url ) ) { $params['url'] = $url; $report = am_query_tenon( $params ); $report = $report['results']; $saved[ $url ] = $report; } else { continue; } } $data = am_format_tenon_report( $saved, $name ); $total = $data['total']; $formatted = $data['html']; remove_action( 'save_post', 'am_run_report' ); wp_update_post( array( 'ID' => $report_id, 'post_content' => $formatted, ) ); update_post_meta( $report_id, '_tenon_total', $total ); update_post_meta( $report_id, '_tenon_json', $saved ); if ( isset( $params['projectID'] ) && '' !== $params['projectID'] ) { update_post_meta( $report_id, '_tenon_projectID', $params['projectID'] ); } update_post_meta( $report_id, '_tenon_params', $params ); update_post_meta( $report_id, '_tenon_pages', $pages ); wp_publish_post( $report_id ); add_action( 'save_post', 'am_run_report' ); return $report_id; } add_action( 'save_post', 'am_run_report' ); /** * Re-run a test cycle when updating post. * * @param int $id Post ID. */ function am_run_report( $id ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE || wp_is_post_revision( $id ) || 'tenon-report' === ! ( get_post_type( $id ) ) ) { return; } $post = get_post( $id ); if ( 'publish' !== $post->post_status ) { return; } $name = get_the_title( $id ); $pages = get_post_meta( $id, '_tenon_pages', true ); $params = get_post_meta( $id, '_tenon_params', true ); $params = ( empty( $params ) ) ? array() : $params; if ( empty( $pages ) ) { return; } remove_action( 'save_post', 'am_run_report' ); $report_id = am_generate_report( $name, $pages, 'none', $params ); add_post_meta( $id, '_tenon_child', $report_id ); update_post_meta( $report_id, '_tenon_parent', $id ); add_action( 'save_post', 'am_run_report' ); } /** * Display an accessibility report. * * @param int $report_id Post ID for report. */ function am_show_report( $report_id = false ) { $report_id = ( isset( $_GET['report'] ) && is_numeric( $_GET['report'] ) ) ? $_GET['report'] : false; $output = ''; $name = ''; if ( $report_id ) { $report = get_post( $report_id ); $output = $report->post_content; $name = $report->post_title; } else { $reports = wp_get_recent_posts( array( 'numberposts' => 1, 'post_type' => 'tenon-report', 'post_status' => 'publish', ), 'OBJECT' ); $report = end( $reports ); if ( $report ) { $output = $report->post_content; $name = $report->post_title; } } if ( '' !== $output ) { echo $output; } else { $data = am_format_tenon_report( get_post_meta( $report_id, '_tenon_json', true ), $name ); $formatted = $data['html']; $total = $data['total']; wp_update_post( array( 'ID' => $report_id, 'post_content' => $formatted, ) ); update_post_meta( $report_id, '_tenon_total', $total ); echo $formatted; } } /** * Add columns about test results to reports list. * * @param array $cols Existing table columns. * * @return array $cols Modified table columns. */ function am_column( $cols ) { $cols['am_total'] = __( 'Errors', 'access-monitor' ); $cols['am_schedule'] = __( 'Schedule', 'access-monitor' ); $cols['am_tested'] = __( 'Level', 'access-monitor' ); return $cols; } /** * Return results pertinent to object in row. * * @param string $column_name Name of current column. * @param int $id Post ID for current object. */ function am_custom_column( $column_name, $id ) { switch ( $column_name ) { case 'am_total': $total = get_post_meta( $id, '_tenon_total', true ); echo $total; break; case 'am_tested': $params = get_post_meta( $id, '_tenon_params', true ); echo isset( $params['level'] ) ? $params['level'] : ''; break; case 'am_schedule': $schedule = get_post_meta( $id, '_tenon_schedule', true ); if ( is_numeric( $schedule ) ) { $edit_url = admin_url( "post.php?post=$schedule&action=edit" ); $edit_link = " " . __( 'View Original Test', 'access-monitor' ) . ''; echo $edit_link; } else { if ( $schedule ) { echo ucfirst( $schedule ); } else { _e( 'One-time report', 'access-monitor' ); } } break; } } /** * Get value for columns. * * @param string $value Current value. * @param string $column_name Column name. * @param int $id Row object ID. * * @return value. */ function am_return_value( $value, $column_name, $id ) { if ( 'am_total' === $column_name || 'am_schedule' === $column_name || 'am_level' === $column_name ) { $value = $id; } return $value; } add_action( 'admin_init', 'am_add' ); /** * Add custom filters & actions for post columns. */ function am_add() { add_filter( 'manage_tenon-report_posts_columns', 'am_column' ); add_action( 'manage_tenon-report_posts_custom_column', 'am_custom_column', 10, 2 ); } /** * Format a report from tenon. * * @param array $results Full results from Tenon. * @param string $name Name of this report. */ function am_format_tenon_report( $results, $name ) { $return = ''; $tbody = ''; $displayed = false; $i = 0; $count = 0; $total = 0; $links = ''; if ( ! empty( $results ) ) { $reported = array(); $count = count( $results ); foreach ( $results as $url => $result_set ) { $tbody = ''; $thead = ''; $url_hash = md5( $url ); $result_count = is_array( $result_set ) ? count( $result_set ) : 0; if ( $result_count > 0 ) { foreach ( $result_set as $result ) { $result = (array) $result; $i++; $hash = md5( $result['resultTitle'] . $result['ref'] . $result['errorSnippet'] . $result['xpath'] ); if ( ! in_array( $hash, $reported, true ) ) { $displayed = true; $total ++; $href = esc_url( add_query_arg( array( 'bpid' => $result['bpID'], 'tid' => $result['tID'], ), 'https://tenon.io/bestpractice.php' ) ); $ref = "$result[errorTitle]"; $tbody .= " $ref

    $result[resultTitle]; $result[errorDescription]

    $result[certainty] $result[priority]
    $result[errorSnippet]
    $result[xpath]
    "; } else { $displayed = false; } $reported[] = $hash; } if ( ! $displayed ) { // Translators: Link to where errors were found. $return .= '

    ' . sprintf( __( 'Errors found on %s.', 'access-monitor' ), "$url" ) . " ($result_count)"; // Translators: Count of errors found that were duplicates of other page errors. $return .= '

    ' . sprintf( __( 'The %d errors found on this page were also found on other pages tested.', 'access-monitor' ), $result_count ) . '

    '; } else { $thead = ""; // Translators: Link to where errors found, number of errors found on that page. $links .= "
  • " . sprintf( __( 'Results for %1$s (%2$d)', 'access-monitor' ), $url, $result_count ) . '
  • '; // Translators: Link to where errors were found. $thead .= '"; $thead .= " '; $tfoot = "
    ' . sprintf( __( 'Errors found on %s.', 'access-monitor' ), "$url" ) . " ($result_count)
    " . __( 'Issue', 'access-monitor' ) . " " . __( 'Certainty', 'access-monitor' ) . " " . __( 'Priority', 'access-monitor' ) . " " . __( 'Source', 'access-monitor' ) . " " . __( 'Xpath', 'access-monitor' ) . '
    " . __( 'Issue', 'access-monitor' ) . " " . __( 'Certainty', 'access-monitor' ) . " " . __( 'Priority', 'access-monitor' ) . " " . __( 'Source', 'access-monitor' ) . " " . __( 'Xpath', 'access-monitor' ) . '
    '; $return .= $thead . $tbody . $tfoot; } } else { // Translators: URL where errors were not found. $return .= '

    ' . sprintf( __( 'No errors found on %s.', 'access-monitor' ), "$url" ) . '

    '; // Translators: URL where errors were not found. $links .= "
  • " . sprintf( __( 'Results for %s (0)', 'access-monitor' ), $url ) . '
  • '; } } } else { $return .= "

    Congratulations! Tenon didn't find any issues on this page.

    "; } // Translators: number of pages tested. $header = '

    ' . stripslashes( $name ) . '; ' . sprintf( __( 'Results from %d pages tested', 'access-monitor' ), $count ) . '

    '; $header .= ''; return array( 'total' => $total, 'html' => $header . $return, ); } add_filter( 'cron_schedules', 'am_custom_schedules' ); /** * Set up custom cron schedules. * * @param array $schedules Existing schedules. * * @return array New schedules. */ function am_custom_schedules( $schedules ) { // Adds once weekly to the existing schedules. $schedules['weekly'] = array( 'interval' => 604800, 'display' => __( 'Once Weekly', 'access-monitor' ), ); $schedules['monthly'] = array( 'interval' => 2635200, 'display' => __( 'Once Monthly', 'access-monitor' ), ); return $schedules; } /** * Access Monitor settings page. */ function am_update_settings() { if ( isset( $_POST['am_settings'] ) ) { $nonce = $_REQUEST['_wpnonce']; if ( ! wp_verify_nonce( $nonce, 'access-monitor-nonce' ) ) { die( 'Security check failed' ); } $tenon_api_key = ( isset( $_POST['tenon_api_key'] ) ) ? $_POST['tenon_api_key'] : ''; $tenon_multisite_key = ( isset( $_POST['tenon_multisite_key'] ) ) ? $_POST['tenon_multisite_key'] : ''; $tenon_pre_publish = ( isset( $_POST['tenon_pre_publish'] ) ) ? 1 : 0; $am_post_types = ( isset( $_POST['am_post_types'] ) ) ? $_POST['am_post_types'] : array(); $am_criteria = ( isset( $_POST['am_criteria'] ) ) ? $_POST['am_criteria'] : array(); $am_notify = ( isset( $_POST['am_notify'] ) ) ? $_POST['am_notify'] : ''; update_site_option( 'tenon_multisite_key', $tenon_multisite_key ); update_option( 'am_settings', array( 'tenon_api_key' => $tenon_api_key, 'am_post_types' => $am_post_types, 'tenon_pre_publish' => $tenon_pre_publish, 'am_criteria' => $am_criteria, 'am_notify' => $am_notify, ) ); echo "

    " . __( 'Access Monitor Settings Updated', 'access-monitor' ) . '

    '; } } add_action( 'admin_head', 'am_setup_admin_notice' ); /** * Create Admin Notice about API keys. */ function am_setup_admin_notice() { if ( ! ( isset( $_POST['tenon_api_key'] ) && '' !== $_POST['tenon_api_key'] ) && ! ( isset( $_POST['tenon_multisite_key'] ) && '' !== $_POST['tenon_multisite_key'] ) ) { $settings = ( is_array( get_option( 'am_settings' ) ) ) ? get_option( 'am_settings' ) : array(); $tenon_api_key = ( isset( $settings['tenon_api_key'] ) ) ? $settings['tenon_api_key'] : false; $key = ( is_multisite() && false !== (bool) get_site_option( 'tenon_multisite_key' ) ) ? get_site_option( 'tenon_multisite_key' ) : $tenon_api_key; if ( ! $key ) { add_action( 'admin_notices', 'am_admin_notice' ); } } } /** * Display admin notice about API key. */ function am_admin_notice() { if ( isset( $_GET['page'] ) && ( 'am-support-page' === $_GET['page'] || 'am-report-page' === $_GET['page'] ) ) { $url = '#settings'; } else { $url = admin_url( 'edit.php?post_type=tenon-report&page=am-support-page#settings' ); } // Translators: Settings page URL. $message = sprintf( __( 'You must enter a Tenon API key to use Access Monitor.', 'access-monitor' ), $url ); if ( ! current_user_can( 'manage_options' ) ) { return; } else { echo "

    $message

    "; } } /** * Display Settings form. */ function am_settings() { $settings = ( is_array( get_option( 'am_settings' ) ) ) ? get_option( 'am_settings' ) : array(); $settings = array_merge( array( 'tenon_api_key' => '', 'tenon_pre_publish' => '', 'am_post_types' => array(), 'am_post_grade' => '', 'am_criteria' => array(), ), $settings ); $multisite = get_site_option( 'tenon_multisite_key' ); $post_types = get_post_types( array( 'public' => true, 'show_ui' => true, ), 'objects' ); $am_post_types = isset( $settings['am_post_types'] ) ? $settings['am_post_types'] : array(); $am_criteria = isset( $settings['am_criteria'] ) ? $settings['am_criteria'] : array(); $am_notify = isset( $settings['am_notify'] ) ? $settings['am_notify'] : get_option( 'admin_email' ); $am_post_type_options = ''; foreach ( $post_types as $type ) { if ( in_array( $type->name, $am_post_types, true ) ) { $selected = ' checked="checked"'; } else { $selected = ''; } if ( 'attachment' !== $type->name ) { $am_post_type_options .= " '; } } echo "

    "; if ( is_multisite() ) { echo "

    "; } $message = ( true === (bool) $settings['tenon_pre_publish'] ) ? '' . __( 'Pre-publication checks are not compatible with the Gutenberg editor.', 'access-monitor' ) . '' : ''; echo "

    '; if ( true === (bool) $settings['tenon_pre_publish'] ) { ?>

    array( 'label' => __( 'Required WCAG Level', 'access-monitor' ), 'default' => 'AA', ), 'certainty' => array( 'label' => __( 'Minimum certainty', 'access-monitor' ), 'default' => '60', ), 'priority' => array( 'label' => __( 'Minimum priority', 'access-monitor' ), 'default' => '20', ), 'grade' => array( 'label' => __( 'Minimum percentage grade to publish', 'access-monitor' ), 'default' => '90', ), 'store' => array( 'label' => __( 'Store data at Tenon.io?', 'access-monitor' ), 'default' => '0', ), 'container' => array( 'label' => __( 'Post content container', 'access-monitor' ), 'default' => '.access-monitor-content', ), ); echo '
    '; ?>

    '; echo "

    "; } /** * Form to set up a report. */ function am_report() { $settings = ( is_array( get_option( 'am_settings' ) ) ) ? get_option( 'am_settings' ) : array(); $settings = array_merge( array( 'tenon_api_key' => '' ), $settings ); $multisite = get_site_option( 'tenon_multisite_key' ); if ( false === (bool) $settings['tenon_api_key'] && false === (bool) $multisite ) { $disabled = " disabled='disabled'"; $message = "

    " . __( 'Sign up with Tenon to get an API key', 'access-monitor' ) . "" . __( 'Add your API key', 'access-monitor' ) . '

    '; } else { $disabled = ''; $message = ''; } echo am_setup_report(); $theme = wp_get_theme(); $theme_name = $theme->get( 'Name' ); $theme_version = $theme->get( 'Version' ); $name = $theme_name . ' ' . $theme_version; echo "$message
    "; echo "

    " . __( 'Set Accessibility Test Options', 'access-monitor' ) . "

    " . __( 'Higher values means Tenon.io has more confidence in the results.', 'access-monitor' ) . "

    " . __( 'Higher values means Tenon.io lists this as a high priority issue.', 'access-monitor' ) . "

    "; } /** * Generate a new accessibility report. */ function am_setup_report() { if ( isset( $_POST['am_generate'] ) ) { $name = ( isset( $_POST['am_report_name'] ) ) ? sanitize_text_field( $_POST['am_report_name'] ) : false; $pages = ( isset( $_POST['am_report_pages'] ) && ! empty( $_POST['am_report_pages'] ) ) ? $_POST['am_report_pages'] : false; $schedule = ( isset( $_POST['report_schedule'] ) ) ? $_POST['report_schedule'] : 'none'; $store = ( isset( $_POST['store'] ) ) ? 1 : 0; $project_id = ( isset( $_POST['projectID'] ) ) ? sanitize_text_field( $_POST['projectID'] ) : ''; $viewport = ( isset( $_POST['viewport'] ) ) ? explode( 'x', $_POST['viewport'] ) : array( '1024', '768' ); $viewportheight = $viewport[1]; $viewportwidth = $viewport[0]; $level = ( isset( $_POST['level'] ) ) ? $_POST['level'] : 'AA'; $priority = ( isset( $_POST['priority'] ) ) ? (int) $_POST['priority'] : 0; $certainty = ( isset( $_POST['certainty'] ) ) ? (int) $_POST['certainty'] : 0; $args = array( 'store' => $store, 'projectID' => $project_id, 'viewPortHeight' => $viewportheight, 'viewPortWidth' => $viewportwidth, 'level' => $level, 'priority' => $priority, 'certainty' => $certainty, ); am_generate_report( $name, $pages, $schedule, $args ); am_show_report(); } } /** * List accessibility reports. * * @param int $count Number of reports to list. */ function am_list_reports( $count = 10 ) { $count = (int) $count; $reports = wp_get_recent_posts( array( 'post_type' => 'tenon-report', 'numberposts' => $count, 'post_status' => 'publish', ), 'OBJECT' ); if ( is_array( $reports ) ) { echo ''; } else { echo '

    ' . __( 'No accessibility reports created yet.', 'access-monitor' ) . '

    '; } } /** * Show settings page. */ function am_support_page() { ?>

    consider a donation to support Access Monitor!', 'access-monitor' ), 'https://www.joedolson.com/donate/' ); ?>

    • Make a donation today! Every donation counts - donate $10, $20, or $100 and help me keep this plug-in running!', 'access-monitor' ); ?>

    support form. If your issue is with the API or on tenon.io, email Tenon support. Thanks!', 'access-monitor' ), '#support-form' ); ?>

    ' . __( 'What Tenon Tests', 'access-monitor' ) . ''; ?>

    Add New Report', 'access-monitor' ), __( 'Add New Report', 'access-monitor' ), $permissions, 'am-report-page', 'am_report_page', 'am-report-page' ); $settings_page = add_submenu_page( 'edit.php?post_type=tenon-report', __( 'Access Monitor > Access Monitor Settings', 'access-monitor' ), __( 'Access Monitor Settings', 'access-monitor' ), $permissions, 'am-support-page', 'am_support_page' ); add_action( 'load-' . $plugin_page, 'am_load_admin_styles' ); add_action( 'load-' . $settings_page, 'am_load_admin_styles' ); } } /** * Enqueue admin styles. */ function am_load_admin_styles() { add_action( 'admin_enqueue_scripts', 'am_admin_styles' ); } /** * Actually enqueue admin styles. */ function am_admin_styles() { wp_enqueue_style( 'am-admin-styles', plugins_url( 'css/am-admin-styles.css', __FILE__ ) ); } /** * Display Access Monitor get support form. */ function am_get_support_form() { global $current_user, $am_version; $current_user = wp_get_current_user(); // send fields for Access Monitor. $version = $am_version; // send fields for all plugins. $wp_version = get_bloginfo( 'version' ); $home_url = home_url(); $wp_url = site_url(); $language = get_bloginfo( 'language' ); $charset = get_bloginfo( 'charset' ); // server. $php_version = phpversion(); // theme data. $theme = wp_get_theme(); $theme_name = $theme->get( 'Name' ); $theme_uri = $theme->get( 'ThemeURI' ); $theme_parent = $theme->get( 'Template' ); $theme_version = $theme->get( 'Version' ); // plugin data. $plugins = get_plugins(); $plugins_string = ''; foreach ( array_keys( $plugins ) as $key ) { if ( is_plugin_active( $key ) ) { $plugin =& $plugins[ $key ]; $plugin_name = $plugin['Name']; $plugin_uri = $plugin['PluginURI']; $plugin_version = $plugin['Version']; $plugins_string .= "$plugin_name: $plugin_version; $plugin_uri\n"; } } $data = " ================ Installation Data ==================== Version: $version ==WordPress:== Version: $wp_version URL: $home_url Install: $wp_url Language: $language Charset: $charset ==Extra info:== PHP Version: $php_version Server Software: $_SERVER[SERVER_SOFTWARE] User Agent: $_SERVER[HTTP_USER_AGENT] ==Theme:== Name: $theme_name URI: $theme_uri Parent: $theme_parent Version: $theme_version ==Active Plugins:== $plugins_string "; if ( isset( $_POST['am_support'] ) ) { $nonce = $_REQUEST['_wpnonce']; if ( ! wp_verify_nonce( $nonce, 'access-monitor-nonce' ) ) { die( 'Security check failed' ); } $request = stripslashes( $_POST['support_request'] ); $has_donated = ( isset( $_POST['has_donated'] ) && 'on' === $_POST['has_donated'] ) ? 'Donor' : 'No donation'; $has_read_faq = ( isset( $_POST['has_read_faq'] ) && 'on' === $_POST['has_read_faq'] ) ? 'Read FAQ' : true; // has no faq, for now. $subject = "Access Monitor support request. $has_donated"; $message = $request . "\n\n" . $data; // Get the site domain and get rid of www. from pluggable.php. $sitename = strtolower( $_SERVER['SERVER_NAME'] ); if ( 'www.' === substr( $sitename, 0, 4 ) ) { $sitename = substr( $sitename, 4 ); } $from_email = 'wordpress@' . $sitename; $from = "From: \"$current_user->display_name\" <$from_email>\r\nReply-to: \"$current_user->display_name\" <$current_user->user_email>\r\n"; if ( ! $has_read_faq ) { echo "

    " . __( 'Please read the FAQ and other Help documents before making a support request.', 'access-monitor' ) . '

    '; } else { wp_mail( 'plugins@joedolson.com', $subject, $message, $from ); if ( 'Donor' === $has_donated ) { echo "

    " . __( 'Thank you for supporting the continuing development of this plug-in! I\'ll get back to you as soon as I can.', 'access-monitor' ) . '

    '; } else { echo "

    " . __( 'I\'ll get back to you as soon as I can, after dealing with any support requests from plug-in supporters.', 'access-monitor' ) . '

    '; } } } else { $request = ''; } echo "


    " . __( 'The following additional information will be sent with your support request:', 'access-monitor' ) . "

    " . wpautop( $data ) . '
    '; } add_filter( 'gettext', 'am_change_publish_button', 10, 2 ); /** * Changes the publish button from saying 'Update' to 'Re-run this test' * * @param string $translation Translated version of text. * @param string $text Original text. * * @return Custom text. */ function am_change_publish_button( $translation, $text ) { if ( is_admin() && isset( $_GET['action'] ) && 'edit' === $_GET['action'] ) { global $post; if ( is_object( $post ) ) { if ( 'Update' === $text && 'tenon-report' === $post->post_type ) { $translation = __( 'Re-run this test', 'access-monitor' ); } } } return $translation; } add_action( 'current_screen', 'am_redirect_new' ); /** * Prevents the default add new post screen from showing. */ function am_redirect_new() { $screen = get_current_screen(); if ( 'tenon-report' === $screen->id && ! isset( $_GET['action'] ) && ! isset( $_POST['save'] ) ) { wp_safe_redirect( admin_url( 'edit.php?post_type=tenon-report&page=am-report-page' ) ); exit; } } add_filter( 'plugin_action_links', 'am_plugin_action', 10, 2 ); /** * Add "Settings" link into Plug-in list * * @param array $links Existing plug-in links. * @param string $file Current plug-in file. * * @return array links. */ function am_plugin_action( $links, $file ) { if ( plugin_basename( dirname( __FILE__ ) . '/access-monitor.php' === $file ) ) { $links[] = "" . __( 'Access Monitor Settings', 'access-monitor' ) . ''; } return $links; }