PluginProbe
WP Accessibility / 2.2.5
WP Accessibility v2.2.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.2.5, at wp-accessibility.php

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