PluginProbe ʕ •ᴥ•ʔ
Web Accessibility Toolkit – Accessibility Checker & ARIA for WCAG, Section 508 & ADA Compliance / 1.5.7
Web Accessibility Toolkit – Accessibility Checker & ARIA for WCAG, Section 508 & ADA Compliance v1.5.7
trunk 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6
aria-accessibility-toolkit / includes / class-admin.php
aria-accessibility-toolkit / includes Last commit date
class-admin.php 8 months ago class-menu-aria-labels.php 8 months ago index.php 11 months ago
class-admin.php
866 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('Web Accessibility', 'Web Accessibility', '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
87 <div class="ariaat-title">
88 <h1 class="wp-heading-inline">Web Accessibility Toolkit <span><?php echo esc_attr(ARIAATVERSION); ?></span></h1>
89 </div>
90
91 <div class="ariaat-header">
92
93 <div class="ariaat-header-buttons">
94 <a href="https://wordpress.org/support/plugin/aria-accessibility-toolkit/reviews/#new-post" target="_blank" class="small">Leave a review</a>
95 <a href="https://wcagforwp.com/docs/" target="_blank" class="button-primary small">Docs</a>
96 <a href="https://wcagforwp.com/downloads/pro-plugin?utm_source=plugin&utm_medium=admin&utm_campaign=pro_upsell&utm_content=header" target="_blank" class="button-primary small">PRO Plugin</a>
97 </div>
98 </div>
99
100 <div class="main_content">
101 <h2 class="nav-tab-wrapper">
102 <a href="?page=ariaat&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : '' ?>">General</a>
103 <a href="?page=ariaat&tab=aria" class="nav-tab <?php echo $active_tab == 'aria' ? 'nav-tab-active' : '' ?>">ARIA</a>
104 <a href="?page=ariaat&tab=roles" class="nav-tab <?php echo $active_tab == 'roles' ? 'nav-tab-active' : '' ?>">Roles</a>
105 <a href="?page=ariaat&tab=contrast" class="nav-tab <?php echo $active_tab == 'contrast' ? 'nav-tab-active' : '' ?>">Contrast</a>
106 <a href="?page=ariaat&tab=images" class="nav-tab <?php echo $active_tab == 'images' ? 'nav-tab-active' : '' ?>">Images</a>
107 <a href="?page=ariaat&tab=forms" class="nav-tab <?php echo $active_tab == 'forms' ? 'nav-tab-active' : '' ?>">Forms</a>
108 <?php do_action( 'ariaat_after_nav_tab_wrapper', $active_tab ); ?>
109 </h2>
110
111
112 <form method="post" action="options.php">
113 <?php
114 if ($active_tab === 'general') {
115 settings_fields('ariaat_general_group');
116 $settings = get_option('ariaat_general_settings', []);
117
118 ?>
119 <div class="section">
120 <h3><span class="dashicons dashicons-laptop"></span> Frontend Accessibility Checker</h3>
121 <div class="setting-row no-border">
122 <div class="setting-label">
123 <p class="description">
124 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.
125 <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?>
126 <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/?utm_source=plugin&utm_medium=admin&utm_campaign=pro_upsell&utm_content=scanner">PRO plugin</a> also includes deep scanning of forms & works with all form plugins.
127 <?php } ?>
128 </p>
129 </div>
130 <div class="setting-control">
131 <label class="toggle-switch">
132 <input type="checkbox" name="ariaat_general_settings[enable_frontend_checker]" value="1" <?php checked($settings['enable_frontend_checker'] ?? '', '1'); ?> />
133 <span class="toggle-slider"></span>
134 </label>
135 </div>
136 </div>
137
138 </div>
139 <div class="section">
140 <h3><span class="dashicons dashicons-universal-access-alt"></span> General Accessibility Enhancements</h3>
141
142 <div class="setting-row">
143 <div class="setting-label">
144 <strong>Language</strong>
145 <p class="description">
146 Sets the default "lang" attribute in your site’s HTML. This helps screen readers and search engines understand the language of your site. For example: <i>en</i> for English, <i>en-AU</i> for Australian English, <i>fr</i> for French.
147 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Techniques/html/H57.html">3.1.1 Language of Page (A)</a></span>
148 </p>
149 </div>
150 <div class="setting-control">
151 <input type="text" name="ariaat_general_settings[language]" value="<?php echo esc_attr($settings['language'] ?? ''); ?>" class="regular-text" placeholder="e.g. en, en-AU" />
152 </div>
153 </div>
154
155 <div class="setting-row">
156 <div class="setting-label">
157 <strong>Skip Link Target</strong>
158 <p class="description">
159 Adds a hidden “Skip to content” link at the top of each page for keyboard and screen reader users.
160 Enter a CSS selector that matches your main content area (e.g., <i>#primary</i> or <i>.main-content</i>).
161 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html">2.4.1 Bypass Blocks (A)</a></span>
162 </p>
163 </div>
164 <div class="setting-control">
165 <input type="text" name="ariaat_general_settings[skip_link]" value="<?php echo esc_attr($settings['skip_link'] ?? ''); ?>" class="regular-text" placeholder=".main-content" />
166 </div>
167 </div>
168
169 <div class="setting-row">
170 <div class="setting-label">
171 <strong>Show Focus Outline</strong>
172 <p class="description">
173 Ensures keyboard users can clearly see which element is currently focused, even if a theme hides outlines.
174 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html">2.4.7 Focus Visible (AA)</a></span>
175 </p>
176 </div>
177 <div class="setting-control">
178 <label class="toggle-switch">
179 <input type="checkbox" name="ariaat_general_settings[focus_outline]" value="1" <?php checked($settings['focus_outline'] ?? '', '1'); ?> />
180 <span class="toggle-slider"></span>
181 </label>
182 </div>
183 </div>
184
185 <div class="setting-row">
186 <div class="setting-label">
187 <strong>Fix Tab Order</strong>
188 <p class="description">
189 Removes <i>tabindex</i> values greater than 0 to maintain a logical, predictable keyboard focus order.
190 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html">2.4.3 Focus Order (A)</a></span>
191 </p>
192 </div>
193 <div class="setting-control">
194 <label class="toggle-switch">
195 <input type="checkbox" name="ariaat_general_settings[fix_tabindex]" value="1" <?php checked($settings['fix_tabindex'] ?? '', '1'); ?> />
196 <span class="toggle-slider"></span>
197 </label>
198 </div>
199 </div>
200
201 <div class="setting-row">
202 <div class="setting-label">
203 <strong>Make Viewport Scalable</strong>
204 <p class="description">
205 Removes <i>user-scalable=no</i> from the viewport meta tag to allow pinch-zooming on mobile devices.
206 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/reflow.html">1.4.10 Reflow (AA)</a> and
207 <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/resize-text.html">1.4.4 Resize Text (AA)</a></span>
208 </p>
209 </div>
210 <div class="setting-control">
211 <label class="toggle-switch">
212 <input type="checkbox" name="ariaat_general_settings[make_viewport_scalable]" value="1" <?php checked($settings['make_viewport_scalable'] ?? '', '1'); ?> />
213 <span class="toggle-slider"></span>
214 </label>
215 </div>
216 </div>
217
218 <?php
219 $default_control = sprintf(
220 '<div class="pro-label"><a target="_blank" href="%s">PRO</a></div>',
221 esc_url('https://wcagforwp.com/downloads/pro-plugin/')
222 );
223 ?>
224
225 <!-- Open External Links in New Tab -->
226 <div class="setting-row">
227 <div class="setting-label">
228 <strong>Open External Links in New Tab</strong>
229 <p class="description">
230 Adds <i>target="_blank"</i> and <i>rel="noopener"</i> to external links and appends an SR-only (opens in new tab) hint.
231 <span class="wcag">WCAG: <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/change-on-request.html">3.2.5 Change on Request (AA)</a></span>
232 </p>
233 </div>
234 <div class="setting-control">
235 <?php
236 echo apply_filters('ariaat_control_open_external_new_tab', $default_control, $settings);
237 ?>
238 </div>
239 </div>
240
241 <!-- Force Underline on Text Links -->
242 <div class="setting-row">
243 <div class="setting-label">
244 <strong>Force Underline on Text Links</strong>
245 <p class="description">
246 Ensures text links are visually distinct without relying on color alone, improving visibility for all users.
247 <span class="wcag">WCAG:
248 <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/use-of-color.html">1.4.1 Use of Color (A)</a>
249 and
250 <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html">2.4.7 Focus Visible (AA)</a></span>
251 </p>
252 </div>
253 <div class="setting-control">
254 <?php
255 echo apply_filters('ariaat_control_force_link_underlines', $default_control, $settings);
256 ?>
257 </div>
258 </div>
259
260 <!-- Mark Decorative Images -->
261 <div class="setting-row no-border">
262 <div class="setting-label">
263 <strong>Mark Decorative Images</strong>
264 <p class="description">
265 Adds <i>role="presentation"</i> to <i>&lt;img alt=""&gt;</i> elements that aren’t links, so screen readers skip purely decorative content.
266 <span class="wcag">WCAG:
267 <a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html">1.1.1 Non-text Content (A)</a></span>
268 </p>
269 </div>
270 <div class="setting-control">
271 <?php
272 echo apply_filters('ariaat_control_mark_decorative_images', $default_control, $settings);
273 ?>
274 </div>
275 </div>
276 </div>
277
278
279 <div class="section">
280 <h3><span class="dashicons dashicons-star-filled"></span> Like this plugin?</h3>
281 <div class="setting-row no-border">
282 <div class="setting-label">
283 <p class="description">The Web Accessibility Toolkit is a new plugin and we’d love your support! If it’s been helpful, please <a href="https://wordpress.org/support/plugin/aria-accessibility-toolkit/reviews/#new-post" target="_blank">leave a review on WordPress.org</a> - it really helps others discover it.</p>
284 </div>
285 </div>
286 </div>
287
288 <?php
289 } else if ($active_tab === 'aria') {
290 settings_fields('ariaat_aria_group');
291 $items = get_option('ariaat_aria_mappings', []);
292 ?>
293
294 <div class="section">
295 <h3><span class="dashicons dashicons-menu"></span> Menu ARIA Labels</h3>
296 <p class="desc">
297 Selecting a menu below will add a new <b>ARIA Label</b> field to all items within that menu. Visit the <a href="<?php echo esc_url( admin_url('/nav-menus.php') ); ?>" target="_blank">menu</a> page to then add ARIA labels to each item.
298 <br>This is ideal when the link text might be "read more" but adding an aria-label like "Learn more about our company" is much more descriptive.
299 </p>
300
301 <?php
302 $all_menus = wp_get_nav_menus();
303 $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus);
304 $selected = get_option('ariaat_aria_menus', []);
305
306 foreach ($menus as $menu) :
307 $checked = is_array( $selected ) && in_array($menu->term_id, $selected);
308 ?>
309 <div class="setting-row no-border">
310 <div class="setting-label">
311 <strong><?php echo esc_html($menu->name); ?></strong>
312 </div>
313 <div class="setting-control">
314 <label class="toggle-switch">
315 <input type="checkbox" name="ariaat_aria_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> />
316 <span class="toggle-slider"></span>
317 </label>
318 </div>
319 </div>
320 <?php endforeach; ?>
321 <p class="desc">
322 <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?>
323 You can select up to 2 menus in the free version.
324 Upgrade to the <a href="https://wcagforwp.com/downloads/pro-plugin/?utm_source=plugin&utm_medium=admin&utm_campaign=pro_upsell&utm_content=aria-menus">PRO plugin</a> to add unlimited menus.
325 <?php } ?>
326 </p>
327 </div>
328
329 <div class="section">
330 <h3><span class="dashicons dashicons-editor-code"></span> Custom ARIA Attributes</h3>
331 <p class="desc">
332 Add custom <code>aria-*</code> attributes to elements on your site by targeting them with CSS selectors. 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.
333 </p>
334
335 <table class="form-table" id="aria-table">
336 <thead>
337 <tr>
338 <th>CSS Selector</th>
339 <th>ARIA Attribute</th>
340 <th>Value</th>
341 <th></th>
342 </tr>
343 </thead>
344 <tbody>
345 <?php foreach ($items as $index => $item) : ?>
346 <tr>
347 <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>
348 <td>
349 <select name="ariaat_aria_mappings[<?php echo esc_attr($index); ?>][attribute]">
350 <?php foreach ($this->get_aria_attributes() as $attr => $attr_item): ?>
351 <option value="<?php echo esc_attr($attr); ?>" <?php selected($item['attribute'], $attr); ?>>
352 <?php echo esc_html($attr_item['label']); ?>
353 </option>
354 <?php endforeach; ?>
355 </select>
356 </td>
357 <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>
358 <td><button type="button" class="button remove-row">Remove</button></td>
359 </tr>
360 <?php endforeach; ?>
361 </tbody>
362 </table>
363
364 <p><button type="button" class="button" id="add-aria-row">Add ARIA Attribute</button></p>
365
366 <p class="desc">
367 <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?>
368 <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/?utm_source=plugin&utm_medium=admin&utm_campaign=pro_upsell&utm_content=aria-attributes">PRO plugin</a> adds more ARIA attributes such as 'aria-checked', 'aria-selected', 'aria-current', 'aria-required', 'aria-level' & more.
369 <?php } ?>
370 </p>
371 </div>
372
373 <?php
374
375
376 } elseif ($active_tab == 'roles') {
377 settings_fields('ariaat_role_group');
378 $items = get_option('ariaat_role_mappings', []);
379 ?>
380
381 <div class="section">
382 <h3><span class="dashicons dashicons-menu"></span> Menu Roles</h3>
383 <p class="desc">
384 Automatically adds <code>role="navigation"</code> to selected menus for improved screen reader support. Use this only if the menu's container is <code>&lt;div&gt;</code> or another non-semantic element. Do <strong>not</strong> use it on <code>&lt;nav&gt;</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.
385 </p>
386
387 <?php
388 $all_menus = wp_get_nav_menus();
389 $menus = apply_filters('ariaat_allowed_menus', array_slice($all_menus, 0, 2), $all_menus);
390 $selected_roles = get_option('ariaat_role_menus', []);
391
392 foreach ($menus as $menu) :
393 $checked = is_array( $selected_roles ) && in_array($menu->term_id, $selected_roles);
394 ?>
395 <div class="setting-row no-border">
396 <div class="setting-label">
397 <strong><?php echo esc_html($menu->name); ?></strong>
398 </div>
399 <div class="setting-control">
400 <label class="toggle-switch">
401 <input type="checkbox" name="ariaat_role_menus[]" value="<?php echo esc_attr($menu->term_id); ?>" <?php checked($checked); ?> />
402 <span class="toggle-slider"></span>
403 </label>
404 </div>
405 </div>
406 <?php endforeach; ?>
407
408 <p class="desc">
409 <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?>
410 You can select up to 2 menus in the free version.
411 Upgrade to the <a href="https://wcagforwp.com/downloads/pro-plugin/?utm_source=plugin&utm_medium=admin&utm_campaign=pro_upsell&utm_content=roles-menu">PRO plugin</a> to add unlimited menus.
412 <?php } ?>
413 </p>
414
415 </div>
416
417 <div class="section">
418 <h3><span class="dashicons dashicons-editor-code"></span> Custom Roles</h3>
419
420 <p class="desc">
421 Assign ARIA landmark <code>role</code> attributes to elements on your site using CSS selectors.
422 This is helpful when your theme doesn’t use semantic HTML or lacks proper roles for key regions like navigation, main content, or banners.
423 To find the right selectors, right-click any element on your site and choose "Inspect." Some familiarity with CSS and HTML is recommended.
424 </p>
425
426
427 <table class="form-table" id="role-table">
428 <thead>
429 <tr>
430 <th>CSS Selector</th>
431 <th>Role</th>
432 <th></th>
433 </tr>
434 </thead>
435 <tbody>
436 <?php foreach ($items as $index => $item) : ?>
437 <tr>
438 <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>
439 <td>
440 <select name="ariaat_role_mappings[<?php echo esc_attr($index); ?>][role]">
441 <?php foreach ($this->get_role_options() as $role => $role_item): ?>
442 <option value="<?php echo esc_attr($role); ?>" <?php selected($item['role'], $role); ?>>
443 <?php echo esc_html($role_item['label']); ?>
444 </option>
445 <?php endforeach; ?>
446 </select>
447 </td>
448 <td><button type="button" class="button remove-row">Remove</button></td>
449 </tr>
450 <?php endforeach; ?>
451 </tbody>
452 </table>
453
454 <p><button type="button" class="button" id="add-role-row">Add Role</button></p>
455 <p class="desc">
456 <?php if (! defined('ARIAAT_PRO_ACTIVE')) { ?>
457 <br>The <a href="https://wcagforwp.com/downloads/pro-plugin/">PRO plugin</a> adds more ARIA landmark roles such as 'alert', 'dialog', 'menu', 'menuitem', 'searchbox', 'tab', 'tooltip' & more.
458 <?php } ?>
459 </p>
460 </div>
461
462 <?php
463
464
465 } else if( $active_tab == 'contrast' ) {
466 settings_fields('ariaat_contrast_group');
467 $items = get_option('ariaat_contrast_mappings', []);
468 ?>
469
470 <div class="section">
471 <h3><span class="dashicons dashicons-star-half"></span> High Contrast Adjustments</h3>
472 <p class="desc">
473 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.
474 </p>
475
476 <table class="form-table" id="contrast-table">
477 <thead>
478 <tr>
479 <th class="selector">HTML Selector</th>
480 <th class="color">Text</th>
481 <th class="color">Background</th>
482 <th>Contrast</th>
483 <th class="remove"></th>
484 </tr>
485 </thead>
486 <tbody>
487 <?php foreach ($items as $index => $item) : ?>
488 <tr>
489 <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>
490 <td>
491 <input type="color"
492 id="text-color-picker-<?php echo esc_attr($index); ?>"
493 class="text-color"
494 data-index="<?php echo esc_attr($index); ?>"
495 value="<?php echo esc_attr($item['color']); ?>"
496 >
497 <input type="hidden"
498 name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][color]"
499 id="real-text-color-<?php echo esc_attr($index); ?>"
500 value="<?php echo esc_attr($item['color']); ?>"
501 >
502 <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="text">Clear</button>
503 </td>
504 <td>
505 <input type="color"
506 id="bg-color-picker-<?php echo esc_attr($index); ?>"
507 class="bg-color"
508 data-index="<?php echo esc_attr($index); ?>"
509 value="<?php echo esc_attr($item['background']); ?>"
510 >
511 <input type="hidden"
512 name="ariaat_contrast_mappings[<?php echo esc_attr($index); ?>][background]"
513 id="real-bg-color-<?php echo esc_attr($index); ?>"
514 value="<?php echo esc_attr($item['background']); ?>"
515 >
516 <button type="button" class="button clear-color" data-target="<?php echo esc_attr($index); ?>" data-type="bg">Clear</button>
517 </td>
518 <td>
519 <div class="contrast-result" id="contrast-result-<?php echo esc_attr( $index ); ?>"></div>
520 </td>
521
522 <td><button type="button" class="button remove-row">Remove</button></td>
523 </tr>
524 <?php endforeach; ?>
525 </tbody>
526 </table>
527 <p><button type="button" class="button" id="add-contrast-row">Add Contrast Item</button></p>
528
529 </div>
530
531 <?php
532
533 } else if ($active_tab === 'images') {
534
535 settings_fields('ariaat_images_group');
536 $settings = get_option('ariaat_image_settings', []);
537
538 $show_all = isset($_POST['ariaat_image_settings']['show_all'])
539 ? boolval($_POST['ariaat_image_settings']['show_all'])
540 : (!empty($settings['show_all']));
541
542 $paged = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
543 $per_page = 50;
544 $offset = ($paged - 1) * $per_page;
545
546 $meta_query = $show_all ? [] : [
547 'relation' => 'OR',
548 [
549 'key' => '_wp_attachment_image_alt',
550 'value' => '',
551 'compare' => '='
552 ],
553 [
554 'key' => '_wp_attachment_image_alt',
555 'value' => ' ',
556 'compare' => '='
557 ],
558 [
559 'key' => '_wp_attachment_image_alt',
560 'compare' => 'NOT EXISTS'
561 ]
562 ];
563
564 $query = new WP_Query([
565 'post_type' => 'attachment',
566 'post_mime_type' => 'image',
567 'post_status' => 'inherit',
568 'posts_per_page' => $per_page,
569 'offset' => $offset,
570 'meta_query' => $meta_query
571 ]);
572
573 $attachments = $query->posts;
574 $total = $query->found_posts;
575 $total_pages = ceil($total / $per_page);
576 ?>
577 <div class="section">
578 <h3><span class="dashicons dashicons-format-gallery"></span> Images Missing Alt Text</h3>
579 <p class="desc">
580 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.
581 </p>
582 <p>
583 <label>
584 <input type="checkbox" name="ariaat_image_settings[show_all]" value="1"
585 <?php checked($settings['show_all'] ?? '', '1'); ?> />
586 Show all images (not just those missing alt text)
587 </label>
588 </p>
589
590 <?php if (empty($attachments)) : ?>
591 <p>No images without alt text were found.</p>
592 <?php else : ?>
593
594 <p style="margin-bottom: 1em;">
595 <button type="button" class="button copy-all-filenames">Copy all Filenames to Alt</button>
596 <button type="button" class="button copy-all-titles">Copy all Titles to Alt Tags</button>
597 </p>
598
599 <table class="form-table widefat fixed striped" id="alt-images" style="margin-bottom: 2em;">
600 <thead>
601 <tr>
602 <th class="id">ID</th>
603 <th class="thumb">Thumb</th>
604 <th>Alt Tag</th>
605 <th>Filename</th>
606 <th>Attached To</th>
607 <th class="save">Save</th>
608 </tr>
609 </thead>
610 <tbody>
611 <?php foreach ($attachments as $attachment) :
612
613 $parent_id = $attachment->post_parent;
614
615 if ($parent_id) {
616 $parent = get_post($parent_id);
617 if (!$parent || $parent->post_status !== 'publish') {
618 continue; // skip this image
619 }
620 }
621 $id = $attachment->ID;
622 $alt = get_post_meta($id, '_wp_attachment_image_alt', true);
623 $thumb = wp_get_attachment_image_url($id, [48, 48]);
624 $nonce = wp_create_nonce("ariaat_update_alt_{$id}");
625 $filename = basename(get_attached_file($id));
626 $parent_id = $attachment->post_parent;
627 $attached_to = $parent_id ? get_post($parent_id) : null;
628 ?>
629
630 <tr data-id="<?php echo esc_attr($id); ?>">
631 <td><?php echo esc_html($id); ?></td>
632 <td><img src="<?php echo esc_url($thumb); ?>" width="48" height="48" /></td>
633 <td><input type="text" class="ariaat-alt" value="<?php echo esc_attr($alt); ?>" placeholder="no alt tag" /></td>
634 <td>
635 <button type="button" class="button button-small copy-filename" data-filename="<?php echo esc_attr($filename); ?>">
636 Copy to Alt
637 </button>
638 <br>
639 <span class="small"><?php echo esc_html($filename); ?></span>
640 </td>
641 <td>
642 <?php if ($attached_to) : ?>
643 <button type="button"
644 class="button button-small copy-title"
645 data-title="<?php echo esc_attr(get_the_title($attached_to->ID)); ?>">
646 Copy to Alt
647 </button>
648 <br>
649 <span class="small">
650 <a href="<?php echo esc_url(get_edit_post_link($attached_to->ID)); ?>">
651 <?php echo esc_html(get_the_title($attached_to->ID)); ?>
652 </a> <small>(<?php echo esc_html( ucfirst(get_post_type($attached_to)) ); ?>)</small>
653 </span>
654 <?php else : ?>
655 <em>Not attached</em>
656 <?php endif; ?>
657 </td>
658 <td>
659 <button class="button ariaat-save-alt"
660 data-id="<?php echo esc_attr($id); ?>"
661 data-nonce="<?php echo esc_attr($nonce); ?>">
662 Save
663 </button>
664 </td>
665 </tr>
666 <?php endforeach; ?>
667 </tbody>
668 </table>
669
670 <?php if ($total_pages > 1) : ?>
671 <div class="ariaat-pagination">
672 <?php
673 $base_url = remove_query_arg(['paged']);
674 for ($i = 1; $i <= $total_pages; $i++) :
675 $url = add_query_arg('paged', $i, $base_url);
676 $active_class = ($i == $paged) ? 'active' : '';
677 printf(
678 '<a class="%s" href="%s">%d</a> ',
679 esc_attr($active_class),
680 esc_url($url),
681 $i
682 );
683 endfor;
684 ?>
685 </div>
686 <?php endif; ?>
687
688 <?php endif; ?>
689
690 </div>
691
692
693 <?php } else if ($active_tab === 'forms') {
694
695 $forms_output = '
696 <div class="section">
697 <h3><span class="dashicons dashicons-info"></span> Upgrade to PRO</h3>
698 <p>Get deep form scanning and automatic form fixes when you upgrade to the PRO plugin.<br>Works with all form plugins.</p>
699 <a style="margin-bottom:30px" href="https://wcagforwp.com/downloads/pro-plugin/" class="button button-primary" target="_blank">View PRO Plugin</a>
700 </div>
701 ';
702
703 echo apply_filters( 'ariaat_forms_page_output', $forms_output, $active_tab );
704
705 }
706
707 do_action( 'ariaat_before_admin_submit_button', $active_tab );
708
709 if ($active_tab !== 'forms' || ($active_tab === 'forms' && defined('ARIAAT_PRO_ACTIVE') ) )
710 submit_button();
711
712 ?>
713
714 </form>
715
716 </div>
717
718 </div>
719
720 <?php
721 }
722
723
724 private function get_aria_attributes() {
725 $attributes = [
726 'aria-describedby' => ['label' => 'aria-describedby'],
727 'aria-disabled' => ['label' => 'aria-disabled'],
728 'aria-expanded' => ['label' => 'aria-expanded'],
729 'aria-hidden' => ['label' => 'aria-hidden'],
730 'aria-label' => ['label' => 'aria-label'],
731 'aria-labelledby' => ['label' => 'aria-labelledby'],
732 ];
733
734 /**
735 * Filter the list of ARIA attributes available in the plugin.
736 *
737 * @param array $attributes The default set of ARIA attributes.
738 */
739 return apply_filters('ariaat_aria_attributes_options', $attributes);
740 }
741
742
743 private function get_role_options() {
744 $roles = [
745 'banner' => ['label' => 'banner'],
746 'button' => ['label' => 'button'],
747 'checkbox' => ['label' => 'checkbox'],
748 'complementary' => ['label' => 'complementary'],
749 'contentinfo' => ['label' => 'contentinfo'],
750 'form' => ['label' => 'form'],
751 'grid' => ['label' => 'grid'],
752 'heading' => ['label' => 'heading'],
753 'img' => ['label' => 'img'],
754 'link' => ['label' => 'link'],
755 'list' => ['label' => 'list'],
756 'listitem' => ['label' => 'listitem'],
757 'main' => ['label' => 'main'],
758 'navigation' => ['label' => 'navigation'],
759 'region' => ['label' => 'region'],
760 'row' => ['label' => 'row'],
761 'rowheader' => ['label' => 'rowheader'],
762 'search' => ['label' => 'search'],
763 'table' => ['label' => 'table'],
764 'textbox' => ['label' => 'textbox'],
765 ];
766
767 /**
768 * Filter the list of available roles.
769 *
770 * @param array $roles Array of role definitions.
771 */
772 return apply_filters('ariaat_roles_options', $roles);
773 }
774
775
776 public function sanitize_array($input) {
777 if (!is_array($input)) {
778 return sanitize_text_field($input);
779 }
780
781 foreach ($input as $key => $item) {
782 if (is_array($item)) {
783 foreach ($item as $subkey => $value) {
784 // Sanitize based on known keys
785 if (in_array($subkey, ['selector'], true)) {
786 // Allow basic CSS selectors: letters, numbers, dashes, underscores, dots, #, >, space, :
787 $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\s\.\#\>\:\[\]\=\"~\+\-\*(),]/', '', $value);
788 } elseif (in_array($subkey, ['attribute', 'role'], true)) {
789 // Whitelist attribute/role names to be alphanumeric with optional dashes
790 $input[$key][$subkey] = preg_replace('/[^a-zA-Z0-9\-_]/', '', $value);
791 } else {
792 $input[$key][$subkey] = sanitize_text_field($value);
793 }
794 }
795 } else {
796 $input[$key] = sanitize_text_field($item);
797 }
798 }
799
800 return $input;
801 }
802
803
804
805 public function sanitize_menu_selection($input) {
806 $allowed_menus = array_keys($this->get_all_menus());
807
808 if( ! $input || ! array( $allowed_menus ) || ! array( $input ) )
809 return;
810 $selected = array_intersect($input, $allowed_menus);
811
812 return $selected;
813 }
814
815 public function sanitize_general_settings($input) {
816
817 $output = [];
818
819 // Language setting
820 $output['language'] = isset($input['language']) ? sanitize_text_field($input['language']) : '';
821
822 $output['skip_link'] = isset($input['skip_link']) ? sanitize_text_field($input['skip_link']) : '';
823
824 $output['enable_frontend_checker'] = isset($input['enable_frontend_checker']) ? '1' : '';
825 $output['focus_outline'] = isset($input['focus_outline']) ? '1' : '';
826 $output['fix_tabindex'] = isset($input['fix_tabindex']) ? '1' : '';
827 $output['make_viewport_scalable'] = isset($input['make_viewport_scalable']) ? '1' : '';
828 $output['open_external_new_tab'] = isset($input['open_external_new_tab']) ? '1' : '';
829 $output['force_link_underlines'] = isset($input['force_link_underlines']) ? '1' : '';
830 $output['mark_decorative_images'] = isset($input['mark_decorative_images']) ? '1' : '';
831
832 return $output;
833
834 }
835
836 public function sanitize_form_settings($input) {
837
838 $sanitized = [];
839
840 $checkboxes = [
841 'auto_generate_labels',
842 'fix_label_for',
843 'generate_aria_labels',
844 'fix_empty_buttons',
845 'remove_positive_tabindex',
846 'group_form_fields',
847 'clean_hidden_labels',
848 'placeholders_to_aria',
849 'fix_select_labels',
850 'fix_submit_labels',
851 ];
852
853 foreach ($checkboxes as $key) {
854 $sanitized[$key] = isset($input[$key]) && $input[$key] === '1' ? '1' : '0';
855 }
856
857 return $sanitized;
858
859 }
860
861
862 }
863
864 // Initialize the class
865 new ARIAAT_Admin();
866