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