class-admin.php
796 lines
| 1 | <?php |
| 2 | |
| 3 | // Exit if accessed directly |
| 4 | if ( ! defined( 'ABSPATH' ) ) exit; |
| 5 | |
| 6 | class ARIAAT_Admin { |
| 7 | |
| 8 | /** |
| 9 | * Constructor to hook the necessary actions. |
| 10 | */ |
| 11 | public function __construct() { |
| 12 | |
| 13 | add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts_styles' ] ); |
| 14 | |
| 15 | add_action('admin_menu', [$this, 'register_admin_menu']); |
| 16 | add_action('admin_init', [$this, 'register_settings']); |
| 17 | |
| 18 | } |
| 19 | |
| 20 | public function admin_scripts_styles( $hook ) { |
| 21 | $v = ARIAATVERSION; |
| 22 | //$v = time(); |
| 23 | |
| 24 | if ( strpos( $hook, 'ariaat' ) !== false ) { |
| 25 | wp_enqueue_script( |
| 26 | 'ariaat-admin', |
| 27 | ARIAATURL . 'assets/js/ariaat-admin.js', |
| 28 | ['jquery'], |
| 29 | $v, |
| 30 | true |
| 31 | ); |
| 32 | |
| 33 | wp_localize_script('ariaat-admin', 'ARIAAT_Data', [ |
| 34 | 'aria_options' => array_map(function($item) { |
| 35 | return [ |
| 36 | 'label' => $item['label'], |
| 37 | ]; |
| 38 | }, $this->get_aria_attributes()), |
| 39 | 'role_options' => array_map(function($item) { |
| 40 | return [ |
| 41 | 'label' => $item['label'], |
| 42 | ]; |
| 43 | }, $this->get_role_options()), |
| 44 | ]); |
| 45 | |
| 46 | wp_enqueue_style( 'ariaat-style', ARIAATURL .'assets/css/ariaat-admin.css', false, $v ); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | public function register_admin_menu() { |
| 51 | add_options_page('ARIA Toolkit', 'ARIA Toolkit', 'manage_options', 'ariaat', [$this, 'render_settings_page']); |
| 52 | } |
| 53 | |
| 54 | public function register_settings() { |
| 55 | |
| 56 | register_setting('ariaat_general_group', 'ariaat_general_settings', [$this, 'sanitize_general_settings']); |
| 57 | |
| 58 | register_setting('ariaat_aria_group', 'ariaat_aria_mappings', [$this, 'sanitize_array']); |
| 59 | register_setting('ariaat_aria_group', 'ariaat_aria_menus', [$this, 'sanitize_menu_selection']); |
| 60 | |
| 61 | register_setting('ariaat_role_group', 'ariaat_role_mappings', [$this, 'sanitize_array']); |
| 62 | register_setting('ariaat_role_group', 'ariaat_role_menus', [$this, 'sanitize_menu_selection']); |
| 63 | |
| 64 | register_setting('ariaat_contrast_group', 'ariaat_contrast_mappings', [$this, 'sanitize_array']); |
| 65 | |
| 66 | register_setting('ariaat_images_group', 'ariaat_image_settings', [$this, 'sanitize_array']); |
| 67 | |
| 68 | } |
| 69 | |
| 70 | |
| 71 | public function get_all_menus() { |
| 72 | $menus = wp_get_nav_menus(); |
| 73 | $output = []; |
| 74 | foreach ($menus as $menu) { |
| 75 | $output[$menu->term_id] = $menu->name; |
| 76 | } |
| 77 | return $output; |
| 78 | } |
| 79 | |
| 80 | |
| 81 | public function render_settings_page() { |
| 82 | |
| 83 | $active_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'general'; |
| 84 | ?> |
| 85 | <div class="wrap ariaat"> |
| 86 | <h1>ARIA & Accessibility Toolkit <span><?php echo esc_attr( ARIAATVERSION ); ?></span></h1> |
| 87 | |
| 88 | <div class="main_content"> |
| 89 | <h2 class="nav-tab-wrapper"> |
| 90 | <a href="?page=ariaat&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : '' ?>">General</a> |
| 91 | <a href="?page=ariaat&tab=aria" class="nav-tab <?php echo $active_tab == 'aria' ? 'nav-tab-active' : '' ?>">ARIA</a> |
| 92 | <a href="?page=ariaat&tab=roles" class="nav-tab <?php echo $active_tab == 'roles' ? 'nav-tab-active' : '' ?>">Roles</a> |
| 93 | <a href="?page=ariaat&tab=contrast" class="nav-tab <?php echo $active_tab == 'contrast' ? 'nav-tab-active' : '' ?>">Contrast</a> |
| 94 | <a href="?page=ariaat&tab=images" class="nav-tab <?php echo $active_tab == 'images' ? 'nav-tab-active' : '' ?>">Images</a> |
| 95 | <?php do_action( 'ariaat_after_nav_tab_wrapper', $active_tab ); ?> |
| 96 | </h2> |
| 97 | |
| 98 | |
| 99 | <form method="post" action="options.php"> |
| 100 | <?php |
| 101 | if ($active_tab === 'general') { |
| 102 | settings_fields('ariaat_general_group'); |
| 103 | $settings = get_option('ariaat_general_settings', []); |
| 104 | |
| 105 | ?> |
| 106 | <h3>Frontend Accessibility Checker</h3> |
| 107 | <table class="form-table general-table"> |
| 108 | <tr> |
| 109 | <th scope="row">Frontend Checker</th> |
| 110 | <td> |
| 111 | <label> |
| 112 | <input type="checkbox" name="ariaat_general_settings[enable_frontend_checker]" value="1" <?php checked($settings['enable_frontend_checker'] ?? '', '1'); ?> /> |
| 113 | Display the frontend accessibility checker. |
| 114 | </label> |
| 115 | <p class="description">Toggle visibility of the accessibility checker overlay on the frontend for testing purposes. Enabling this will make it visible to logged-in admins only.</p> |
| 116 | </td> |
| 117 | </tr> |
| 118 | </table> |
| 119 | |
| 120 | <h3>General Accessibility Enhancements</h3> |
| 121 | |
| 122 | <table class="form-table general-table"> |
| 123 | |
| 124 | <tr> |
| 125 | <th scope="row">Language</th> |
| 126 | <td> |
| 127 | <input type="text" name="ariaat_general_settings[language]" value="<?php echo esc_attr($settings['language'] ?? ''); ?>" placeholder="e.g. en, en-AU" class="regular-text" /> |
| 128 | <p class="description">If your site is already setting the Language, this will override it.</p> |
| 129 | </td> |
| 130 | </tr> |
| 131 | |
| 132 | <tr> |
| 133 | <th scope="row">Skip Link Target</th> |
| 134 | <td> |
| 135 | <input type="text" name="ariaat_general_settings[skip_link]" value="<?php echo esc_attr($settings['skip_link'] ?? ''); ?>" class="regular-text" placeholder=".main-content" /> |
| 136 | <p class="description">Enter a CSS selector for the main content area (e.g. <code>.main-content</code>). If provided, a “Skip to content” link will be added at the top of the page but will only be visible to keyboard and screen reader users.</p> |
| 137 | </td> |
| 138 | </tr> |
| 139 | |
| 140 | <tr> |
| 141 | <th scope="row">Show Focus Outline</th> |
| 142 | <td> |
| 143 | <label> |
| 144 | <input type="checkbox" name="ariaat_general_settings[focus_outline]" value="1" <?php checked($settings['focus_outline'] ?? '', '1'); ?> /> |
| 145 | Ensures keyboard focus outlines are visible for better navigation. |
| 146 | </label> |
| 147 | </td> |
| 148 | </tr> |
| 149 | |
| 150 | <tr> |
| 151 | <th scope="row">Fix Tab Order</th> |
| 152 | <td> |
| 153 | <label> |
| 154 | <input type="checkbox" name="ariaat_general_settings[fix_tabindex]" value="1" <?php checked($settings['fix_tabindex'] ?? '', '1'); ?> /> |
| 155 | Remove tabindex values greater than 0 to maintain a logical focus order. |
| 156 | </label> |
| 157 | </td> |
| 158 | </tr> |
| 159 | |
| 160 | <tr> |
| 161 | <th scope="row">Make Viewport Scalable</th> |
| 162 | <td> |
| 163 | <label> |
| 164 | <input type="checkbox" name="ariaat_general_settings[make_viewport_scalable]" value="1" <?php checked($settings['make_viewport_scalable'] ?? '', '1'); ?> /> |
| 165 | Remove <code>user-scalable=no</code> from the viewport meta tag to allow pinch-zooming. |
| 166 | </label> |
| 167 | </td> |
| 168 | </tr> |
| 169 | |
| 170 | |
| 171 | </table> |
| 172 | |
| 173 | <?php |
| 174 | } else if ($active_tab === 'aria') { |
| 175 | settings_fields('ariaat_aria_group'); |
| 176 | $items = get_option('ariaat_aria_mappings', []); |
| 177 | ?> |
| 178 | |
| 179 | <h3>Menu ARIA Labels</h3> |
| 180 | <p class="desc"> |
| 181 | Select your Nav menus to automatically apply <code>aria-label</code> attributes to the links within that menu. |
| 182 | </p> |
| 183 | |
| 184 | <?php |
| 185 | $all_menus = wp_get_nav_menus(); |
| 186 | $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus); |
| 187 | $selected = get_option('ariaat_aria_menus', []); |
| 188 | |
| 189 | foreach ($menus as $menu) { |
| 190 | $checked = in_array($menu->term_id, $selected); |
| 191 | ?> |
| 192 | <label style="display:block; margin-bottom:4px;"> |
| 193 | <input type="checkbox" name="ariaat_aria_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> /> |
| 194 | <?php echo esc_html($menu->name); ?> |
| 195 | </label> |
| 196 | <?php |
| 197 | } |
| 198 | ?> |
| 199 | <hr> |
| 200 | <h3>Custom ARIA Attributes</h3> |
| 201 | <p class="desc"> |
| 202 | Add ARIA attributes to specific elements on your site using CSS selectors. |
| 203 | </p> |
| 204 | <table class="form-table" id="aria-table"> |
| 205 | <thead> |
| 206 | <tr> |
| 207 | <th>HTML Selector</th> |
| 208 | <th>ARIA Attribute</th> |
| 209 | <th>Value</th> |
| 210 | <th></th> |
| 211 | </tr> |
| 212 | </thead> |
| 213 | <tbody> |
| 214 | <?php foreach ($items as $index => $item) : ?> |
| 215 | <tr> |
| 216 | <td><input type="text" name="ariaat_aria_mappings[<?php echo esc_attr($index); ?>][selector]" value="<?php echo esc_attr($item['selector']); ?>" class="regular-text" /></td> |
| 217 | <td> |
| 218 | <select name="ariaat_aria_mappings[<?php echo esc_attr($index); ?>][attribute]"> |
| 219 | <?php foreach ($this->get_aria_attributes() as $attr => $attr_item): ?> |
| 220 | <option value="<?php echo esc_attr($attr); ?>" <?php selected($item['attribute'], $attr); ?>> |
| 221 | <?php echo esc_html($attr_item['label']); ?> |
| 222 | </option> |
| 223 | <?php endforeach; ?> |
| 224 | </select> |
| 225 | </td> |
| 226 | <td><input type="text" name="ariaat_aria_mappings[<?php echo esc_attr($index); ?>][value]" value="<?php echo esc_attr($item['value']); ?>" class="regular-text" /></td> |
| 227 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 228 | </tr> |
| 229 | <?php endforeach; ?> |
| 230 | </tbody> |
| 231 | </table> |
| 232 | |
| 233 | <p><button type="button" class="button" id="add-aria-row">Add ARIA Attribute</button></p> |
| 234 | <?php |
| 235 | |
| 236 | |
| 237 | } elseif ($active_tab == 'roles') { |
| 238 | settings_fields('ariaat_role_group'); |
| 239 | $items = get_option('ariaat_role_mappings', []); |
| 240 | ?> |
| 241 | |
| 242 | <h3>Menu Roles</h3> |
| 243 | <p class="desc"> |
| 244 | Add a <code>role="navigation"</code> to selected menus for better screen reader support. |
| 245 | </p> |
| 246 | |
| 247 | <?php |
| 248 | $all_menus = wp_get_nav_menus(); |
| 249 | $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus); |
| 250 | $selected_roles = get_option('ariaat_role_menus', []); |
| 251 | |
| 252 | foreach ($menus as $menu) { |
| 253 | $checked = in_array($menu->term_id, $selected_roles); |
| 254 | ?> |
| 255 | <label style="display:block; margin-bottom:4px;"> |
| 256 | <input type="checkbox" name="ariaat_role_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> /> |
| 257 | <?php echo esc_html($menu->name); ?> |
| 258 | </label> |
| 259 | <?php |
| 260 | } |
| 261 | ?> |
| 262 | <hr> |
| 263 | <h3>Custom Roles</h3> |
| 264 | <p class="desc"> |
| 265 | Assign ARIA roles to specific elements on your site using CSS selectors. |
| 266 | </p> |
| 267 | |
| 268 | <table class="form-table" id="role-table"> |
| 269 | <thead> |
| 270 | <tr> |
| 271 | <th>HTML Selector</th> |
| 272 | <th>Role</th> |
| 273 | <th></th> |
| 274 | </tr> |
| 275 | </thead> |
| 276 | <tbody> |
| 277 | <?php foreach ($items as $index => $item) : ?> |
| 278 | <tr> |
| 279 | <td><input type="text" name="ariaat_role_mappings[<?php echo esc_attr($index); ?>][selector]" value="<?php echo esc_attr($item['selector']); ?>" class="regular-text" /></td> |
| 280 | <td> |
| 281 | <select name="ariaat_role_mappings[<?php echo esc_attr($index); ?>][role]"> |
| 282 | <?php foreach ($this->get_role_options() as $role => $role_item): ?> |
| 283 | <option value="<?php echo esc_attr($role); ?>" <?php selected($item['role'], $role); ?>> |
| 284 | <?php echo esc_html($role_item['label']); ?> |
| 285 | </option> |
| 286 | <?php endforeach; ?> |
| 287 | </select> |
| 288 | </td> |
| 289 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 290 | </tr> |
| 291 | <?php endforeach; ?> |
| 292 | </tbody> |
| 293 | </table> |
| 294 | |
| 295 | <p><button type="button" class="button" id="add-role-row">Add Role</button></p> |
| 296 | <?php |
| 297 | |
| 298 | |
| 299 | } else if( $active_tab == 'contrast' ) { |
| 300 | settings_fields('ariaat_contrast_group'); |
| 301 | $items = get_option('ariaat_contrast_mappings', []); |
| 302 | ?> |
| 303 | |
| 304 | <h3>High Contrast Adjustments</h3> |
| 305 | <p class="desc"> |
| 306 | Apply custom foreground and background colors to specific elements to improve visual contrast and accessibility. This is especially useful for meeting WCAG contrast ratio requirements and making content easier to read for users with visual impairments. |
| 307 | </p> |
| 308 | |
| 309 | <table class="form-table" id="contrast-table"> |
| 310 | <thead> |
| 311 | <tr> |
| 312 | <th>HTML Selector</th> |
| 313 | <th>Text</th> |
| 314 | <th>Background</th> |
| 315 | <th>Contrast</th> |
| 316 | <th></th> |
| 317 | </tr> |
| 318 | </thead> |
| 319 | <tbody> |
| 320 | <?php foreach ($items as $index => $item) : ?> |
| 321 | <tr> |
| 322 | <td><input type="text" name="ariaat_contrast_mappings[<?php echo esc_attr( $index ) ?>][selector]" value="<?php echo esc_attr($item['selector']) ?>" class="regular-text" /></td> |
| 323 | <td> |
| 324 | <input type="color" |
| 325 | id="text-color-picker-<?php echo esc_attr($index); ?>" |
| 326 | class="text-color" |
| 327 | data-index="<?php echo esc_attr($index); ?>" |
| 328 | value="<?php echo esc_attr($item['color']); ?>" |
| 329 | > |
| 330 | <input type="hidden" |
| 331 | name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][color]" |
| 332 | id="real-text-color-<?php echo esc_attr($index); ?>" |
| 333 | value="<?php echo esc_attr($item['color']); ?>" |
| 334 | > |
| 335 | <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="text">Clear</button> |
| 336 | </td> |
| 337 | <td> |
| 338 | <input type="color" |
| 339 | id="bg-color-picker-<?php echo esc_attr($index); ?>" |
| 340 | class="bg-color" |
| 341 | data-index="<?php echo esc_attr($index); ?>" |
| 342 | value="<?php echo esc_attr($item['background']); ?>" |
| 343 | > |
| 344 | <input type="hidden" |
| 345 | name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][background]" |
| 346 | id="real-bg-color-<?php echo esc_attr($index); ?>" |
| 347 | value="<?php echo esc_attr($item['background']); ?>" |
| 348 | > |
| 349 | <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="bg">Clear</button> |
| 350 | </td> |
| 351 | <td> |
| 352 | <div class="contrast-result" id="contrast-result-<?php echo esc_attr( $index ); ?>"></div> |
| 353 | </td> |
| 354 | |
| 355 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 356 | </tr> |
| 357 | <?php endforeach; ?> |
| 358 | </tbody> |
| 359 | </table> |
| 360 | <p><button type="button" class="button" id="add-contrast-row">Add Contrast Item</button></p> |
| 361 | <?php |
| 362 | |
| 363 | } else if ($active_tab === 'images') { |
| 364 | settings_fields('ariaat_images_group'); |
| 365 | $settings = get_option('ariaat_image_settings', []); |
| 366 | |
| 367 | $show_all = isset($_POST['ariaat_image_settings']['show_all']) |
| 368 | ? boolval($_POST['ariaat_image_settings']['show_all']) |
| 369 | : (!empty($settings['show_all'])); |
| 370 | |
| 371 | $paged = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; |
| 372 | $per_page = 5; |
| 373 | $offset = ($paged - 1) * $per_page; |
| 374 | |
| 375 | $meta_query = $show_all ? [] : [ |
| 376 | 'relation' => 'OR', |
| 377 | [ |
| 378 | 'key' => '_wp_attachment_image_alt', |
| 379 | 'value' => '', |
| 380 | 'compare' => '=' |
| 381 | ], |
| 382 | [ |
| 383 | 'key' => '_wp_attachment_image_alt', |
| 384 | 'value' => ' ', |
| 385 | 'compare' => '=' |
| 386 | ], |
| 387 | [ |
| 388 | 'key' => '_wp_attachment_image_alt', |
| 389 | 'compare' => 'NOT EXISTS' |
| 390 | ] |
| 391 | ]; |
| 392 | |
| 393 | $query = new WP_Query([ |
| 394 | 'post_type' => 'attachment', |
| 395 | 'post_mime_type' => 'image', |
| 396 | 'post_status' => 'inherit', |
| 397 | 'posts_per_page' => $per_page, |
| 398 | 'offset' => $offset, |
| 399 | 'meta_query' => $meta_query |
| 400 | ]); |
| 401 | |
| 402 | $attachments = $query->posts; |
| 403 | $total = $query->found_posts; |
| 404 | $total_pages = ceil($total / $per_page); |
| 405 | ?> |
| 406 | |
| 407 | <h3>Images Missing Alt Text</h3> |
| 408 | <p class="desc"> |
| 409 | Lists images on your site that are missing alt text. Add the alt text below and click "Save" to update each image. Does not scan images that are within themes or plugins, only images from your WordPress Media Library. |
| 410 | </p> |
| 411 | <p> |
| 412 | <label> |
| 413 | <input type="checkbox" name="ariaat_image_settings[show_all]" value="1" |
| 414 | <?php checked($settings['show_all'] ?? '', '1'); ?> /> |
| 415 | Show all images (not just those missing alt text) |
| 416 | </label> |
| 417 | </p> |
| 418 | |
| 419 | <?php if (empty($attachments)) : ?> |
| 420 | <p>No images without alt text were found.</p> |
| 421 | <?php else : ?> |
| 422 | <table class="widefat fixed striped"> |
| 423 | <thead> |
| 424 | <tr> |
| 425 | <th>ID</th> |
| 426 | <th>Thumbnail</th> |
| 427 | <th>Alt Tag</th> |
| 428 | <th>Save</th> |
| 429 | </tr> |
| 430 | </thead> |
| 431 | <tbody> |
| 432 | <?php foreach ($attachments as $attachment) : |
| 433 | $id = $attachment->ID; |
| 434 | $alt = get_post_meta($id, '_wp_attachment_image_alt', true); |
| 435 | $thumb = wp_get_attachment_image_url($id, [48, 48]); |
| 436 | $nonce = wp_create_nonce("ariaat_update_alt_{$id}"); |
| 437 | ?> |
| 438 | <tr data-id="<?php echo esc_attr($id); ?>"> |
| 439 | <td><?php echo esc_html($id); ?></td> |
| 440 | <td><img src="<?php echo esc_url($thumb); ?>" width="48" height="48" /></td> |
| 441 | <td><input type="text" class="ariaat-alt" value="<?php echo esc_attr($alt); ?>" placeholder="no alt tag" /></td> |
| 442 | <td> |
| 443 | <button class="button ariaat-save-alt" |
| 444 | data-id="<?php echo esc_attr($id); ?>" |
| 445 | data-nonce="<?php echo esc_attr($nonce); ?>"> |
| 446 | Save |
| 447 | </button> |
| 448 | </td> |
| 449 | </tr> |
| 450 | <?php endforeach; ?> |
| 451 | </tbody> |
| 452 | </table> |
| 453 | |
| 454 | <?php if ($total_pages > 1) : ?> |
| 455 | <div class="ariaat-pagination"> |
| 456 | <?php |
| 457 | $base_url = remove_query_arg(['paged']); |
| 458 | for ($i = 1; $i <= $total_pages; $i++) : |
| 459 | $url = add_query_arg('paged', $i, $base_url); |
| 460 | $active_class = ($i == $paged) ? 'active' : ''; |
| 461 | echo "<a class='{$active_class}' href='" . esc_url($url) . "'>$i</a> "; |
| 462 | endfor; |
| 463 | ?> |
| 464 | </div> |
| 465 | <?php endif; ?> |
| 466 | |
| 467 | <?php endif; |
| 468 | } |
| 469 | |
| 470 | |
| 471 | |
| 472 | |
| 473 | do_action( 'ariaat_before_admin_submit_button', $active_tab ); |
| 474 | |
| 475 | submit_button(); |
| 476 | ?> |
| 477 | |
| 478 | </form> |
| 479 | |
| 480 | </div> |
| 481 | |
| 482 | <div class="sidebar_cell"> |
| 483 | <?php $this->sidebar_help( $active_tab ); ?> |
| 484 | </div> |
| 485 | |
| 486 | </div> |
| 487 | |
| 488 | <?php |
| 489 | } |
| 490 | |
| 491 | |
| 492 | // sidebar |
| 493 | public function sidebar_help( $active_tab ) { |
| 494 | ?> |
| 495 | <div class="box"> |
| 496 | <h3>PRO Plugin</h3> |
| 497 | |
| 498 | <p><strong>More Features</strong></p> |
| 499 | <p> |
| 500 | The PRO plugin provides extra features: |
| 501 | </p> |
| 502 | <ul> |
| 503 | <li><span class="dashicons dashicons-saved"></span> Unlimited Menus in ARIA labels</li> |
| 504 | <li><span class="dashicons dashicons-saved"></span> Unlimited Menus in Roles</li> |
| 505 | <li><span class="dashicons dashicons-saved"></span> All ARIA attributes in dropdown</li> |
| 506 | <li><span class="dashicons dashicons-saved"></span> All Roles in dropdown</li> |
| 507 | </ul> |
| 508 | <a class="button" href="https://wcagforwp.com/downloads/pro">View PRO Plugin</a> |
| 509 | </div> |
| 510 | <?php |
| 511 | $method = 'sidebar_' . str_replace( '-', '_', $active_tab ); |
| 512 | if ( method_exists( $this, $method ) ) { |
| 513 | $this->$method( $active_tab ); |
| 514 | } else { |
| 515 | $this->sidebar_general( $active_tab ); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | |
| 520 | // sidebar |
| 521 | public function sidebar_general() { |
| 522 | |
| 523 | ?> |
| 524 | <div class="box"> |
| 525 | <h3>Help</h3> |
| 526 | |
| 527 | <p><strong>Language</strong></p> |
| 528 | <p> |
| 529 | This sets the default <code>lang</code> attribute in your site’s HTML. It helps screen readers and search engines understand what language your site is written in. |
| 530 | <br>For example, use <code>en</code> for English, <code>en-AU</code> for Australian English, <code>fr</code> for French, etc. |
| 531 | <br><strong>Tip:</strong> This should match the main language used across your website. |
| 532 | </p> |
| 533 | |
| 534 | <p><strong>Skip Link Target</strong></p> |
| 535 | <p> |
| 536 | Adds a “Skip to content” link at the very top of your site. This improves accessibility for keyboard and screen reader users by letting them jump straight to the main content. |
| 537 | <br>Enter a CSS selector that matches your main content area, like <code>#primary</code> or <code>.site-main</code>. This should point to the container that wraps your main article or page content. |
| 538 | <br><strong>Tip:</strong> You can inspect your site using right-click → “Inspect” to find the correct ID or class. |
| 539 | </p> |
| 540 | |
| 541 | <p><strong>Focus Outline</strong></p> |
| 542 | <p> |
| 543 | Ensures that keyboard users can clearly see which element is currently focused - like buttons, links, or form fields. Some themes hide this outline by default, which can make navigation difficult. |
| 544 | <br>This setting forces the outline to remain visible for better usability and compliance with WCAG. |
| 545 | </p> |
| 546 | </div> |
| 547 | |
| 548 | |
| 549 | |
| 550 | <?php |
| 551 | } |
| 552 | |
| 553 | |
| 554 | // sidebar |
| 555 | public function sidebar_aria() { |
| 556 | |
| 557 | ?> |
| 558 | <div class="box"> |
| 559 | <h3>Menu ARIA Labels</h3> |
| 560 | |
| 561 | <p><strong>What it does</strong><br> |
| 562 | Adds <code>aria-label</code> attributes to links in the menu when the <code>title</code> is different from the visible text.</p> |
| 563 | |
| 564 | <p><strong>Why it matters</strong><br> |
| 565 | This helps screen readers provide more context. <code>title</code> attributes are not reliably announced by screen readers. Using <code>aria-label</code> improves accessibility and clarity.</p> |
| 566 | |
| 567 | <p><strong>When to use</strong><br> |
| 568 | Ideal when your menu link text is short (e.g. “Home”) but you’ve added a longer title like “Go to homepage” in the Menu editor.</p> |
| 569 | |
| 570 | <p><strong>Example</strong><br> |
| 571 | If a link shows “About” and the title is “Learn more about our company”, this becomes:<br> |
| 572 | <code><a href="/about" aria-label="Learn more about our company">About</a></code></p> |
| 573 | |
| 574 | <p><strong>Where to set titles</strong><br> |
| 575 | Add title attributes <a target="_blank" href="<?php echo esc_url( admin_url('/nav-menus.php') );?>">here</a> or go to <strong>Appearance → Menus</strong>. Open any menu item and use the “Title Attribute” field. Enable it via “Screen Options” if it’s hidden. </p> |
| 576 | |
| 577 | <p><strong>Limitations</strong><br> |
| 578 | Only applies when the <code>title</code> and link text differ. If no <code>title</code> exists, nothing will be added.</p> |
| 579 | </div> |
| 580 | |
| 581 | <div class="box"> |
| 582 | <h3>Custom ARIA Attributes</h3> |
| 583 | <p><strong>HTML Selector</strong></p> |
| 584 | <p> |
| 585 | The HTML selector is the item that you wish to target. For example, to add an aria-label attribute to the |
| 586 | <code><?php echo esc_attr( htmlentities('<div class="header"></div>') ); ?></code> on your site, you would set the HTML selector to |
| 587 | <code>.header</code>. |
| 588 | </p> |
| 589 | |
| 590 | <p><strong>ARIA Attribute</strong></p> |
| 591 | <p> |
| 592 | Choose which ARIA attribute you want to apply, such as <code>aria-label</code> or <code>aria-hidden</code>. |
| 593 | These attributes help assistive technologies better understand the purpose of elements on your site. |
| 594 | <br><a href="https://www.w3.org/WAI/ARIA/apg/practices/" target="_blank">View ARIA Authoring Practices</a> |
| 595 | </p> |
| 596 | |
| 597 | <p><strong>Value</strong></p> |
| 598 | <p> |
| 599 | The value is what will be inserted into the ARIA attribute. For example, if you’re using <code>aria-label</code>, |
| 600 | you might enter <code>Main Navigation</code> to describe the element’s purpose. |
| 601 | </p> |
| 602 | |
| 603 | |
| 604 | </div> |
| 605 | |
| 606 | |
| 607 | <?php |
| 608 | } |
| 609 | |
| 610 | // sidebar |
| 611 | public function sidebar_roles() { |
| 612 | ?> |
| 613 | <div class="box"> |
| 614 | <h3>Menu Roles</h3> |
| 615 | |
| 616 | <p><strong>What it does</strong><br> |
| 617 | Adds a <code>role="navigation"</code> to selected menus to help screen readers identify them as navigation areas.</p> |
| 618 | |
| 619 | <p><strong>Why it matters</strong><br> |
| 620 | Not all themes include ARIA roles by default. This role improves accessibility by signaling the purpose of the menu structure.</p> |
| 621 | |
| 622 | <p><strong>When to use</strong><br> |
| 623 | Use this when your theme’s menu is missing a semantic container (like a <code><nav></code> tag) or lacks a <code>role</code> attribute.</p> |
| 624 | |
| 625 | <p><strong>Best practice</strong><br> |
| 626 | ARIA roles should only be added when the HTML is not already semantic. For example, use <code>role="navigation"</code> on a <code><div></code> - but not on a <code><nav></code> unless it’s missing the role.</p> |
| 627 | |
| 628 | <p><strong>Limitations</strong><br> |
| 629 | This only applies to menus created using WordPress’s <code>wp_nav_menu()</code>. It cannot modify HTML added manually by your theme.</p> |
| 630 | </div> |
| 631 | |
| 632 | <div class="box"> |
| 633 | <h3>Custom Roles</h3> |
| 634 | |
| 635 | <p><strong>What it does</strong><br> |
| 636 | Lets you assign ARIA roles (like <code>main</code>, <code>banner</code>, or <code>search</code>) to specific elements using CSS selectors.</p> |
| 637 | |
| 638 | <p><strong>When to use</strong><br> |
| 639 | Useful for marking important sections of your site when semantic elements like <code><main></code>, <code><header></code>, or <code><aside></code> are not being used. Avoid using roles redundantly on elements that are already semantic.</p> |
| 640 | |
| 641 | <p><strong>HTML Selector</strong><br> |
| 642 | Target the element you want to apply a role to - for example: <code>.sidebar</code> or <code>#main-content</code>.</p> |
| 643 | |
| 644 | <p><strong>Role</strong><br> |
| 645 | Choose the appropriate role based on the element’s purpose. For example, use <code>main</code> for primary content, or <code>complementary</code> for sidebars. |
| 646 | <br><a href="https://www.w3.org/WAI/ARIA/apg/roles/" target="_blank">View all landmark roles</a></p> |
| 647 | </div> |
| 648 | <?php |
| 649 | } |
| 650 | |
| 651 | public function sidebar_contrast() { |
| 652 | ?> |
| 653 | <div class="box"> |
| 654 | <h3>High Contrast Adjustments</h3> |
| 655 | |
| 656 | <p><strong>What it does</strong><br> |
| 657 | Allows you to manually set text and background colors on specific elements to improve readability and meet WCAG contrast requirements.</p> |
| 658 | |
| 659 | <p><strong>Why it matters</strong><br> |
| 660 | Insufficient contrast is one of the most common accessibility issues. Low contrast can make text unreadable for users with vision impairments, especially in bright or dim environments.</p> |
| 661 | |
| 662 | <p><strong>When to use</strong><br> |
| 663 | Use this when you have areas on your site with poor contrast (e.g. light gray text on a white background). This feature is ideal for fixing contrast issues without editing your theme's CSS directly.</p> |
| 664 | |
| 665 | <p><strong>HTML Selector</strong><br> |
| 666 | The selector is the element you want to target. For example, to apply contrast to:<br> |
| 667 | <code><?php echo esc_html('<div class="content-box">...</div>'); ?></code><br> |
| 668 | use the selector <code>.content-box</code>.</p> |
| 669 | |
| 670 | <p><strong>Text & Background Colors</strong><br> |
| 671 | Choose a readable foreground (text) and background color. The tool will calculate the contrast ratio and tell you if it passes WCAG AA or AAA standards.</p> |
| 672 | |
| 673 | <p><strong>Limitations</strong><br> |
| 674 | This is best used for specific tweaks - not full theme styling.</p> |
| 675 | |
| 676 | <p><a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html" target="_blank">Learn more about color contrast requirements</a></p> |
| 677 | </div> |
| 678 | <?php |
| 679 | } |
| 680 | |
| 681 | |
| 682 | private function get_aria_attributes() { |
| 683 | $attributes = [ |
| 684 | 'aria-describedby' => ['label' => 'aria-describedby'], |
| 685 | 'aria-disabled' => ['label' => 'aria-disabled'], |
| 686 | 'aria-expanded' => ['label' => 'aria-expanded'], |
| 687 | 'aria-hidden' => ['label' => 'aria-hidden'], |
| 688 | 'aria-label' => ['label' => 'aria-label'], |
| 689 | 'aria-labelledby' => ['label' => 'aria-labelledby'], |
| 690 | ]; |
| 691 | |
| 692 | /** |
| 693 | * Filter the list of ARIA attributes available in the plugin. |
| 694 | * |
| 695 | * @param array $attributes The default set of ARIA attributes. |
| 696 | */ |
| 697 | return apply_filters('ariaat_aria_attributes_options', $attributes); |
| 698 | } |
| 699 | |
| 700 | |
| 701 | private function get_role_options() { |
| 702 | $roles = [ |
| 703 | 'banner' => ['label' => 'banner'], |
| 704 | 'button' => ['label' => 'button'], |
| 705 | 'checkbox' => ['label' => 'checkbox'], |
| 706 | 'complementary' => ['label' => 'complementary'], |
| 707 | 'contentinfo' => ['label' => 'contentinfo'], |
| 708 | 'form' => ['label' => 'form'], |
| 709 | 'grid' => ['label' => 'grid'], |
| 710 | 'heading' => ['label' => 'heading'], |
| 711 | 'img' => ['label' => 'img'], |
| 712 | 'link' => ['label' => 'link'], |
| 713 | 'list' => ['label' => 'list'], |
| 714 | 'listitem' => ['label' => 'listitem'], |
| 715 | 'main' => ['label' => 'main'], |
| 716 | 'navigation' => ['label' => 'navigation'], |
| 717 | 'region' => ['label' => 'region'], |
| 718 | 'row' => ['label' => 'row'], |
| 719 | 'rowheader' => ['label' => 'rowheader'], |
| 720 | 'search' => ['label' => 'search'], |
| 721 | 'table' => ['label' => 'table'], |
| 722 | 'textbox' => ['label' => 'textbox'], |
| 723 | ]; |
| 724 | |
| 725 | /** |
| 726 | * Filter the list of available roles. |
| 727 | * |
| 728 | * @param array $roles Array of role definitions. |
| 729 | */ |
| 730 | return apply_filters('ariaat_roles_options', $roles); |
| 731 | } |
| 732 | |
| 733 | |
| 734 | public function sanitize_array($input) { |
| 735 | if (!is_array($input)) { |
| 736 | return sanitize_text_field($input); |
| 737 | } |
| 738 | |
| 739 | foreach ($input as $key => $item) { |
| 740 | if (is_array($item)) { |
| 741 | foreach ($item as $subkey => $value) { |
| 742 | // Sanitize based on known keys |
| 743 | if (in_array($subkey, ['selector'], true)) { |
| 744 | // Allow basic CSS selectors: letters, numbers, dashes, underscores, dots, #, >, space, : |
| 745 | $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\s\.\#\>\:\[\]\=\"~\+\-\*(),]/', '', $value); |
| 746 | } elseif (in_array($subkey, ['attribute', 'role'], true)) { |
| 747 | // Whitelist attribute/role names to be alphanumeric with optional dashes |
| 748 | $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\-_]/', '', $value); |
| 749 | } else { |
| 750 | $input[$key][$subkey] = sanitize_text_field($value); |
| 751 | } |
| 752 | } |
| 753 | } else { |
| 754 | $input[$key] = sanitize_text_field($item); |
| 755 | } |
| 756 | } |
| 757 | |
| 758 | return $input; |
| 759 | } |
| 760 | |
| 761 | |
| 762 | |
| 763 | public function sanitize_menu_selection($input) { |
| 764 | $allowed_menus = array_keys($this->get_all_menus()); |
| 765 | |
| 766 | if( ! $input || ! array( $allowed_menus ) || ! array( $input ) ) |
| 767 | return; |
| 768 | $selected = array_intersect($input, $allowed_menus); |
| 769 | |
| 770 | return $selected; |
| 771 | } |
| 772 | |
| 773 | public function sanitize_general_settings($input) { |
| 774 | |
| 775 | $output = []; |
| 776 | |
| 777 | // Language setting |
| 778 | $output['language'] = isset($input['language']) ? sanitize_text_field($input['language']) : ''; |
| 779 | |
| 780 | $output['skip_link'] = isset($input['skip_link']) ? sanitize_text_field($input['skip_link']) : ''; |
| 781 | |
| 782 | $output['enable_frontend_checker'] = isset($input['enable_frontend_checker']) ? '1' : ''; |
| 783 | $output['focus_outline'] = isset($input['focus_outline']) ? '1' : ''; |
| 784 | $output['fix_tabindex'] = isset($input['fix_tabindex']) ? '1' : ''; |
| 785 | $output['make_viewport_scalable'] = isset($input['make_viewport_scalable']) ? '1' : ''; |
| 786 | |
| 787 | return $output; |
| 788 | |
| 789 | } |
| 790 | |
| 791 | |
| 792 | } |
| 793 | |
| 794 | // Initialize the class |
| 795 | new ARIAAT_Admin(); |
| 796 |