PluginProbe
WP Accessibility / 2.3.5
WP Accessibility v2.3.5
2.3.5 trunk 1.0.0 1.2.3 1.2.9 1.3.0 1.3.11 1.4.0 1.4.6 1.5.0 1.5.10 1.6.0 1.6.10 1.7.0 1.7.14 1.8.0 1.8.1 1.9.0 1.9.2 2.0.0 2.0.1 2.1.0 2.1.19 2.2.0 2.2.5 All 29 releases
wp-accessibility / wp-accessibility.php

wp-accessibility.php in WP Accessibility 2.3.5, at wp-accessibility.php

1,063 lines 36.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WP Accessibility
4 *
5 * @package WP Accessibility
6 * @author Joe Dolson
7 * @copyright 2012-2026 Joe Dolson
8 * @license GPL-2.0+
9 *
10 * @wordpress-plugin
11 * Plugin Name: WP Accessibility
12 * Plugin URI: https://www.joedolson.com/wp-accessibility/
13 * Description: Helps improve accessibility in your WordPress site, like removing title attributes.
14 * Author: Joe Dolson
15 * Author URI: https://www.joedolson.com/
16 * Text Domain: wp-accessibility
17 * Domain Path: /lang
18 * License: GPL-2.0+
19 * License URI: http://www.gnu.org/license/gpl-2.0.txt
20 * Version: 2.3.5
21 */
22
23 /*
24 Copyright 2012-2026 Joe Dolson (email : joe@joedolson.com)
25
26 This program is free software; you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation; either version 2 of the License, or
29 (at your option) any later version.
30
31 This program is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with this program; if not, write to the Free Software
38 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 */
40
41 if ( 'on' === get_option( 'wpa_toolbar' ) || 'on' === get_option( 'wpa_widget_toolbar' ) ) {
42 require_once __DIR__ . '/wp-accessibility-toolbar.php';
43 }
44 require_once __DIR__ . '/wp-accessibility-longdesc.php';
45 require_once __DIR__ . '/wp-accessibility-alt.php';
46 require_once __DIR__ . '/wp-accessibility-contrast.php';
47 require_once __DIR__ . '/wp-accessibility-settings.php';
48 require_once __DIR__ . '/wp-accessibility-overlay.php';
49 require_once __DIR__ . '/wp-accessibility-admin.php';
50 require_once __DIR__ . '/wp-accessibility-help.php';
51 if ( 'off' !== get_option( 'wpa_track_stats' ) ) {
52 require_once __DIR__ . '/wp-accessibility-stats.php';
53 }
54
55 define( 'WP_ACCESSIBILITY_VERSION', '2.3.5' );
56
57 register_activation_hook( __FILE__, 'wpa_install' );
58
59 add_action( 'admin_notices', 'wpa_status_notice', 10 );
60 /**
61 * Display notice in Playground for demo purposes.
62 */
63 function wpa_status_notice() {
64 // Only shown when in the Playground preview.
65 if ( 'true' === get_option( 'wpa_show_playground_intro', '' ) ) {
66 echo '<div class="notice notice-info">';
67 echo '<h3>' . __( 'Thanks for trying out WP Accessibility!', 'wp-accessibility' ) . '</h3>';
68 echo '<p>' . __( "Let me give you a few quick things to try out while you're here:", 'wp-accessibility' ) . '</p>';
69 echo '<ol>';
70 echo '<li>' . __( 'Create a new post and add media to observe some WP Accessibility features.', 'wp-accessibility' ) . '</li>';
71 echo '<li>' . __( 'Go to the Themes page and install a theme of your choice, then explore the site. Errors corrected are logged in the console and saved to stats.', 'wp-accessibility' ) . '</li>';
72 echo '<li>' . __( 'Visit the WP Accessibility Stats to view what WP Accessibility did on each page.', 'wp-accessibility' ) . '</li>';
73 echo '</ol>';
74 // translators: link to plugin documentation.
75 echo '<p>' . sprintf( __( 'To learn more, check out the <a href="%s">plugin documentation</a>.', 'wp-accessibility' ), 'https://docs.joedolson.com/wp-accessibility/' ) . '</p>';
76 echo '</div>';
77 }
78 }
79
80 add_action( 'admin_menu', 'wpa_admin_menu' );
81 /**
82 * Set up admin menu.
83 */
84 function wpa_admin_menu() {
85 add_menu_page( __( 'WP Accessibility - Features', 'wp-accessibility' ), __( 'WP Accessibility', 'wp-accessibility' ), 'manage_options', 'wp-accessibility', 'wpa_admin_settings', 'dashicons-universal-access' );
86 add_submenu_page( 'wp-accessibility', __( 'WP Accessibility - Fixes', 'wp-accessibility' ), __( 'Accessibility Fixes', 'wp-accessibility' ), 'manage_options', 'wp-accessibility-overlay', 'wpa_admin_overlay_settings' );
87 add_submenu_page( 'wp-accessibility', __( 'WP Accessibility - Admin & Testing', 'wp-accessibility' ), __( 'Testing & Admin', 'wp-accessibility' ), 'manage_options', 'wp-accessibility-admin', 'wpa_admin_admin_settings' );
88 add_submenu_page( 'wp-accessibility', __( 'WP Accessibility - Help', 'wp-accessibility' ), __( 'Get Help', 'wp-accessibility' ), 'manage_options', 'wp-accessibility-help', 'wpa_help_screen' );
89 }
90
91 /**
92 * Install on activation.
93 */
94 function wpa_install() {
95 if ( 'true' !== get_option( 'wpa_installed' ) ) {
96 add_option( 'asl_styles_focus', '' );
97 add_option( 'asl_styles_passive', '' );
98 add_option( 'asl_default_styles', 'true' );
99 add_option( 'wpa_target', 'on' );
100 add_option( 'wpa_search', 'on' );
101 add_option( 'wpa_tabindex', 'on' );
102 add_option( 'wpa_continue', 'Continue Reading' );
103 add_option( 'wpa_focus', '' );
104 add_option( 'wpa_installed', 'true' );
105 add_option( 'wpa_longdesc', 'button' );
106 add_option( 'wpa_post_types', array( 'post' ) );
107 } else {
108 wpa_check_version();
109 }
110 }
111
112 /**
113 * Check current version and upgrade if needed.
114 *
115 * @return string
116 */
117 function wpa_check_version() {
118 // upgrade for version 1.3.0.
119 $version = WP_ACCESSIBILITY_VERSION;
120 if ( version_compare( $version, '2.3.0', '<' ) ) {
121 add_option( 'wpa_lang_attributes', 'on' );
122 }
123 if ( version_compare( $version, '1.3.0', '<' ) ) {
124 add_option( 'wpa_longdesc', 'button' );
125 }
126 // upgrade for version 1.9.0.
127 if ( version_compare( $version, '1.9.0', '<' ) ) {
128 add_option( 'wpa_post_types', array( 'post' ) );
129 $wpa_toolbar_fs = get_option( 'wpa_toolbar_fs', '' );
130 $wpa_toolbar_ct = get_option( 'wpa_toolbar_ct', '' );
131 if ( '' === $wpa_toolbar_fs ) {
132 update_option( 'wpa_toolbar_fs', 'on' );
133 }
134 if ( '' === $wpa_toolbar_ct ) {
135 update_option( 'wpa_toolbar_ct', 'on' );
136 }
137 }
138 if ( version_compare( $version, '2.2.0', '<' ) ) {
139 $ld_option = get_option( 'wpa_longdesc' );
140 if ( 'jquery' === $ld_option ) {
141 update_option( 'wpa_longdesc', 'button' );
142 }
143 }
144
145 return $version;
146 }
147
148 add_filter( 'plugin_action_links', 'wpa_plugin_action', 10, 2 );
149 /**
150 * Add plugin action links.
151 *
152 * @param array $links Existing links.
153 * @param string $file File name.
154 */
155 function wpa_plugin_action( $links, $file ) {
156 if ( plugin_basename( __DIR__ . '/wp-accessibility.php' ) === $file ) {
157 $admin_url = admin_url( 'admin.php?page=wp-accessibility' );
158 $links[] = "<a href='$admin_url'>" . __( 'Accessibility Settings', 'wp-accessibility' ) . '</a>';
159 }
160
161 return $links;
162 }
163
164 add_action( 'wp_enqueue_scripts', 'wpa_stylesheet' );
165 /**
166 * Enqueue stylesheets for WP Accessibility.
167 */
168 function wpa_stylesheet() {
169 $version = ( SCRIPT_DEBUG ) ? wp_rand( 10000, 100000 ) : wpa_check_version();
170 wp_register_style( 'wpa-style', plugins_url( 'css/wpa-style.css', __FILE__ ), array(), $version );
171 $ld = get_option( 'wpa_longdesc' );
172 if ( 'link' === $ld || 'jquery' === $ld || 'button' === $ld || 'on' === get_option( 'asl_enable' ) || ! empty( get_option( 'wpa_post_types', array() ) ) ) {
173 wp_enqueue_style( 'wpa-style' );
174 // these styles are derived from the WordPress skip link defaults.
175 $top = '7px';
176 if ( is_admin_bar_showing() ) {
177 $top = '37px';
178 }
179 $add_css = ( ! wpa_accessible_theme() ) ? wpa_css() : '';
180 $custom_css = ':root { --admin-bar-top : ' . $top . '; }';
181 wp_add_inline_style( 'wpa-style', wp_filter_nohtml_kses( wp_unslash( $add_css . $custom_css ) ) );
182 }
183 if ( current_user_can( 'edit_files' ) && 'on' === get_option( 'wpa_diagnostics' ) ) {
184 wp_register_style( 'diagnostic', plugins_url( 'css/diagnostic.css', __FILE__ ) );
185 wp_register_style( 'diagnostic-head', plugins_url( 'css/diagnostic-head.css', __FILE__ ) );
186 wp_enqueue_style( 'diagnostic' );
187 wp_enqueue_style( 'diagnostic-head' );
188 }
189 }
190
191 add_action( 'admin_head', 'wpa_admin_stylesheet' );
192 /**
193 * Enqueue admin stylesheets if enabled
194 */
195 function wpa_admin_stylesheet() {
196 // Used to provide an admin CSS from plug-in, now only enqueue if custom provided in theme.
197 if ( file_exists( get_stylesheet_directory() . '/wp-admin.css' ) ) {
198 $file = get_stylesheet_directory_uri() . '/wp-admin.css';
199 wp_register_style( 'wp-a11y-css', $file );
200 wp_enqueue_style( 'wp-a11y-css' );
201 }
202 }
203
204 /**
205 * Generate skiplink CSS.
206 *
207 * @param bool $defaults 'true' to return defaults regardless of settings.
208 *
209 * @return string
210 */
211 function wpa_skiplink_css( $defaults = false ) {
212 $use_defaults = get_option( 'asl_default_styles', '' );
213 $styles = '';
214 $focus = '';
215 $passive = '';
216 $visibility = ( 'on' === get_option( 'asl_visible' ) ) ? 'wpa-visible' : 'wpa-hide';
217 $is_rtl = ( is_rtl() ) ? '-rtl' : '-ltr';
218 $off_vis = ( 'on' === get_option( 'asl_visible' ) ) ? 'wpa-hide' : 'wpa-visible';
219 $off_rtl = ( is_rtl() ) ? '-ltr' : '-rtl';
220 // If not using default styles.
221 if ( 'true' !== $use_defaults && ! $defaults ) {
222 $default_focus = '';
223 $default_passive = '';
224 if ( '' !== get_option( 'asl_styles' ) ) {
225 $styles = wp_filter_nohtml_kses( get_option( 'asl_styles' ) );
226 // Ensure custom styles match settings.
227 $styles = str_replace( array( $off_vis, $off_rtl ), array( $visibility, $is_rtl ), $styles );
228 // If custom styles contain #skiplinks, we can just return this; it's probably a fully realized selector.
229 if ( false !== stripos( $styles, '#skiplinks' ) ) {
230 return $styles;
231 }
232 } else {
233 $focus = wp_filter_nohtml_kses( get_option( 'asl_styles_focus' ) );
234 $passive = wp_filter_nohtml_kses( get_option( 'asl_styles_passive' ) );
235 }
236 } else {
237 // these styles are derived from the WordPress skip link defaults.
238 $default_focus = 'background-color: #f1f1f1;
239 box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
240 color: #0073aa;
241 display: block;
242 font-weight: 600;
243 height: auto;
244 line-height: normal;
245 padding: 15px 23px 14px;
246 position: absolute;
247 left: 6px;
248 top: var(--admin-bar-top);
249 text-decoration: none;
250 text-transform: none;
251 width: auto;
252 z-index: 100000;';
253
254 // Passive default styles derived from WordPress default focus styles.
255 $default_passive = 'background-color: #fff;
256 box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
257 color: #333;
258 display: block;
259 font-weight: 600;
260 height: auto;
261 line-height: normal;
262 padding: 15px 23px 14px;
263 position: absolute;
264 left: 6px;
265 top: var(--admin-bar-top);
266 text-decoration: none;
267 text-transform: none;
268 width: auto;
269 z-index: 100000;';
270 }
271 if ( is_admin() && '' !== $styles ) {
272 return $styles;
273 }
274
275 if ( ! $focus ) {
276 $focus = $default_focus;
277 } else {
278 $focus = $default_focus . $focus;
279 }
280
281 $vis = '';
282 $invis = '';
283 $class = '.' . $visibility . $is_rtl;
284 // If links are visible, "hover" is a focus style, otherwise, it's a passive style.
285 if ( 'on' === get_option( 'asl_visible' ) ) {
286 $vis = $class . '#skiplinks a:hover,';
287 $passive = $default_passive . $passive;
288 } else {
289 $invis = $class . '#skiplinks a:hover,';
290 $passive = '';
291 }
292 $styles .= "
293 $class#skiplinks a, $invis $class#skiplinks a:visited {
294 $passive
295 }
296 $class#skiplinks a:active, $vis $class#skiplinks a:focus {
297 $focus
298 }
299 ";
300 /**
301 * Filter CSS styles output on front-end for skip links.
302 *
303 * @hook wpa_skiplink_styles
304 *
305 * @param string $styles Styles configured by settings.
306 *
307 * @return string
308 */
309 $styles = apply_filters( 'wpa_skiplink_styles', $styles );
310
311 return $styles;
312 }
313
314 /**
315 * Generate styles needed for WP Accessibility options.
316 */
317 function wpa_css() {
318 $styles = '';
319 if ( get_option( 'asl_enable' ) === 'on' ) {
320 $styles .= wpa_skiplink_css();
321 }
322 if ( 'on' === get_option( 'wpa_focus' ) ) {
323 $color = ( false !== (bool) get_option( 'wpa_focus_color' ) ) ? ' #' . get_option( 'wpa_focus_color' ) : '#233c7f';
324 $styles .= "
325 :focus { outline: 2px solid$color!important; outline-offset: 2px !important; }
326 ";
327 }
328
329 return $styles;
330 }
331
332 /**
333 * Test whether a URL is validly structured.
334 *
335 * @param string $url A purported URL.
336 *
337 * @return mixed URL if valid, false if not.
338 */
339 function wpa_is_url( $url ) {
340 return preg_match( '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $url );
341 }
342
343 add_action( 'wp_enqueue_scripts', 'wpa_enqueue_js', 100 );
344 /**
345 * Enqueue JS needed for WP Accessibility options.
346 */
347 function wpa_enqueue_js() {
348 $version = ( SCRIPT_DEBUG ) ? wp_rand( 10000, 100000 ) : wpa_check_version();
349 $visibility = ( 'on' === get_option( 'asl_visible' ) ) ? 'wpa-visible' : 'wpa-hide';
350 $output = '';
351 if ( 'on' === get_option( 'asl_enable' ) && ! wpa_accessible_theme() ) {
352 $html = '';
353 /**
354 * Customize the default value for extra skiplink. Turns on extra skiplink options in WP Accessibility versions > 1.9.0.
355 *
356 * @hook asl_extra_target
357 * @param string $setting Value to use as a default for the extra skiplink.
358 *
359 * @return string
360 */
361 $default_extra = apply_filters( 'asl_extra_target', '' );
362 $extra = get_option( 'asl_extra_target', $default_extra );
363 $extra = ( wpa_is_url( $extra ) ) ? esc_url( $extra ) : str_replace( '#', '', esc_attr( $extra ) );
364 if ( '' !== $extra && ! wpa_is_url( $extra ) ) {
365 $extra = "#$extra";
366 }
367 $extra_text = (string) stripslashes( get_option( 'asl_extra_text' ) );
368 $content = str_replace( '#', '', esc_attr( get_option( 'asl_content' ) ) );
369 $nav = str_replace( '#', '', esc_attr( get_option( 'asl_navigation' ) ) );
370 /**
371 * Customize the default value for sitemap skiplink. Turns on sitemap skiplink options in WP Accessibility versions > 1.9.0.
372 *
373 * @hook asl_sitemap
374 * @param string $settingValue to use as a default for the sitemap.
375 *
376 * @return string
377 */
378 $default_sitemap = apply_filters( 'asl_sitemap', '' );
379 $sitemap = esc_url( get_option( 'asl_sitemap', $default_sitemap ) );
380 $html .= ( '' !== $content ) ? "<a href=\"#$content\" class='no-scroll et_smooth_scroll_disabled'>" . __( 'Skip to Content', 'wp-accessibility' ) . '</a> ' : '';
381 $html .= ( '' !== $nav ) ? "<a href=\"#$nav\" class='no-scroll et_smooth_scroll_disabled'>" . __( 'Skip to navigation', 'wp-accessibility' ) . '</a> ' : '';
382 $html .= ( '' !== $sitemap ) ? "<a href=\"$sitemap\" class='no-scroll et_smooth_scroll_disabled'>" . __( 'Site map', 'wp-accessibility' ) . '</a> ' : '';
383 $html .= ( '' !== $extra && '' !== $extra_text ) ? "<a href=\"$extra\" class='no-scroll et_smooth_scroll_disabled'>$extra_text</a> " : '';
384 $is_rtl = ( is_rtl() ) ? '-rtl' : '-ltr';
385 $skiplinks = __( 'Skip links', 'wp-accessibility' );
386 $output = ( '' !== $html ) ? "<div class=\"$visibility$is_rtl\" id=\"skiplinks\" role=\"navigation\" aria-label=\"" . esc_attr( $skiplinks ) . "\">$html</div>" : '';
387 }
388
389 $labels = array(
390 's' => _x( 'Search', 'Search field label', 'wp-accessibility' ),
391 'author' => _x( 'Name', 'Commenter name label', 'wp-accessibility' ),
392 'email' => _x( 'Email', 'Commenter email label', 'wp-accessibility' ),
393 'url' => _x( 'Website', 'Commenter website label', 'wp-accessibility' ),
394 'comment' => _x( 'Comment', 'Commenter message label', 'wp-accessibility' ),
395 );
396 /**
397 * Customize labels passed to automatically label core WordPress fields.
398 *
399 * @hook wpa_labels
400 * @param array $labels Array of labels for search and comment fields.
401 *
402 * @return array
403 */
404 $labels = apply_filters( 'wpa_labels', $labels );
405 $dir = '';
406 $lang = '';
407 if ( 'off' !== get_option( 'wpa_lang_attributes' ) && ! wpa_accessible_theme() ) {
408 $dir = ( is_rtl() ) ? 'rtl' : 'ltr';
409 $lang = get_bloginfo( 'language' );
410 }
411
412 if ( SCRIPT_DEBUG ) {
413 $wpajs = plugins_url( 'js/wp-accessibility.js', __FILE__ );
414 } else {
415 $wpajs = plugins_url( 'js/wp-accessibility.min.js', __FILE__ );
416 }
417 wp_enqueue_script(
418 'wp-accessibility',
419 $wpajs,
420 array(),
421 $version,
422 array(
423 'in_footer' => true,
424 'strategy' => 'defer',
425 )
426 );
427
428 $longdesc_type = false;
429 if ( 'link' === get_option( 'wpa_longdesc' ) ) {
430 $longdesc_type = 'link';
431 } elseif ( 'jquery' === get_option( 'wpa_longdesc' ) || 'button' === get_option( 'wpa_longdesc' ) ) {
432 $longdesc_type = 'button';
433 }
434 /**
435 * Modify the selector used to attach the alt attribute toggle button on images. Default `.hentry img[alt!=""], .comment-content img[alt!=""]`.
436 *
437 * @hook wpa_show_alt_selector
438 *
439 * @since 2.0.0
440 *
441 * @param string $selector Valid CSS selector string.
442 *
443 * @return string
444 */
445 $selector = apply_filters( 'wpa_show_alt_selector', '.hentry img[alt]:not([alt=""]), .comment-content img[alt]:not([alt=""]), #content img[alt]:not([alt=""]),.entry-content img[alt]:not([alt=""])' );
446 $alttext = ( 'on' === get_option( 'wpa_show_alt' ) ) ? true : false;
447
448 /**
449 * Filter target element selector for underlines. Default `a`.
450 *
451 * @hook wpa_underline_target
452 *
453 * @param string $el Target element selector.
454 *
455 * @return string
456 */
457 $target = apply_filters( 'wpa_underline_target', 'a' );
458 /**
459 * Filter whether console log messages about remediation actions will be sent.
460 *
461 * @hook wpa_view_remediation_logs
462 *
463 * @param bool $visible Default `true` if user is logged in and has capabilities to manage options.
464 *
465 * @return bool
466 */
467 $errors_enabled = apply_filters( 'wpa_view_remediation_logs', current_user_can( 'manage_options' ) );
468 $track = ( '' === get_option( 'wpa_track_stats', '' ) ) ? current_user_can( 'manage_options' ) : true;
469 $track = ( 'off' === get_option( 'wpa_track_stats' ) ) ? false : $track;
470 /**
471 * Filter whether data from views will be tracked.
472 *
473 * @hook wpa_track_view_statistics
474 *
475 * @param bool $visible Default `true` if user is logged in and has capabilities to manage options or if enabled in settings.
476 *
477 * @return bool
478 */
479 $tracking_enabled = apply_filters( 'wpa_track_view_statistics', $track );
480 $apply_labels = ( 'off' === get_option( 'wpa_labels' ) ) ? false : true;
481 /**
482 * Filter whether automatic labeling is enabled.
483 *
484 * @hook wpa_disable_labels
485 *
486 * @param bool $enabled True if labels are automatically added.
487 *
488 * @return bool
489 */
490 $apply_labels = apply_filters( 'wpa_disable_labels', $apply_labels );
491 $remove_titles = ( 'off' === get_option( 'wpa_remove_titles' ) ) ? false : true;
492 /**
493 * Filter whether title attributes are removed. Used to be image titles only, now applies buttons and links, as well.
494 *
495 * @hook wpa_remove_titles
496 *
497 * @param bool $enabled True if title attributes are removed.
498 *
499 * @return bool
500 */
501 $remove_titles = apply_filters( 'wpa_remove_titles', $remove_titles );
502 $set_viewport = ( 'off' === get_option( 'wpa_viewport' ) ) ? false : true;
503 /**
504 * Filter whether WP Accessibility manipulates the viewport.
505 *
506 * @hook wpa_viewport
507 *
508 * @param bool $enabled True viewport can be modified.
509 *
510 * @return bool
511 */
512 $viewport = apply_filters( 'wpa_viewport', $set_viewport );
513 wp_localize_script(
514 'wp-accessibility',
515 'wpa',
516 array(
517 'skiplinks' => array(
518 'enabled' => ( 'on' === get_option( 'asl_enable' ) ) ? true : false,
519 'output' => $output,
520 ),
521 'target' => ( 'on' === get_option( 'wpa_target' ) ) ? true : false,
522 'tabindex' => ( 'on' === get_option( 'wpa_tabindex' ) ) ? true : false,
523 'underline' => array(
524 'enabled' => ( 'on' === get_option( 'wpa_underline' ) ) ? true : false,
525 'target' => $target,
526 ),
527 'videos' => ( 'on' === get_option( 'wpa_videos' ) ) ? true : false,
528 'dir' => $dir,
529 'viewport' => $viewport,
530 'lang' => $lang,
531 'titles' => $remove_titles,
532 'labels' => $apply_labels,
533 'wpalabels' => $labels,
534 'alt' => $alttext,
535 'altSelector' => $selector,
536 'current' => ( version_compare( $GLOBALS['wp_version'], '5.3', '<' ) ) ? true : false,
537 'errors' => ( $errors_enabled ) ? true : false,
538 'tracking' => ( $tracking_enabled ) ? true : false,
539 'ajaxurl' => admin_url( 'admin-ajax.php' ),
540 'security' => wp_create_nonce( 'wpa-stats-action' ),
541 'action' => 'wpa_stats_action',
542 'url' => ( function_exists( 'wpa_get_current_url' ) ) ? wpa_get_current_url() : 'disabled',
543 'post_id' => ( is_singular() ) ? get_the_ID() : '',
544 'continue' => ( wp_is_block_theme() ) ? true : false,
545 'pause' => __( 'Pause video', 'wp-accessibility' ),
546 'play' => __( 'Play video', 'wp-accessibility' ),
547 'restUrl' => get_rest_url( null, 'wp/v2/media' ),
548 'ldType' => $longdesc_type,
549 'ldHome' => home_url(),
550 'ldText' => '<span class="dashicons dashicons-media-text" aria-hidden="true"></span><span class="screen-reader">' . __( 'Long Description', 'wp-accessibility' ) . '</span>',
551 )
552 );
553 }
554
555 // courtesy of Graham Armfield (modified).
556 add_action( 'admin_bar_menu', 'wpa_logout_item', 11 );
557 /**
558 * Add adminbar menu logout.
559 *
560 * @link http://www.coolfields.co.uk/2013/02/wordpress-permanently-visible-log-out-link-plugin-version-0-1/
561 * @param object $admin_bar Admin bar object.
562 */
563 function wpa_logout_item( $admin_bar ) {
564 if ( ! is_user_logged_in() || 'on' === get_option( 'wpa_disable_logout', 'true' ) ) {
565 return;
566 }
567
568 $args = array(
569 'id' => 'wpa-logout',
570 'title' => __( 'Log Out', 'wp-accessibility' ),
571 'href' => wp_logout_url(),
572 );
573 $admin_bar->add_node( $args );
574 }
575
576 add_filter( 'posts_clauses', 'wpa_search_attachment_alt', 20, 2 );
577 /**
578 * Allow users to search alt attributes in the media library.
579 *
580 * @param array $clauses WordPress post query clauses.
581 * @param object $query WordPress query object.
582 *
583 * @return array
584 */
585 function wpa_search_attachment_alt( $clauses, $query ) {
586 if ( is_admin() && 'on' === get_option( 'wpa_search_alt' ) ) {
587 global $wpdb;
588 if ( isset( $query->query['post_type'] ) && 'attachment' === $query->query['post_type'] && '' !== $query->query_vars['s'] ) {
589 $clauses['join'] = " LEFT JOIN {$wpdb->postmeta} AS sq1 ON ( {$wpdb->posts}.ID = sq1.post_id AND ( sq1.meta_key = '_wp_attached_file' OR sq1.meta_key = '_wp_attachment_image_alt' ) )";
590 }
591 }
592
593 return $clauses;
594 }
595
596 add_filter( 'mce_css', 'wpa_diagnostic_css' );
597 /**
598 * Add diagnostic CSS.
599 *
600 * @param string $mce_css Existing CSS.
601 *
602 * @return string Full string CSS.
603 */
604 function wpa_diagnostic_css( $mce_css ) {
605 if ( get_option( 'wpa_diagnostics' ) === 'on' ) {
606 $mce_css .= ', ' . plugins_url( 'css/diagnostic.css', __FILE__ );
607 }
608
609 return $mce_css;
610 }
611
612 if ( 'on' === get_option( 'wpa_search' ) ) {
613 add_filter( 'pre_get_posts', 'wpa_filter' );
614 }
615
616 /**
617 * Filter search queries to ensure that an error page is returned if no results.
618 *
619 * @param object $query Main WP_Query object.
620 *
621 * @return $query.
622 */
623 function wpa_filter( $query ) {
624 if ( ! is_admin() ) {
625 if ( isset( $_GET['s'] ) && '' === trim( $_GET['s'] ) && ( $query->is_main_query() ) ) {
626 $query->query_vars['s'] = '&#32;';
627 $query->set( 'is_search', 1 );
628 add_action( 'template_include', 'wpa_search_error' );
629 add_filter(
630 'get_search_query',
631 function ( $search_query ) {
632 if ( '&#32;' === $search_query ) {
633 return '';
634 }
635 return $search_query;
636 }
637 );
638 }
639 }
640
641 return $query;
642 }
643
644 /**
645 * Locate template for the search error page.
646 *
647 * @param string $template Current template name.
648 *
649 * @return string New template name if changed.
650 */
651 function wpa_search_error( $template ) {
652 $search = locate_template( 'search.php' );
653 if ( $search ) {
654 return $search;
655 }
656
657 return $template;
658 }
659
660 if ( 'on' === get_option( 'wpa_more' ) && ! wpa_accessible_theme() ) {
661 add_filter(
662 'body_class',
663 function ( $classes ) {
664 return array_merge( $classes, array( 'wpa-excerpt' ) );
665 }
666 );
667 add_filter( 'get_the_excerpt', 'wpa_custom_excerpt_more', 100 );
668 add_filter( 'excerpt_more', 'wpa_excerpt_more', 100 );
669 add_filter( 'the_content_more_link', 'wpa_content_more', 100 );
670 }
671
672 /**
673 * Custom "Continue Reading" with post title context.
674 *
675 * @param int $id Post ID.
676 *
677 * @return string HTML link & text.
678 */
679 function wpa_continue_reading( $id ) {
680 return '<a class="continue" href="' . esc_url( get_permalink( $id ) ) . '">' . get_option( 'wpa_continue' ) . '<span> ' . get_the_title( $id ) . '</span></a>';
681 }
682
683 /**
684 * Add custom continue reading text to excerpts.
685 *
686 * @return string Ellipsis + continue reading text.
687 */
688 function wpa_excerpt_more() {
689 global $id;
690
691 return '&hellip; ' . wpa_continue_reading( $id );
692 }
693
694 /**
695 * Add custom continue reading text to content.
696 *
697 * @return string Continue reading text.
698 */
699 function wpa_content_more() {
700 global $id;
701
702 return wpa_continue_reading( $id );
703 }
704
705 /**
706 * Add custom continue reading text to custom excerpts.
707 *
708 * @param string $output Existing content.
709 *
710 * @return string Continue reading text.
711 */
712 function wpa_custom_excerpt_more( $output ) {
713 if ( has_excerpt() && ! is_attachment() ) {
714 global $id;
715 $output .= ' ' . wpa_continue_reading( $id ); // insert a blank space.
716 }
717
718 return $output;
719 }
720
721 /**
722 * Create support form.
723 */
724 function wpa_get_support_form() {
725 global $current_user;
726 $current_user = wp_get_current_user();
727 $request = '';
728 $version = wpa_check_version();
729 // send fields for all plugins.
730 $wp_version = get_bloginfo( 'version' );
731 $home_url = home_url();
732 $wp_url = site_url();
733 $language = get_bloginfo( 'language' );
734 $charset = get_bloginfo( 'charset' );
735 // server.
736 $php_version = phpversion();
737
738 // theme data.
739 $theme = wp_get_theme();
740 $theme_name = $theme->get( 'Name' );
741 $theme_uri = $theme->get( 'ThemeURI' );
742 $theme_parent = $theme->get( 'Template' );
743 $theme_parent = ( $theme_parent ) ? $theme_parent : __( 'None', 'wp-accessibility' );
744 $theme_version = $theme->get( 'Version' );
745
746 // plugin data.
747 $plugins = get_plugins();
748 $plugins_string = '';
749 foreach ( array_keys( $plugins ) as $key ) {
750 if ( is_plugin_active( $key ) ) {
751 $plugin =& $plugins[ $key ];
752 $plugin_name = $plugin['Name'];
753 $plugin_uri = $plugin['PluginURI'];
754 $plugin_version = $plugin['Version'];
755 $plugins_string .= "$plugin_name: $plugin_version; $plugin_uri\n";
756 }
757 }
758 $data = "
759 ================ Installation Data ====================
760 ==WP Accessibility==
761 Version: $version
762
763 ==WordPress:==
764 Version: $wp_version
765 URL: $home_url
766 Install: $wp_url
767 Language: $language
768 Charset: $charset
769 Admin Email: $current_user->user_email
770
771 ==Extra info:==
772 PHP Version: $php_version
773 Server Software: $_SERVER[SERVER_SOFTWARE]
774 User Agent: $_SERVER[HTTP_USER_AGENT]
775
776 ==Theme:==
777 Name: $theme_name
778 URI: $theme_uri
779 Parent: $theme_parent
780 Version: $theme_version
781
782 ==Active Plugins:==
783 $plugins_string
784 ";
785 if ( isset( $_POST['wpa_support'] ) ) {
786 $nonce = $_REQUEST['_wpnonce'];
787 if ( ! wp_verify_nonce( $nonce, 'wpa-nonce' ) ) {
788 wp_die( 'WP Accessibility: Security check failed' );
789 }
790 $request = ( ! empty( $_POST['support_request'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['support_request'] ) ) : false;
791 $has_donated = ( 'on' === $_POST['has_donated'] ) ? 'Donor' : 'No donation';
792 $subject = "WP Accessibility support request. $has_donated";
793 $message = $request . "\n\n" . $data;
794 // Get the site domain and get rid of www. from pluggable.php.
795 $sitename = sanitize_text_field( strtolower( $_SERVER['SERVER_NAME'] ) );
796 if ( 'www.' === substr( $sitename, 0, 4 ) ) {
797 $sitename = substr( $sitename, 4 );
798 }
799 $from_email = 'wordpress@' . $sitename;
800 $from = "From: $current_user->display_name <$from_email>\r\nReply-to: $current_user->display_name <$current_user->user_email>\r\n";
801
802 if ( ! $request ) {
803 echo "<div class='message error'><p>" . esc_html__( 'Please describe your problem.', 'wp-accessibility' ) . '</p></div>';
804 } else {
805 wp_mail( 'plugins@joedolson.com', $subject, $message, $from );
806 if ( 'Donor' === $has_donated ) {
807 echo "<div class='message updated'><p>" . esc_html__( 'Thank you for supporting the continuing development of this plug-in! I\'ll get back to you as soon as I can.', 'wp-accessibility' ) . '</p></div>';
808 } else {
809 echo "<div class='message updated'><p>" . esc_html__( 'I cannot provide support, but will treat your request as a bug report, and will incorporate any permanent solutions I discover into the plug-in.', 'wp-accessibility' ) . '</p></div>';
810 }
811 }
812 }
813 $admin_url = admin_url( 'admin.php?page=wp-accessibility' );
814
815 echo "
816 <form method='post' action='" . esc_url( $admin_url ) . "'>
817 <div><input type='hidden' name='_wpnonce' value='" . wp_create_nonce( 'wpa-nonce' ) . "' /></div>
818 <div>";
819 echo '
820 <p>
821 <code>' . __( 'From:', 'wp-accessibility' ) . " \"$current_user->display_name\" &lt;$current_user->user_email&gt;</code>
822 </p>
823 <p>
824 <input type='checkbox' name='has_donated' id='has_donated' value='on' /> <label for='has_donated'>";
825 // Translators: Donation URL.
826 echo sprintf( __( 'I <a href="%s">made a donation</a> to help support this plugin', 'wp-accessibility' ), 'https://www.joedolson.com/donate/' ) . "</label>
827 </p>
828 <p>
829 <label for='support_request'>" . __( 'Support Request:', 'wp-accessibility' ) . "</label><br /><textarea name='support_request' required id='support_request' cols='80' rows='10' class='widefat'>" . esc_textarea( stripslashes( $request ) ) . "</textarea>
830 </p>
831 <p>
832 <input type='submit' value='" . __( 'Send Support Request', 'wp-accessibility' ) . "' name='wpa_support' class='button-primary' />
833 </p>
834 <p>" . __( 'The following additional information will be sent with your support request:', 'wp-accessibility' ) . "</p>
835 <div class='wpa_support'>
836 " . wpautop( $data ) . '
837 </div>
838 </div>
839 </form>';
840 }
841
842 /**
843 * Tests whether the current theme is labeled accessibility-ready
844 *
845 * @return boolean True if this theme has the tag 'accessibility-ready'.
846 */
847 function wpa_accessible_theme() {
848 // This is Oxygen Builder, and the active theme test is invalid.
849 if ( defined( 'CT_VERSION' ) ) {
850 return false;
851 }
852 $theme = wp_get_theme();
853 $tags = $theme->get( 'Tags' );
854 if ( is_array( $tags ) && in_array( 'accessibility-ready', $tags, true ) ) {
855 return true;
856 }
857 return false;
858 }
859
860 /**
861 * Disable infinite scrolling by default. Enable if turned on by the user.
862 */
863 function wpa_disable_infinite_scroll() {
864 $user_id = get_current_user_id();
865 $enabled = ( 'true' === get_user_option( 'infinite_scrolling', $user_id ) ) ? true : false;
866 if ( ! $enabled ) {
867 // Disable infinite scrolling.
868 add_filter( 'media_library_infinite_scrolling', '__return_false' );
869 }
870 }
871 add_action( 'admin_init', 'wpa_disable_infinite_scroll' );
872
873 /**
874 * Disable full screen block editor by default.
875 */
876 function wpa_disable_editor_fullscreen_by_default() {
877 if ( 'on' === get_option( 'wpa_disable_fullscreen' ) ) {
878 $script = "window.onload = function() { const isFullscreenMode = wp.data.select( 'core/edit-post' ).isFeatureActive( 'fullscreenMode' ); if ( isFullscreenMode ) { wp.data.dispatch( 'core/edit-post' ).toggleFeature( 'fullscreenMode' ); } }";
879 wp_add_inline_script( 'wp-blocks', $script );
880 }
881 }
882 add_action( 'enqueue_block_editor_assets', 'wpa_disable_editor_fullscreen_by_default' );
883
884 /**
885 * Disable file embeds by default.
886 *
887 * @param array $blockdata Array of block configuration meta data.
888 *
889 * @return array
890 */
891 function wpa_disable_file_embed_by_default( $blockdata ) {
892 if ( 'core/file' === $blockdata['name'] && 'on' === get_option( 'wpa_disable_file_embed' ) ) {
893 if ( isset( $blockdata['attributes']['displayPreview'] ) ) {
894 $blockdata['attributes']['displayPreview']['default'] = false;
895 }
896 }
897
898 return $blockdata;
899 }
900 add_filter( 'block_type_metadata', 'wpa_disable_file_embed_by_default' );
901
902 /**
903 * Remove the H1 heading from the headings block.
904 *
905 * @param array $args Block type arguments array.
906 * @param string $block_type Block type declaration.
907 *
908 * @return array
909 */
910 function wpa_remove_h1( $args, $block_type ) {
911 // The H1 is not allowed by default.
912 $allowed = get_option( 'wpa_allow_h1', '' );
913 if ( 'on' !== $allowed ) {
914 // Only proceed if the current block is a Heading.
915 if ( 'core/heading' !== $block_type ) {
916 return $args;
917 }
918
919 // Declare valid heading levels.
920 $args['attributes']['levelOptions']['default'] = array( 2, 3, 4, 5, 6 );
921 }
922 return $args;
923 }
924 add_filter( 'register_block_type_args', 'wpa_remove_h1', 10, 2 );
925
926 /**
927 * Insert content summary at top of article content.
928 *
929 * @param string $content Post content.
930 *
931 * @return string
932 */
933 function wpa_content_summary( $content ) {
934 if ( is_singular() && wpa_in_post_type( get_queried_object_id() ) ) {
935 $post_id = get_the_ID();
936 $summary = wpa_get_content_summary( $post_id );
937 if ( ! $summary ) {
938 return $content;
939 }
940 $content = $summary . $content;
941 }
942
943 return $content;
944 }
945 add_filter( 'the_content', 'wpa_content_summary' );
946
947 /**
948 * Get a simplified summary for content.
949 *
950 * @param int $post_id Post ID.
951 *
952 * @return string
953 */
954 function wpa_get_content_summary( $post_id ) {
955 $summary = trim( get_post_meta( $post_id, '_wpa_content_summary', true ) );
956 if ( ! $summary ) {
957 return '';
958 }
959 /**
960 * Filter the heading text for content summaries. Default `Summary`.
961 *
962 * @hook wpa_summary_heading
963 *
964 * @param string $heading Heading text.
965 * @param int $post_id Post ID.
966 *
967 * @return string
968 */
969 $heading = apply_filters( 'wpa_summary_heading', __( 'Summary', 'wp-accessibility' ), $post_id );
970 /**
971 * Filter the heading level for content summaries. Default `h2`.
972 *
973 * @hook wpa_summary_heading_level
974 *
975 * @param string $heading Element selector.
976 * @param int $post_id Post ID.
977 *
978 * @return string
979 */
980 $level = apply_filters( 'wpa_summary_heading_level', 'h2', $post_id );
981
982 $heading = "<$level>" . $heading . "</$level>";
983 $content = '<section class="wpa-content-summary" id="summary-' . absint( $post_id ) . '"><div class="wpa-content-summary-inner">' . $heading . wpautop( wp_kses_post( stripslashes( $summary ) ) ) . '</div></section>';
984
985 return $content;
986 }
987
988 /**
989 * Check whether a given post is in an allowed post type for content summaries.
990 *
991 * @param integer $post_id Post ID.
992 *
993 * @return boolean True if post is allowed, false otherwise.
994 */
995 function wpa_in_post_type( $post_id ) {
996 $settings = get_option( 'wpa_post_types', array() );
997 if ( is_array( $settings ) && ! empty( $settings ) ) {
998 $type = get_post_type( $post_id );
999 if ( in_array( $type, $settings, true ) ) {
1000 return true;
1001 }
1002 }
1003
1004 return false;
1005 }
1006
1007 add_action( 'admin_menu', 'wpa_add_outer_box' );
1008 /**
1009 * Add metabox for content summaries.
1010 */
1011 function wpa_add_outer_box() {
1012 $allowed = get_option( 'wpa_post_types', array() );
1013 if ( is_array( $allowed ) ) {
1014 foreach ( $allowed as $post_type ) {
1015 add_meta_box( 'wpa_content_summary_container', __( 'Content Summary', 'wp-accessibility' ), 'wpa_add_inner_box', $post_type, 'normal', 'high' );
1016 }
1017 }
1018 }
1019
1020 /**
1021 * Render content summary form.
1022 */
1023 function wpa_add_inner_box() {
1024 global $post;
1025 $summary = get_post_meta( $post->ID, '_wpa_content_summary', true );
1026 $nonce = wp_nonce_field( 'wpa-nonce-field', 'wpa_nonce_name', true, false );
1027 echo $nonce;
1028 ?>
1029 <p class='wpa-content-summary-field'>
1030 <label for="wpa_content_summary"><?php _e( 'Simple Content Summary', 'wp-accessibility' ); ?></label><br/>
1031 <textarea class="wpa-content-summary widefat" name="wpa_content_summary" id="wpa_content_summary" rows="4" cols="60" aria-describedy="content-summary-description"><?php echo esc_textarea( $summary ); ?></textarea>
1032 <span id="content-summary-description"><?php _e( 'Provide a simplified summary to aid comprehension of complex content.', 'wp-accessibility' ); ?></span>
1033 </p>
1034 <?php
1035 }
1036
1037 /**
1038 * Save content summary from post meta box.
1039 *
1040 * @param int $id Post ID.
1041 * @param object $post Post.
1042 *
1043 * @return int
1044 */
1045 function wpa_save_content_summary( $id, $post ) {
1046 if ( empty( $_POST ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $id ) || isset( $_POST['_inline_edit'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ! wpa_in_post_type( $id ) ) {
1047 return $id;
1048 }
1049
1050 // verify this came from our screen and with proper authorization.
1051 // because save_post can be triggered at other times.
1052 if ( isset( $_POST['wpa_nonce_name'] ) ) {
1053 if ( ! wp_verify_nonce( $_POST['wpa_nonce_name'], 'wpa-nonce-field' ) ) {
1054 return $id;
1055 }
1056 $summary = isset( $_POST['wpa_content_summary'] ) ? wp_kses_post( $_POST['wpa_content_summary'] ) : '';
1057 update_post_meta( $id, '_wpa_content_summary', $summary );
1058 }
1059
1060 return $id;
1061 }
1062 add_action( 'save_post', 'wpa_save_content_summary', 10, 2 );
1063