• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

equalizedigital / accessibility-checker / 22634711490

03 Mar 2026 05:22PM UTC coverage: 59.97% (-0.7%) from 60.629%
22634711490

push

github

web-flow
Merge pull request #1332 from equalizedigital/william/pro-526-setup-integration-branch-for-ac-sidebar

Integration branch: Sidebar Metabox

385 of 814 new or added lines in 10 files covered. (47.3%)

10 existing lines in 3 files now uncovered.

4773 of 7959 relevant lines covered (59.97%)

4.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.44
/includes/classes/Fixes/FixesManager.php
1
<?php
2
/**
3
 * Manager class for fixes.
4
 *
5
 * @package Accessibility_Checker
6
 */
7

8
namespace EqualizeDigital\AccessibilityChecker\Fixes;
9

10
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddFileSizeAndTypeToLinkedFilesFix;
11
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddLabelToUnlabelledFormFieldsFix;
12
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddMissingOrEmptyPageTitleFix;
13
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\AddNewWindowWarningFix;
14
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\BlockPDFUploadsFix;
15
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\CommentSearchLabelFix;
16
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\HTMLLangAndDirFix;
17
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\RemoveTitleIfPrefferedAccessibleNameFix;
18
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\PreventLinksOpeningNewWindowFix;
19
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\SkipLinkFix;
20
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\TabindexFix;
21
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\LinkUnderline;
22
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\MetaViewportScalableFix;
23
use EqualizeDigital\AccessibilityChecker\Fixes\Fix\FocusOutlineFix;
24
use EqualizeDigital\AccessibilityChecker\Admin\AdminPage\FixesPage;
25

26
if ( ! defined( 'ABSPATH' ) ) {
27
        exit;
28
}
29

30
/**
31
 * Manager class for fixes.
32
 *
33
 * @since 1.16.0
34
 */
