-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathFixesManager.php
More file actions
391 lines (350 loc) · 10.7 KB
/
Copy pathFixesManager.php
File metadata and controls
391 lines (350 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
<?php
/**
* Manager class for fixes.
*
* @package Accessibility_Checker
*/
namespace EqualizeDigital\AccessibilityChecker\Fixes;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddFileSizeAndTypeToLinkedFilesFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddLabelToUnlabelledFormFieldsFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddMissingOrEmptyPageTitleFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddNewWindowWarningFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\BlockPDFUploadsFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\CommentSearchLabelFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\EmptySearchFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\HTMLLangAndDirFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\RemoveTitleIfPrefferedAccessibleNameFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\PreventLinksOpeningNewWindowFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\SkipLinkFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\TabindexFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\LinkUnderline;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\MetaViewportScalableFix;
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\FocusOutlineFix;
use EqualizeDigital\AccessibilityChecker\Admin\AdminPage\FixesPage;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Manager class for fixes.
*
* @since 1.16.0
*/
class FixesManager {
/**
* The single instance of the class.
*
* @var FixesManager|null
*/
private static $instance = null;
/**
* Whether the theme has the accessibility-ready tag.
*
* @var bool|null
*/
private static $theme_is_accessibility_ready = null;
/**
* The fixes.
*
* @var array
*/
private $fixes = [];
/**
* Private constructor to prevent direct instantiation.
*/
private function __construct() {
$this->maybe_enqueue_frontend_scripts();
$this->maybe_enqueue_thickbox();
self::$theme_is_accessibility_ready = self::is_theme_accessibility_ready();
}
/**
* Maybe enqueue the thickbox script.
*
* This powers the modal that is used to display fix settings in the editor.
*/
public function maybe_enqueue_thickbox() {
add_action(
'admin_enqueue_scripts',
function () {
add_thickbox();
}
);
}
/**
* Get the single instance of the class.
*
* @return FixesManager
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Maybe enqueue the frontend scripts.
*/
private function maybe_enqueue_frontend_scripts() {
if (
( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
( defined( 'DOING_CRON' ) && DOING_CRON ) ||
( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
) {
return;
}
// Consider adding this only if we can determine at least 1 of the fixes are enabled.
add_action(
'wp_enqueue_scripts',
function () {
wp_enqueue_script( 'edac-frontend-fixes', EDAC_PLUGIN_URL . 'build/frontendFixes.bundle.js', [], EDAC_VERSION, true );
wp_set_script_translations( 'edac-frontend-fixes', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' );
wp_localize_script(
'edac-frontend-fixes',
'edac_frontend_fixes',
apply_filters( 'edac_filter_frontend_fixes_data', [] )
);
do_action( 'edac_action_enqueue_frontend_fixes' );
}
);
}
/**
* Load the fixes.
*/
private function load_fixes() {
$fixes = apply_filters(
'edac_filter_fixes',
[
SkipLinkFix::class,
CommentSearchLabelFix::class,
HTMLLangAndDirFix::class,
TabindexFix::class,
RemoveTitleIfPrefferedAccessibleNameFix::class,
LinkUnderline::class,
MetaViewportScalableFix::class,
PreventLinksOpeningNewWindowFix::class,
FocusOutlineFix::class,
BlockPDFUploadsFix::class,
AddFileSizeAndTypeToLinkedFilesFix::class,
AddMissingOrEmptyPageTitleFix::class,
AddLabelToUnlabelledFormFieldsFix::class,
AddNewWindowWarningFix::class,
EmptySearchFix::class,
]
);
if ( ! is_array( $fixes ) ) {
$fixes = is_string( $fixes ) ? [ $fixes ] : [];
}
foreach ( $fixes as $fix ) {
if ( ! is_string( $fix ) ) {
continue;
}
if ( is_subclass_of( $fix, '\EqualizeDigital\AccessibilityChecker\Fixes\FixInterface' ) ) {
if ( ! isset( $this->fixes[ $fix::get_slug() ] ) ) {
$this->fixes[ $fix::get_slug() ] = ( new $fix() );
}
}
}
}
/**
* Get a fix by its slug.
*
* @param string $slug The fix slug.
*
* @return FixInterface|null
*/
public function get_fix( $slug ) {
return isset( $this->fixes[ $slug ] ) ? $this->fixes[ $slug ] : null;
}
/**
* Get the fixes settings.
*
* Returns an array of all the fix settings and their values along with a pro or not flag.
*
* @return array
*/
public function get_fixes_settings() {
$fixes_array = [];
foreach ( $this->fixes as $fix ) {
$fields = [];
foreach ( $fix->get_fields_array() as $field_slug => $field ) {
$fields[ $field_slug ] = get_option( $field_slug, $field['default'] ?? 0 );
}
$fixes_array[ $fix::get_slug() ] = [
'fields' => $fields,
'is_pro' => isset( $fix->is_pro ) ? $fix->is_pro : false,
];
}
return $fixes_array;
}
/**
* Register the fixes.
*/
public function register_fixes() {
$this->load_fixes();
foreach ( $this->fixes as $fix ) {
$fix->register();
$this->maybe_run_fix( $fix );
}
}
/**
* Maybe run a fix depending on current context.
*
* @param FixInterface $fix The fix to maybe run.
*/
public function maybe_run_fix( $fix ) {
if ( 'backend' === $fix::get_type() && is_admin() ) {
$fix->run();
} elseif ( 'frontend' === $fix::get_type() && ! is_admin() ) {
$fix->run();
} elseif ( 'everywhere' === $fix::get_type() ) {
$fix->run();
}
}
/**
* Check if the theme is accessibility ready.
*
* True if the theme has the tag, false otherwise.
*
* @return bool
*/
public static function is_theme_accessibility_ready() {
if ( null !== self::$theme_is_accessibility_ready ) {
return self::$theme_is_accessibility_ready;
}
$theme = wp_get_theme();
$tags = $theme->get( 'Tags' );
self::$theme_is_accessibility_ready = is_array( $tags ) && in_array( 'accessibility-ready', $tags, true );
return self::$theme_is_accessibility_ready;
}
/**
* Maybe show a notice if the theme is accessibility-ready.
*/
public static function maybe_show_accessibility_ready_conflict_notice() {
if ( self::is_theme_accessibility_ready() ) {
?>
<span class="edac-notice--accessibility-ready-conflict">
<?php esc_html_e( 'Note: This setting is not recommended for themes that are already accessibility-ready.', 'accessibility-checker' ); ?>
</span>
<?php
}
}
/**
* Register the rest routes.
*
* @return void
*/
public function register_rest_routes() {
$namespaces = [
'accessibility-checker/v1',
'edac/v1', // Retained for backward compatibility while integrations migrate.
];
$routes = [
'/fixes' => [
'methods' => 'GET',
'callback' => [ $this, 'get_fixes_settings' ],
'permission_callback' => function () {
return current_user_can( apply_filters( 'edac_filter_settings_capability', 'manage_options' ) );
},
],
'/fixes/update' => [
'methods' => 'POST',
'callback' => [ $this, 'update_fix_settings' ],
'permission_callback' => function () {
return current_user_can( apply_filters( 'edac_filter_settings_capability', 'manage_options' ) );
},
],
'/fix-fields/(?P<slug>[a-zA-Z0-9_-]+)' => [
'methods' => 'GET',
'callback' => [ $this, 'get_fix_fields' ],
'args' => [
'slug' => [
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
],
],
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
],
];
foreach ( $namespaces as $namespace ) {
foreach ( $routes as $route => $args ) {
register_rest_route( $namespace, $route, $args );
}
}
}
/**
* Handle the request to set a fix.
*
* @param \WP_REST_Request $request The request recieved through a rest call.
*
* @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response
*/
public function update_fix_settings( $request ) {
// get body of the request.
$body = $request->get_json_params();
// loop through body and find fixes for those items.
foreach ( $body as $rule_slug => $settings ) {
$fix = $this->get_fix( $rule_slug );
if ( ! $fix ) {
return new \WP_Error( 'edac_fix_not_found', esc_html__( 'Fix not found', 'accessibility-checker' ), [ 'status' => 404 ] );
}
$fix_fields = $fix->get_fields_array();
foreach ( $settings as $setting => $value ) {
$sanitizer = isset( $fix_fields[ $setting ]['sanitize_callback'] ) ? $fix_fields[ $setting ]['sanitize_callback'] : [ FixesPage::class, 'sanitize_' . $fix_fields[ $setting ]['type'] ];
if ( ! $sanitizer || ! is_callable( $sanitizer ) ) {
// no sanitizer, do not save.
continue;
}
update_option( $setting, $sanitizer( $value ) );
}
}
return rest_ensure_response( [ 'success' => true ] );
}
/**
* Get fix fields and information for a specific fix.
*
* Returns the fix name, description, enabled status, and configuration fields.
*
* @param \WP_REST_Request $request The REST request.
*
* @return \WP_REST_Response|\WP_Error
*/
public function get_fix_fields( \WP_REST_Request $request ) {
$fix_slug = sanitize_text_field( $request['slug'] );
// Get the fix instance.
$fix = $this->get_fix( $fix_slug );
if ( ! $fix ) {
return new \WP_Error(
'fix_not_found',
__( 'Fix not found.', 'accessibility-checker' ),
[ 'status' => 404 ]
);
}
// Get the fix's fields array.
$fields = $fix->get_fields_array();
// Check if the fix is enabled.
$option_key = 'edac_fix_' . $fix_slug;
$enabled = (bool) get_option( $option_key, false );
// Build a simplified fields array for display in the modal.
$display_fields = [];
foreach ( $fields as $field_key => $field ) {
$display_fields[ $field_key ] = [
'label' => isset( $field['label'] ) ? wp_kses_post( $field['label'] ) : '',
'description' => isset( $field['description'] ) ? wp_kses_post( $field['description'] ) : '',
'type' => isset( $field['type'] ) ? sanitize_text_field( $field['type'] ) : 'text',
'value' => get_option( $field_key, $field['default'] ?? '' ),
];
}
return new \WP_REST_Response(
[
'success' => true,
'fix_slug' => $fix_slug,
'fix_name' => $fix::get_nicename(),
'enabled' => $enabled,
'fields' => $display_fields,
],
200
);
}
}