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() ) { ?> [ '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[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 ); } }