35
class FixesManager {
36

37
        /**
38
         * The single instance of the class.
39
         *
40
         * @var FixesManager|null
41
         */
42
        private static $instance = null;
43

44
        /**
45
         * Whether the theme has the accessibility-ready tag.
46
         *
47
         * @var bool|null
48
         */
49
        private static $theme_is_accessibility_ready = null;
50

51
        /**
52
         * The fixes.
53
         *
54
         * @var array
55
         */
56
        private $fixes = [];
57

58
        /**
59
         * Private constructor to prevent direct instantiation.
60
         */
61
        private function __construct() {
62
                $this->maybe_enqueue_frontend_scripts();
8✔
63
                $this->maybe_enqueue_thickbox();
8✔
64

65
                self::$theme_is_accessibility_ready = self::is_theme_accessibility_ready();
8✔
66
        }
67

68
        /**
69
         * Maybe enqueue the thickbox script.
70
         *
71
         * This powers the modal that is used to display fix settings in the editor.
72
         */
73
        public function maybe_enqueue_thickbox() {
74
                add_action(
8✔
75
                        'admin_enqueue_scripts',
8✔
76
                        function () {
8✔
77
                                add_thickbox();
×
78
                        }
8✔
79
                );
8✔
80
        }
81

82
        /**
83
         * Get the single instance of the class.
84
         *
85
         * @return FixesManager
86
         */
87
        public static function get_instance() {
88
                if ( null === self::$instance ) {
8✔
89
                        self::$instance = new self();
8✔
90
                }
91
                return self::$instance;
8✔
92
        }
93

94
        /**
95
         * Maybe enqueue the frontend scripts.
96
         */
97
        private function maybe_enqueue_frontend_scripts() {
98

99
                if (
100
                        ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
8✔
101
                        ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
8✔
102
                        ( defined( 'DOING_CRON' ) && DOING_CRON ) ||
8✔
103
                        ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
8✔
104
                ) {
105
                        return;
×
106
                }
107

108
                // Consider adding this only if we can determine at least 1 of the fixes are enabled.
109
                add_action(
8✔
110
                        'wp_enqueue_scripts',
8✔
111
                        function () {
8✔
112
                                wp_enqueue_script( 'edac-frontend-fixes', EDAC_PLUGIN_URL . 'build/frontendFixes.bundle.js', [], EDAC_VERSION, true );
×
113
                                wp_set_script_translations( 'edac-frontend-fixes', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' );
×
114
                                wp_localize_script(
×
115
                                        'edac-frontend-fixes',
×
116
                                        'edac_frontend_fixes',
×
117
                                        apply_filters( 'edac_filter_frontend_fixes_data', [] )
×
118
                                );
×
119
                                do_action( 'edac_action_enqueue_frontend_fixes' );
×
120
                        }
8✔
121
                );
8✔
122
        }
123

124
        /**
125
         * Load the fixes.
126
         */
127
        private function load_fixes() {
128
                $fixes = apply_filters(
2✔
129
                        'edac_filter_fixes',
2✔
130
                        [
2✔
131
                                SkipLinkFix::class,
2✔
132
                                CommentSearchLabelFix::class,
2✔
133
                                HTMLLangAndDirFix::class,
2✔
134
                                TabindexFix::class,
2✔
135
                                RemoveTitleIfPrefferedAccessibleNameFix::class,
2✔
136
                                LinkUnderline::class,
2✔
137
                                MetaViewportScalableFix::class,
2✔
138
                                PreventLinksOpeningNewWindowFix::class,
2✔
139
                                FocusOutlineFix::class,
2✔
140
                                BlockPDFUploadsFix::class,
2✔
141
                                AddFileSizeAndTypeToLinkedFilesFix::class,
2✔
142
                                AddMissingOrEmptyPageTitleFix::class,
2✔
143
                                AddLabelToUnlabelledFormFieldsFix::class,
2✔
144
                                AddNewWindowWarningFix::class,
2✔
145
                        ]
2✔
146
                );
2✔
147
                if ( ! is_array( $fixes ) ) {
2✔
148
                        $fixes = is_string( $fixes ) ? [ $fixes ] : [];
2✔
149
                }
150
                foreach ( $fixes as $fix ) {
2✔
151
                        if ( ! is_string( $fix ) ) {
×
152
                                continue;
×
153
                        }
154
                        if ( is_subclass_of( $fix, '\EqualizeDigital\AccessibilityChecker\Fixes\FixInterface' ) ) {
×
155
                                if ( ! isset( $this->fixes[ $fix::get_slug() ] ) ) {
×
156
                                        $this->fixes[ $fix::get_slug() ] = ( new $fix() );
×
157
                                }
158
                        }
159
                }
160
        }
161

162
        /**
163
         * Get a fix by its slug.
164
         *
165
         * @param string $slug The fix slug.
166
         *
167
         * @return FixInterface|null
168
         */
169
        public function get_fix( $slug ) {
170
                return isset( $this->fixes[ $slug ] ) ? $this->fixes[ $slug ] : null;
×
171
        }
172

173
        /**
174
         * Get the fixes settings.
175
         *
176
         * Returns an array of all the fix settings and their values along with a pro or not flag.
177
         *
178
         * @return array
179
         */
180
        public function get_fixes_settings() {
181
                $fixes_array = [];
8✔
182
                foreach ( $this->fixes as $fix ) {
8✔
183

184
                        $fields = [];
4✔
185
                        foreach ( $fix->get_fields_array() as $field_slug => $field ) {
4✔
186
                                $fields[ $field_slug ] = get_option( $field_slug, $field['default'] ?? 0 );
4✔
187
                        }
188

189
                        $fixes_array[ $fix::get_slug() ] = [
4✔
190
                                'fields' => $fields,
4✔
191
                                'is_pro' => isset( $fix->is_pro ) ? $fix->is_pro : false,
4✔
192
                        ];
4✔
193
                }
194
                return $fixes_array;
8✔
195
        }
196

197
        /**
198
         * Register the fixes.
199
         */
200
        public function register_fixes() {
201
                $this->load_fixes();
2✔
202

203
                foreach ( $this->fixes as $fix ) {
2✔
204
                        $fix->register();
×
205
                        $this->maybe_run_fix( $fix );
×
206
                }
207
        }
208

209
        /**
210
         * Maybe run a fix depending on current context.
211
         *
212
         * @param FixInterface $fix The fix to maybe run.
213
         */
214
        public function maybe_run_fix( $fix ) {
215
                if ( 'backend' === $fix::get_type() && is_admin() ) {
×
216
                        $fix->run();
×
217
                } elseif ( 'frontend' === $fix::get_type() && ! is_admin() ) {
×
218
                        $fix->run();
×
219
                } elseif ( 'everywhere' === $fix::get_type() ) {
×
220
                        $fix->run();
×
221
                }
222
        }
223

224
        /**
225
         * Check if the theme is accessibility ready.
226
         *
227
         * True if the theme has the tag, false otherwise.
228
         *
229
         * @return bool
230
         */
231
        public static function is_theme_accessibility_ready() {
232
                if ( null !== self::$theme_is_accessibility_ready ) {
8✔
233
                        return self::$theme_is_accessibility_ready;
8✔
234
                }
235

236
                $theme = wp_get_theme();
×
237
                $tags  = $theme->get( 'Tags' );
×
238

239
                self::$theme_is_accessibility_ready = is_array( $tags ) && in_array( 'accessibility-ready', $tags, true );
×
240
                return self::$theme_is_accessibility_ready;
×
241
        }
242

243
        /**
244
         * Maybe show a notice if the theme is accessibility-ready.
245
         */
246
        public static function maybe_show_accessibility_ready_conflict_notice() {
247
                if ( self::is_theme_accessibility_ready() ) {
×
248
                        ?>
249
                        <span class="edac-notice--accessibility-ready-conflict">
×
250
                                <?php esc_html_e( 'Note: This setting is not recommended for themes that are already accessibility-ready.', 'accessibility-checker' ); ?>
×
251
                        </span>
×
252
                        <?php
×
253
                }
254
        }
255

256
        /**
257
         * Register the rest routes.
258
         *
259
         * @return void
260
         */
261
        public function register_rest_routes() {
262
                register_rest_route(
20✔
263
                        'edac/v1',
20✔
264
                        '/fixes',
20✔
265
                        [
20✔
266
                                'methods'             => 'GET',
20✔
267
                                'callback'            => [ $this, 'get_fixes' ],
20✔
268
                                'permission_callback' => function () {
20✔
269
                                        return current_user_can( apply_filters( 'edac_filter_settings_capability', 'manage_options' ) );
×
270
                                },
20✔
271
                        ]
20✔
272
                );
20✔
273

274
                register_rest_route(
20✔
275
                        'edac/v1',
20✔
276
                        '/fixes/update',
20✔
277
                        [
20✔
278
                                'methods'             => 'POST',
20✔
279
                                'callback'            => [ $this, 'update_fix_settings' ],
20✔
280
                                'permission_callback' => function () {
20✔
281
                                        return current_user_can( apply_filters( 'edac_filter_settings_capability', 'manage_options' ) );
×
282
                                },
20✔
283
                        ]
20✔
284
                );
20✔
285

286
                register_rest_route(
20✔
287
                        'edac/v1',
20✔
288
                        '/fix-fields/(?P<slug>[a-zA-Z0-9_-]+)',
20✔
289
                        [
20✔
290
                                'methods'             => 'GET',
20✔
291
                                'callback'            => [ $this, 'get_fix_fields' ],
20✔
292
                                'args'                => [
20✔
293
                                        'slug' => [
20✔
294
                                                'required'          => true,
20✔
295
                                                'sanitize_callback' => 'sanitize_text_field',
20✔
296
                                        ],
20✔
297
                                ],
20✔
298
                                'permission_callback' => function () {
20✔
NEW
299
                                        return current_user_can( 'edit_posts' );
×
300
                                },
20✔
301
                        ]
20✔
302
                );
20✔
303
        }
304

305
        /**
306
         * Handle the request to set a fix.
307
         *
308
         * @param \WP_REST_Request $request The request recieved through a rest call.
309
         *
310
         * @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response
311
         */
312
        public function update_fix_settings( $request ) {
313
                // get body of the request.
314
                $body = $request->get_json_params();
×
315

316
                // loop through body and find fixes for those items.
317
                foreach ( $body as $rule_slug => $settings ) {
×
NEW
318
                        $fix = $this->get_fix( $rule_slug );
×
319
                        if ( ! $fix ) {
×
320
                                return new \WP_Error( 'edac_fix_not_found', esc_html__( 'Fix not found', 'accessibility-checker' ), [ 'status' => 404 ] );
×
321
                        }
322

NEW
323
                        $fix_fields = $fix->get_fields_array();
×
324

325
                        foreach ( $settings as $setting => $value ) {
×
326
                                $sanitizer = isset( $fix_fields[ $setting ]['sanitize_callback'] ) ? $fix_fields[ $setting ]['sanitize_callback'] : [ FixesPage::class, 'sanitize_' . $fix_fields[ $setting ]['type'] ];
×
327
                                if ( ! $sanitizer || ! is_callable( $sanitizer ) ) {
×
328
                                        // no sanitizer, do not save.
329
                                        continue;
×
330
                                }
331
                                update_option( $setting, $sanitizer( $value ) );
×
332
                        }
333
                }
334

NEW
335
                return rest_ensure_response( [ 'success' => true ] );
×
336
        }
337

338
        /**
339
         * Get fix fields and information for a specific fix.
340
         *
341
         * Returns the fix name, description, enabled status, and configuration fields.
342
         *
343
         * @param \WP_REST_Request $request The REST request.
344
         *
345
         * @return \WP_REST_Response|\WP_Error
346
         */
347
        public function get_fix_fields( \WP_REST_Request $request ) {
NEW
348
                $fix_slug = sanitize_text_field( $request['slug'] );
×
349

350
                // Get the fix instance.
NEW
351
                $fix = $this->get_fix( $fix_slug );
×
352

NEW
353
                if ( ! $fix ) {
×
NEW
354
                        return new \WP_Error(
×
NEW
355
                                'fix_not_found',
×
NEW
356
                                __( 'Fix not found.', 'accessibility-checker' ),
×
NEW
357
                                [ 'status' => 404 ]
×
NEW
358
                        );
×
359
                }
360

361
                // Get the fix's fields array.
NEW
362
                $fields = $fix->get_fields_array();
×
363

364
                // Check if the fix is enabled.
NEW
365
                $option_key = 'edac_fix_' . $fix_slug;
×
NEW
366
                $enabled    = (bool) get_option( $option_key, false );
×
367

368
                // Build a simplified fields array for display in the modal.
NEW
369
                $display_fields = [];
×
NEW
370
                foreach ( $fields as $field_key => $field ) {
×
NEW
371
                        $display_fields[ $field_key ] = [
×
NEW
372
                                'label'       => isset( $field['label'] ) ? wp_kses_post( $field['label'] ) : '',
×
NEW
373
                                'description' => isset( $field['description'] ) ? wp_kses_post( $field['description'] ) : '',
×
NEW
374
                                'type'        => isset( $field['type'] ) ? sanitize_text_field( $field['type'] ) : 'text',
×
NEW
375
                                'value'       => get_option( $field_key, $field['default'] ?? '' ),
×
NEW
376
                        ];
×
377
                }
378

NEW
379
                return new \WP_REST_Response(
×
NEW
380
                        [
×
NEW
381
                                'success'  => true,
×
NEW
382
                                'fix_slug' => $fix_slug,
×
NEW
383
                                'fix_name' => $fix::get_nicename(),
×
NEW
384
                                'enabled'  => $enabled,
×
NEW
385
                                'fields'   => $display_fields,
×
NEW
386
                        ],
×
NEW
387
                        200
×
NEW
388
                );
×
389
        }
390
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc