class-admin.php
788 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 | |
| 21 | public function admin_scripts_styles( $hook ) { |
| 22 | $v = ARIAATVERSION; |
| 23 | //$v = time(); |
| 24 | |
| 25 | if ( strpos( $hook, 'ariaat' ) !== false ) { |
| 26 | wp_enqueue_script( |
| 27 | 'ariaat-admin', |
| 28 | ARIAATURL . 'assets/js/ariaat-admin.js', |
| 29 | ['jquery'], |
| 30 | $v, |
| 31 | true |
| 32 | ); |
| 33 | |
| 34 | wp_localize_script('ariaat-admin', 'ARIAAT_Data', [ |
| 35 | 'aria_options' => array_map(function($item) { |
| 36 | return [ |
| 37 | 'label' => $item['label'], |
| 38 | ]; |
| 39 | }, $this->get_aria_attributes()), |
| 40 | 'role_options' => array_map(function($item) { |
| 41 | return [ |
| 42 | 'label' => $item['label'], |
| 43 | ]; |
| 44 | }, $this->get_role_options()), |
| 45 | ]); |
| 46 | |
| 47 | wp_enqueue_style( 'ariaat-style', ARIAATURL .'assets/css/ariaat-admin.css', false, $v ); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | public function register_admin_menu() { |
| 52 | add_options_page('Web Accessibility', 'Web Accessibility', 'manage_options', 'ariaat', [$this, 'render_settings_page']); |
| 53 | } |
| 54 | |
| 55 | public function register_settings() { |
| 56 | |
| 57 | register_setting('ariaat_general_group', 'ariaat_general_settings', [$this, 'sanitize_general_settings']); |
| 58 | |
| 59 | register_setting('ariaat_aria_group', 'ariaat_aria_mappings', [$this, 'sanitize_array']); |
| 60 | register_setting('ariaat_aria_group', 'ariaat_aria_menus', [$this, 'sanitize_menu_selection']); |
| 61 | |
| 62 | register_setting('ariaat_role_group', 'ariaat_role_mappings', [$this, 'sanitize_array']); |
| 63 | register_setting('ariaat_role_group', 'ariaat_role_menus', [$this, 'sanitize_menu_selection']); |
| 64 | |
| 65 | register_setting('ariaat_contrast_group', 'ariaat_contrast_mappings', [$this, 'sanitize_array']); |
| 66 | |
| 67 | register_setting('ariaat_images_group', 'ariaat_image_settings', [$this, 'sanitize_array']); |
| 68 | |
| 69 | } |
| 70 | |
| 71 | |
| 72 | public function get_all_menus() { |
| 73 | $menus = wp_get_nav_menus(); |
| 74 | $output = []; |
| 75 | foreach ($menus as $menu) { |
| 76 | $output[$menu->term_id] = $menu->name; |
| 77 | } |
| 78 | return $output; |
| 79 | } |
| 80 | |
| 81 | |
| 82 | public function render_settings_page() { |
| 83 | |
| 84 | $active_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'general'; |
| 85 | ?> |
| 86 | <div class="wrap ariaat"> |
| 87 | |
| 88 | <div class="ariaat-header"> |
| 89 | <h1>Web Accessibility Toolkit <span><?php echo esc_attr(ARIAATVERSION); ?></span></h1> |
| 90 | |
| 91 | <div class="ariaat-header-buttons"> |
| 92 | <a href="https://wordpress.org/support/plugin/aria-accessibility-toolkit/reviews/#new-post" target="_blank" class="small">Leave a review</a> |
| 93 | <a href="https://wcagforwp.com/docs/" target="_blank" class="button-primary small">Docs</a> |
| 94 | <a href="https://wcagforwp.com/downloads/pro-plugin" target="_blank" class="button-primary small">PRO Plugin</a> |
| 95 | </div> |
| 96 | </div> |
| 97 | |
| 98 | <div class="main_content"> |
| 99 | <h2 class="nav-tab-wrapper"> |
| 100 | <a href="?page=ariaat&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : '' ?>">General</a> |
| 101 | <a href="?page=ariaat&tab=aria" class="nav-tab <?php echo $active_tab == 'aria' ? 'nav-tab-active' : '' ?>">ARIA</a> |
| 102 | <a href="?page=ariaat&tab=roles" class="nav-tab <?php echo $active_tab == 'roles' ? 'nav-tab-active' : '' ?>">Roles</a> |
| 103 | <a href="?page=ariaat&tab=contrast" class="nav-tab <?php echo $active_tab == 'contrast' ? 'nav-tab-active' : '' ?>">Contrast</a> |
| 104 | <a href="?page=ariaat&tab=images" class="nav-tab <?php echo $active_tab == 'images' ? 'nav-tab-active' : '' ?>">Images</a> |
| 105 | <a href="?page=ariaat&tab=forms" class="nav-tab <?php echo $active_tab == 'forms' ? 'nav-tab-active' : '' ?>">Forms</a> |
| 106 | <?php do_action( 'ariaat_after_nav_tab_wrapper', $active_tab ); ?> |
| 107 | </h2> |
| 108 | |
| 109 | |
| 110 | <form method="post" action="options.php"> |
| 111 | <?php |
| 112 | if ($active_tab === 'general') { |
| 113 | settings_fields('ariaat_general_group'); |
| 114 | $settings = get_option('ariaat_general_settings', []); |
| 115 | |
| 116 | ?> |
| 117 | <div class="section"> |
| 118 | <h3><span class="dashicons dashicons-laptop"></span> Frontend Accessibility Checker</h3> |
| 119 | <div class="setting-row no-border"> |
| 120 | <div class="setting-label"> |
| 121 | <p class="description"> |
| 122 | Toggle visibility of the accessibility checker on the frontend. The frontend accessibility checker is only visible to logged-in admins - it will never be visible to your users. |
| 123 | <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?> |
| 124 | <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> also includes deep scanning of forms & works with all form plugins. |
| 125 | <?php } ?> |
| 126 | </p> |
| 127 | </div> |
| 128 | <div class="setting-control"> |
| 129 | <label class="toggle-switch"> |
| 130 | <input type="checkbox" name="ariaat_general_settings[enable_frontend_checker]" value="1" <?php checked($settings['enable_frontend_checker'] ?? '', '1'); ?> /> |
| 131 | <span class="toggle-slider"></span> |
| 132 | </label> |
| 133 | </div> |
| 134 | </div> |
| 135 | |
| 136 | </div> |
| 137 | <div class="section"> |
| 138 | <h3><span class="dashicons dashicons-universal-access-alt"></span> General Accessibility Enhancements</h3> |
| 139 | |
| 140 | <div class="setting-row"> |
| 141 | <div class="setting-label"> |
| 142 | <strong>Language</strong> |
| 143 | <p class="description">This sets the default lang attribute in your site’s HTML. It helps screen readers and search engines understand what language your site is written in. For example, use <code>en</code> for English, <code>en-AU</code> for Australian English, <code>fr</code> for French, etc.</p> |
| 144 | </div> |
| 145 | <div class="setting-control"> |
| 146 | <input type="text" name="ariaat_general_settings[language]" value="<?php echo esc_attr($settings['language'] ?? ''); ?>" class="regular-text" placeholder="e.g. en, en-AU" /> |
| 147 | </div> |
| 148 | </div> |
| 149 | |
| 150 | <div class="setting-row"> |
| 151 | <div class="setting-label"> |
| 152 | <strong>Skip Link Target</strong> |
| 153 | <p class="description"> |
| 154 | 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. This improves accessibility for keyboard and screen reader users by letting them jump straight to the main content. Enter a CSS selector that matches your main content area, like <code>#primary</code> or <code>.main-content</code>. This should point to the container that wraps your main article or page content. |
| 155 | </p> |
| 156 | </div> |
| 157 | <div class="setting-control"> |
| 158 | <input type="text" name="ariaat_general_settings[skip_link]" value="<?php echo esc_attr($settings['skip_link'] ?? ''); ?>" class="regular-text" placeholder=".main-content" /> |
| 159 | </div> |
| 160 | </div> |
| 161 | |
| 162 | <div class="setting-row"> |
| 163 | <div class="setting-label"> |
| 164 | <strong>Show Focus Outline</strong> |
| 165 | <p class="description"> |
| 166 | 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. This setting forces the outline to remain visible for better usability and compliance with WCAG. |
| 167 | </p> |
| 168 | </div> |
| 169 | <div class="setting-control"> |
| 170 | <label class="toggle-switch"> |
| 171 | <input type="checkbox" name="ariaat_general_settings[focus_outline]" value="1" <?php checked($settings['focus_outline'] ?? '', '1'); ?> /> |
| 172 | <span class="toggle-slider"></span> |
| 173 | </label> |
| 174 | </div> |
| 175 | </div> |
| 176 | |
| 177 | <div class="setting-row"> |
| 178 | <div class="setting-label"> |
| 179 | <strong>Fix Tab Order</strong> |
| 180 | <p class="description"> |
| 181 | Remove <code>tabindex</code> values greater than 0 to maintain a logical keyboard focus order. |
| 182 | </p> |
| 183 | </div> |
| 184 | <div class="setting-control"> |
| 185 | <label class="toggle-switch"> |
| 186 | <input type="checkbox" name="ariaat_general_settings[fix_tabindex]" value="1" <?php checked($settings['fix_tabindex'] ?? '', '1'); ?> /> |
| 187 | <span class="toggle-slider"></span> |
| 188 | </label> |
| 189 | </div> |
| 190 | </div> |
| 191 | |
| 192 | <div class="setting-row no-border"> |
| 193 | <div class="setting-label"> |
| 194 | <strong>Make Viewport Scalable</strong> |
| 195 | <p class="description">Remove <code>user-scalable=no</code> from the viewport meta tag to allow pinch-zooming.</p> |
| 196 | </div> |
| 197 | <div class="setting-control"> |
| 198 | <label class="toggle-switch"> |
| 199 | <input type="checkbox" name="ariaat_general_settings[make_viewport_scalable]" value="1" <?php checked($settings['make_viewport_scalable'] ?? '', '1'); ?> /> |
| 200 | <span class="toggle-slider"></span> |
| 201 | </label> |
| 202 | </div> |
| 203 | </div> |
| 204 | |
| 205 | </div> |
| 206 | |
| 207 | <div class="section"> |
| 208 | <h3><span class="dashicons dashicons-star-filled"></span> Like this plugin?</h3> |
| 209 | <div class="setting-row no-border"> |
| 210 | <div class="setting-label"> |
| 211 | <p class="description">The Web Accessibility Toolkit is a new plugin and we'd love it if you could help out and provide a kind review if you've found this plugin useful. You can <a href="https://wordpress.org/support/plugin/aria-accessibility-toolkit/reviews/#new-post" target="_blank">leave a review here</a>.</p> |
| 212 | </div> |
| 213 | </div> |
| 214 | </div> |
| 215 | |
| 216 | <?php |
| 217 | } else if ($active_tab === 'aria') { |
| 218 | settings_fields('ariaat_aria_group'); |
| 219 | $items = get_option('ariaat_aria_mappings', []); |
| 220 | ?> |
| 221 | |
| 222 | <div class="section"> |
| 223 | <h3><span class="dashicons dashicons-menu"></span> Menu ARIA Labels</h3> |
| 224 | <p class="desc"> |
| 225 | Automatically converts <code>title</code> attributes on menu links to <code>aria-label</code> attributes, which is more reliable for screen readers. <br>This is ideal when the link text might be "About" but has a longer title like "Learn more about our company" which is much more descriptive.<br>To set the titles, visit the <a href="<?php echo esc_url( admin_url('/nav-menus.php') ); ?>" target="_blank">Menu editor</a> or go to <strong>Appearance → Menus</strong> and use the “Title Attribute” field (enable it via “Screen Options” if hidden). |
| 226 | |
| 227 | </p> |
| 228 | |
| 229 | <?php |
| 230 | $all_menus = wp_get_nav_menus(); |
| 231 | $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus); |
| 232 | $selected = get_option('ariaat_aria_menus', []); |
| 233 | |
| 234 | foreach ($menus as $menu) : |
| 235 | $checked = in_array($menu->term_id, $selected); |
| 236 | ?> |
| 237 | <div class="setting-row no-border"> |
| 238 | <div class="setting-label"> |
| 239 | <strong><?php echo esc_html($menu->name); ?></strong> |
| 240 | </div> |
| 241 | <div class="setting-control"> |
| 242 | <label class="toggle-switch"> |
| 243 | <input type="checkbox" name="ariaat_aria_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> /> |
| 244 | <span class="toggle-slider"></span> |
| 245 | </label> |
| 246 | </div> |
| 247 | </div> |
| 248 | <?php endforeach; ?> |
| 249 | <p class="desc"> |
| 250 | <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?> |
| 251 | You can select up to 2 menus in the free version. |
| 252 | Upgrade to the <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> to add unlimited menus. |
| 253 | <?php } ?> |
| 254 | </p> |
| 255 | </div> |
| 256 | |
| 257 | <div class="section"> |
| 258 | <h3><span class="dashicons dashicons-editor-code"></span> Custom ARIA Attributes</h3> |
| 259 | <p class="desc"> |
| 260 | Add custom <code>aria-*</code> attributes to elements on your site by targeting them with CSS selectors. |
| 261 | This is useful when your theme or plugins don’t include the necessary ARIA markup. |
| 262 | For example, you might target a navigation item with a selector like <code>.nav-primary .current</code>. To find the correct selectors, right click on your page and click "Inspect" - some knowledge of CSS and HTML is required. |
| 263 | <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?> |
| 264 | <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> includes a more comprehensive list of ARIA attributes. |
| 265 | <?php } ?> |
| 266 | </p> |
| 267 | |
| 268 | <table class="form-table" id="aria-table"> |
| 269 | <thead> |
| 270 | <tr> |
| 271 | <th>CSS Selector</th> |
| 272 | <th>ARIA Attribute</th> |
| 273 | <th>Value</th> |
| 274 | <th></th> |
| 275 | </tr> |
| 276 | </thead> |
| 277 | <tbody> |
| 278 | <?php foreach ($items as $index => $item) : ?> |
| 279 | <tr> |
| 280 | <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> |
| 281 | <td> |
| 282 | <select name="ariaat_aria_mappings[<?php echo esc_attr($index); ?>][attribute]"> |
| 283 | <?php foreach ($this->get_aria_attributes() as $attr => $attr_item): ?> |
| 284 | <option value="<?php echo esc_attr($attr); ?>" <?php selected($item['attribute'], $attr); ?>> |
| 285 | <?php echo esc_html($attr_item['label']); ?> |
| 286 | </option> |
| 287 | <?php endforeach; ?> |
| 288 | </select> |
| 289 | </td> |
| 290 | <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> |
| 291 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 292 | </tr> |
| 293 | <?php endforeach; ?> |
| 294 | </tbody> |
| 295 | </table> |
| 296 | |
| 297 | <p><button type="button" class="button" id="add-aria-row">Add ARIA Attribute</button></p> |
| 298 | </div> |
| 299 | |
| 300 | <?php |
| 301 | |
| 302 | |
| 303 | } elseif ($active_tab == 'roles') { |
| 304 | settings_fields('ariaat_role_group'); |
| 305 | $items = get_option('ariaat_role_mappings', []); |
| 306 | ?> |
| 307 | |
| 308 | <div class="section"> |
| 309 | <h3><span class="dashicons dashicons-menu"></span> Menu Roles</h3> |
| 310 | <p class="desc"> |
| 311 | Automatically adds <code>role="navigation"</code> to selected menus for improved screen reader support. Use this only if the menu's container is <code><div></code> or another non-semantic element. Do <strong>not</strong> use it on <code><nav></code> elements, which are already semantic. Applies only to menus output by WordPress’s <code>wp_nav_menu()</code> function. It won't modify custom HTML added manually by your theme. |
| 312 | </p> |
| 313 | |
| 314 | <?php |
| 315 | $all_menus = wp_get_nav_menus(); |
| 316 | $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus); |
| 317 | $selected_roles = get_option('ariaat_role_menus', []); |
| 318 | |
| 319 | foreach ($menus as $menu) : |
| 320 | $checked = in_array($menu->term_id, $selected_roles); |
| 321 | ?> |
| 322 | <div class="setting-row no-border"> |
| 323 | <div class="setting-label"> |
| 324 | <strong><?php echo esc_html($menu->name); ?></strong> |
| 325 | </div> |
| 326 | <div class="setting-control"> |
| 327 | <label class="toggle-switch"> |
| 328 | <input type="checkbox" name="ariaat_role_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> /> |
| 329 | <span class="toggle-slider"></span> |
| 330 | </label> |
| 331 | </div> |
| 332 | </div> |
| 333 | <?php endforeach; ?> |
| 334 | |
| 335 | <p class="desc"> |
| 336 | <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?> |
| 337 | You can select up to 2 menus in the free version. |
| 338 | Upgrade to the <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> to add unlimited menus. |
| 339 | <?php } ?> |
| 340 | </p> |
| 341 | |
| 342 | </div> |
| 343 | |
| 344 | <div class="section"> |
| 345 | <h3><span class="dashicons dashicons-editor-code"></span> Custom Roles</h3> |
| 346 | |
| 347 | <p class="desc"> |
| 348 | Assign ARIA landmark <code>role</code> attributes to elements on your site using CSS selectors. |
| 349 | This is helpful when your theme doesn’t use semantic HTML or lacks proper roles for key regions like navigation, main content, or banners. |
| 350 | To find the right selectors, right-click any element on your site and choose "Inspect." Some familiarity with CSS and HTML is recommended. |
| 351 | <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?> |
| 352 | <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> includes a more comprehensive list of ARIA landmark roles. |
| 353 | <?php } ?> |
| 354 | </p> |
| 355 | |
| 356 | |
| 357 | <table class="form-table" id="role-table"> |
| 358 | <thead> |
| 359 | <tr> |
| 360 | <th>CSS Selector</th> |
| 361 | <th>Role</th> |
| 362 | <th></th> |
| 363 | </tr> |
| 364 | </thead> |
| 365 | <tbody> |
| 366 | <?php foreach ($items as $index => $item) : ?> |
| 367 | <tr> |
| 368 | <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> |
| 369 | <td> |
| 370 | <select name="ariaat_role_mappings[<?php echo esc_attr($index); ?>][role]"> |
| 371 | <?php foreach ($this->get_role_options() as $role => $role_item): ?> |
| 372 | <option value="<?php echo esc_attr($role); ?>" <?php selected($item['role'], $role); ?>> |
| 373 | <?php echo esc_html($role_item['label']); ?> |
| 374 | </option> |
| 375 | <?php endforeach; ?> |
| 376 | </select> |
| 377 | </td> |
| 378 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 379 | </tr> |
| 380 | <?php endforeach; ?> |
| 381 | </tbody> |
| 382 | </table> |
| 383 | |
| 384 | <p><button type="button" class="button" id="add-role-row">Add Role</button></p> |
| 385 | </div> |
| 386 | |
| 387 | <?php |
| 388 | |
| 389 | |
| 390 | } else if( $active_tab == 'contrast' ) { |
| 391 | settings_fields('ariaat_contrast_group'); |
| 392 | $items = get_option('ariaat_contrast_mappings', []); |
| 393 | ?> |
| 394 | |
| 395 | <div class="section"> |
| 396 | <h3><span class="dashicons dashicons-star-half"></span> High Contrast Adjustments</h3> |
| 397 | <p class="desc"> |
| 398 | 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. |
| 399 | </p> |
| 400 | |
| 401 | <table class="form-table" id="contrast-table"> |
| 402 | <thead> |
| 403 | <tr> |
| 404 | <th class="selector">HTML Selector</th> |
| 405 | <th class="color">Text</th> |
| 406 | <th class="color">Background</th> |
| 407 | <th>Contrast</th> |
| 408 | <th class="remove"></th> |
| 409 | </tr> |
| 410 | </thead> |
| 411 | <tbody> |
| 412 | <?php foreach ($items as $index => $item) : ?> |
| 413 | <tr> |
| 414 | <td class="selector"><input type="text" name="ariaat_contrast_mappings[<?php echo esc_attr( $index ) ?>][selector]" value="<?php echo esc_attr($item['selector']) ?>" class="regular-text" /></td> |
| 415 | <td> |
| 416 | <input type="color" |
| 417 | id="text-color-picker-<?php echo esc_attr($index); ?>" |
| 418 | class="text-color" |
| 419 | data-index="<?php echo esc_attr($index); ?>" |
| 420 | value="<?php echo esc_attr($item['color']); ?>" |
| 421 | > |
| 422 | <input type="hidden" |
| 423 | name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][color]" |
| 424 | id="real-text-color-<?php echo esc_attr($index); ?>" |
| 425 | value="<?php echo esc_attr($item['color']); ?>" |
| 426 | > |
| 427 | <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="text">Clear</button> |
| 428 | </td> |
| 429 | <td> |
| 430 | <input type="color" |
| 431 | id="bg-color-picker-<?php echo esc_attr($index); ?>" |
| 432 | class="bg-color" |
| 433 | data-index="<?php echo esc_attr($index); ?>" |
| 434 | value="<?php echo esc_attr($item['background']); ?>" |
| 435 | > |
| 436 | <input type="hidden" |
| 437 | name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][background]" |
| 438 | id="real-bg-color-<?php echo esc_attr($index); ?>" |
| 439 | value="<?php echo esc_attr($item['background']); ?>" |
| 440 | > |
| 441 | <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="bg">Clear</button> |
| 442 | </td> |
| 443 | <td> |
| 444 | <div class="contrast-result" id="contrast-result-<?php echo esc_attr( $index ); ?>"></div> |
| 445 | </td> |
| 446 | |
| 447 | <td><button type="button" class="button remove-row">Remove</button></td> |
| 448 | </tr> |
| 449 | <?php endforeach; ?> |
| 450 | </tbody> |
| 451 | </table> |
| 452 | <p><button type="button" class="button" id="add-contrast-row">Add Contrast Item</button></p> |
| 453 | |
| 454 | </div> |
| 455 | |
| 456 | <?php |
| 457 | |
| 458 | } else if ($active_tab === 'images') { |
| 459 | |
| 460 | settings_fields('ariaat_images_group'); |
| 461 | $settings = get_option('ariaat_image_settings', []); |
| 462 | |
| 463 | $show_all = isset($_POST['ariaat_image_settings']['show_all']) |
| 464 | ? boolval($_POST['ariaat_image_settings']['show_all']) |
| 465 | : (!empty($settings['show_all'])); |
| 466 | |
| 467 | $paged = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; |
| 468 | $per_page = 50; |
| 469 | $offset = ($paged - 1) * $per_page; |
| 470 | |
| 471 | $meta_query = $show_all ? [] : [ |
| 472 | 'relation' => 'OR', |
| 473 | [ |
| 474 | 'key' => '_wp_attachment_image_alt', |
| 475 | 'value' => '', |
| 476 | 'compare' => '=' |
| 477 | ], |
| 478 | [ |
| 479 | 'key' => '_wp_attachment_image_alt', |
| 480 | 'value' => ' ', |
| 481 | 'compare' => '=' |
| 482 | ], |
| 483 | [ |
| 484 | 'key' => '_wp_attachment_image_alt', |
| 485 | 'compare' => 'NOT EXISTS' |
| 486 | ] |
| 487 | ]; |
| 488 | |
| 489 | $query = new WP_Query([ |
| 490 | 'post_type' => 'attachment', |
| 491 | 'post_mime_type' => 'image', |
| 492 | 'post_status' => 'inherit', |
| 493 | 'posts_per_page' => $per_page, |
| 494 | 'offset' => $offset, |
| 495 | 'meta_query' => $meta_query |
| 496 | ]); |
| 497 | |
| 498 | $attachments = $query->posts; |
| 499 | $total = $query->found_posts; |
| 500 | $total_pages = ceil($total / $per_page); |
| 501 | ?> |
| 502 | <div class="section"> |
| 503 | <h3><span class="dashicons dashicons-format-gallery"></span> Images Missing Alt Text</h3> |
| 504 | <p class="desc"> |
| 505 | 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. |
| 506 | </p> |
| 507 | <p> |
| 508 | <label> |
| 509 | <input type="checkbox" name="ariaat_image_settings[show_all]" value="1" |
| 510 | <?php checked($settings['show_all'] ?? '', '1'); ?> /> |
| 511 | Show all images (not just those missing alt text) |
| 512 | </label> |
| 513 | </p> |
| 514 | |
| 515 | <?php if (empty($attachments)) : ?> |
| 516 | <p>No images without alt text were found.</p> |
| 517 | <?php else : ?> |
| 518 | |
| 519 | <p style="margin-bottom: 1em;"> |
| 520 | <button type="button" class="button copy-all-filenames">Copy all Filenames to Alt</button> |
| 521 | <button type="button" class="button copy-all-titles">Copy all Titles to Alt Tags</button> |
| 522 | </p> |
| 523 | |
| 524 | <table class="form-table widefat fixed striped" id="alt-images" style="margin-bottom: 2em;"> |
| 525 | <thead> |
| 526 | <tr> |
| 527 | <th class="id">ID</th> |
| 528 | <th class="thumb">Thumb</th> |
| 529 | <th>Alt Tag</th> |
| 530 | <th>Filename</th> |
| 531 | <th>Attached To</th> |
| 532 | <th class="save">Save</th> |
| 533 | </tr> |
| 534 | </thead> |
| 535 | <tbody> |
| 536 | <?php foreach ($attachments as $attachment) : |
| 537 | |
| 538 | $parent_id = $attachment->post_parent; |
| 539 | |
| 540 | if ($parent_id) { |
| 541 | $parent = get_post($parent_id); |
| 542 | if (!$parent || $parent->post_status !== 'publish') { |
| 543 | continue; // skip this image |
| 544 | } |
| 545 | } |
| 546 | $id = $attachment->ID; |
| 547 | $alt = get_post_meta($id, '_wp_attachment_image_alt', true); |
| 548 | $thumb = wp_get_attachment_image_url($id, [48, 48]); |
| 549 | $nonce = wp_create_nonce("ariaat_update_alt_{$id}"); |
| 550 | $filename = basename(get_attached_file($id)); |
| 551 | $parent_id = $attachment->post_parent; |
| 552 | $attached_to = $parent_id ? get_post($parent_id) : null; |
| 553 | ?> |
| 554 | |
| 555 | <tr data-id="<?php echo esc_attr($id); ?>"> |
| 556 | <td><?php echo esc_html($id); ?></td> |
| 557 | <td><img src="<?php echo esc_url($thumb); ?>" width="48" height="48" /></td> |
| 558 | <td><input type="text" class="ariaat-alt" value="<?php echo esc_attr($alt); ?>" placeholder="no alt tag" /></td> |
| 559 | <td> |
| 560 | <button type="button" class="button button-small copy-filename" data-filename="<?php echo esc_attr($filename); ?>"> |
| 561 | Copy to Alt |
| 562 | </button> |
| 563 | <br> |
| 564 | <?php echo esc_html($filename); ?> |
| 565 | |
| 566 | </td> |
| 567 | <td> |
| 568 | <?php if ($attached_to) : ?> |
| 569 | <button type="button" |
| 570 | class="button button-small copy-title" |
| 571 | data-title="<?php echo esc_attr(get_the_title($attached_to->ID)); ?>"> |
| 572 | Copy to Alt |
| 573 | </button> |
| 574 | <br> |
| 575 | <a href="<?php echo esc_url(get_edit_post_link($attached_to->ID)); ?>"> |
| 576 | <?php echo esc_html(get_the_title($attached_to->ID)); ?> |
| 577 | </a> <small>(<?php echo esc_html( ucfirst(get_post_type($attached_to)) ); ?>)</small> |
| 578 | |
| 579 | <?php else : ?> |
| 580 | <em>Not attached</em> |
| 581 | <?php endif; ?> |
| 582 | </td> |
| 583 | <td> |
| 584 | <button class="button ariaat-save-alt" |
| 585 | data-id="<?php echo esc_attr($id); ?>" |
| 586 | data-nonce="<?php echo esc_attr($nonce); ?>"> |
| 587 | Save |
| 588 | </button> |
| 589 | </td> |
| 590 | </tr> |
| 591 | <?php endforeach; ?> |
| 592 | </tbody> |
| 593 | </table> |
| 594 | |
| 595 | <?php if ($total_pages > 1) : ?> |
| 596 | <div class="ariaat-pagination"> |
| 597 | <?php |
| 598 | $base_url = remove_query_arg(['paged']); |
| 599 | for ($i = 1; $i <= $total_pages; $i++) : |
| 600 | $url = add_query_arg('paged', $i, $base_url); |
| 601 | $active_class = ($i == $paged) ? 'active' : ''; |
| 602 | printf( |
| 603 | '<a class="%s" href="%s">%d</a> ', |
| 604 | esc_attr($active_class), |
| 605 | esc_url($url), |
| 606 | $i |
| 607 | ); |
| 608 | endfor; |
| 609 | ?> |
| 610 | </div> |
| 611 | <?php endif; ?> |
| 612 | |
| 613 | <?php endif; ?> |
| 614 | |
| 615 | </div> |
| 616 | |
| 617 | |
| 618 | <?php } else if ($active_tab === 'forms') { |
| 619 | |
| 620 | $forms_output = ' |
| 621 | <div class="section"> |
| 622 | <h3><span class="dashicons dashicons-info"></span> Upgrade to PRO</h3> |
| 623 | <p>Get deep form scanning and automatic form fixes when you upgrade to the PRO plugin.<br>Works with all form plugins.</p> |
| 624 | <a style="margin-bottom:30px" href="https://wcagforwp.com/downloads/pro-plugin/" class="button button-primary" target="_blank">View PRO Plugin</a> |
| 625 | </div> |
| 626 | '; |
| 627 | |
| 628 | echo apply_filters( 'ariaat_forms_page_output', $forms_output, $active_tab ); |
| 629 | |
| 630 | } |
| 631 | |
| 632 | do_action( 'ariaat_before_admin_submit_button', $active_tab ); |
| 633 | |
| 634 | if ($active_tab !== 'forms' || ($active_tab === 'forms' && defined('ARIAAT_PRO_ACTIVE') ) ) |
| 635 | submit_button(); |
| 636 | |
| 637 | ?> |
| 638 | |
| 639 | </form> |
| 640 | |
| 641 | </div> |
| 642 | |
| 643 | </div> |
| 644 | |
| 645 | <?php |
| 646 | } |
| 647 | |
| 648 | |
| 649 | private function get_aria_attributes() { |
| 650 | $attributes = [ |
| 651 | 'aria-describedby' => ['label' => 'aria-describedby'], |
| 652 | 'aria-disabled' => ['label' => 'aria-disabled'], |
| 653 | 'aria-expanded' => ['label' => 'aria-expanded'], |
| 654 | 'aria-hidden' => ['label' => 'aria-hidden'], |
| 655 | 'aria-label' => ['label' => 'aria-label'], |
| 656 | 'aria-labelledby' => ['label' => 'aria-labelledby'], |
| 657 | ]; |
| 658 | |
| 659 | /** |
| 660 | * Filter the list of ARIA attributes available in the plugin. |
| 661 | * |
| 662 | * @param array $attributes The default set of ARIA attributes. |
| 663 | */ |
| 664 | return apply_filters('ariaat_aria_attributes_options', $attributes); |
| 665 | } |
| 666 | |
| 667 | |
| 668 | private function get_role_options() { |
| 669 | $roles = [ |
| 670 | 'banner' => ['label' => 'banner'], |
| 671 | 'button' => ['label' => 'button'], |
| 672 | 'checkbox' => ['label' => 'checkbox'], |
| 673 | 'complementary' => ['label' => 'complementary'], |
| 674 | 'contentinfo' => ['label' => 'contentinfo'], |
| 675 | 'form' => ['label' => 'form'], |
| 676 | 'grid' => ['label' => 'grid'], |
| 677 | 'heading' => ['label' => 'heading'], |
| 678 | 'img' => ['label' => 'img'], |
| 679 | 'link' => ['label' => 'link'], |
| 680 | 'list' => ['label' => 'list'], |
| 681 | 'listitem' => ['label' => 'listitem'], |
| 682 | 'main' => ['label' => 'main'], |
| 683 | 'navigation' => ['label' => 'navigation'], |
| 684 | 'region' => ['label' => 'region'], |
| 685 | 'row' => ['label' => 'row'], |
| 686 | 'rowheader' => ['label' => 'rowheader'], |
| 687 | 'search' => ['label' => 'search'], |
| 688 | 'table' => ['label' => 'table'], |
| 689 | 'textbox' => ['label' => 'textbox'], |
| 690 | ]; |
| 691 | |
| 692 | /** |
| 693 | * Filter the list of available roles. |
| 694 | * |
| 695 | * @param array $roles Array of role definitions. |
| 696 | */ |
| 697 | return apply_filters('ariaat_roles_options', $roles); |
| 698 | } |
| 699 | |
| 700 | |
| 701 | public function sanitize_array($input) { |
| 702 | if (!is_array($input)) { |
| 703 | return sanitize_text_field($input); |
| 704 | } |
| 705 | |
| 706 | foreach ($input as $key => $item) { |
| 707 | if (is_array($item)) { |
| 708 | foreach ($item as $subkey => $value) { |
| 709 | // Sanitize based on known keys |
| 710 | if (in_array($subkey, ['selector'], true)) { |
| 711 | // Allow basic CSS selectors: letters, numbers, dashes, underscores, dots, #, >, space, : |
| 712 | $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\s\.\#\>\:\[\]\=\"~\+\-\*(),]/', '', $value); |
| 713 | } elseif (in_array($subkey, ['attribute', 'role'], true)) { |
| 714 | // Whitelist attribute/role names to be alphanumeric with optional dashes |
| 715 | $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\-_]/', '', $value); |
| 716 | } else { |
| 717 | $input[$key][$subkey] = sanitize_text_field($value); |
| 718 | } |
| 719 | } |
| 720 | } else { |
| 721 | $input[$key] = sanitize_text_field($item); |
| 722 | } |
| 723 | } |
| 724 | |
| 725 | return $input; |
| 726 | } |
| 727 | |
| 728 | |
| 729 | |
| 730 | public function sanitize_menu_selection($input) { |
| 731 | $allowed_menus = array_keys($this->get_all_menus()); |
| 732 | |
| 733 | if( ! $input || ! array( $allowed_menus ) || ! array( $input ) ) |
| 734 | return; |
| 735 | $selected = array_intersect($input, $allowed_menus); |
| 736 | |
| 737 | return $selected; |
| 738 | } |
| 739 | |
| 740 | public function sanitize_general_settings($input) { |
| 741 | |
| 742 | $output = []; |
| 743 | |
| 744 | // Language setting |
| 745 | $output['language'] = isset($input['language']) ? sanitize_text_field($input['language']) : ''; |
| 746 | |
| 747 | $output['skip_link'] = isset($input['skip_link']) ? sanitize_text_field($input['skip_link']) : ''; |
| 748 | |
| 749 | $output['enable_frontend_checker'] = isset($input['enable_frontend_checker']) ? '1' : ''; |
| 750 | $output['focus_outline'] = isset($input['focus_outline']) ? '1' : ''; |
| 751 | $output['fix_tabindex'] = isset($input['fix_tabindex']) ? '1' : ''; |
| 752 | $output['make_viewport_scalable'] = isset($input['make_viewport_scalable']) ? '1' : ''; |
| 753 | |
| 754 | return $output; |
| 755 | |
| 756 | } |
| 757 | |
| 758 | public function sanitize_form_settings($input) { |
| 759 | |
| 760 | $sanitized = []; |
| 761 | |
| 762 | $checkboxes = [ |
| 763 | 'auto_generate_labels', |
| 764 | 'fix_label_for', |
| 765 | 'generate_aria_labels', |
| 766 | 'fix_empty_buttons', |
| 767 | 'remove_positive_tabindex', |
| 768 | 'group_form_fields', |
| 769 | 'clean_hidden_labels', |
| 770 | 'placeholders_to_aria', |
| 771 | 'fix_select_labels', |
| 772 | 'fix_submit_labels', |
| 773 | ]; |
| 774 | |
| 775 | foreach ($checkboxes as $key) { |
| 776 | $sanitized[$key] = isset($input[$key]) && $input[$key] === '1' ? '1' : '0'; |
| 777 | } |
| 778 | |
| 779 | return $sanitized; |
| 780 | |
| 781 | } |
| 782 | |
| 783 | |
| 784 | } |
| 785 | |
| 786 | // Initialize the class |
| 787 | new ARIAAT_Admin(); |
| 788 |