false, 'fix_focus_visible' => false, 'fix_viewport_zoom' => false, 'fix_link_underline' => false, 'fix_iframe_titles' => false, 'fix_scrollable_focus' => false, 'fix_landmark_labels' => false, 'fix_image_alt' => false, 'fix_form_labels' => false, 'fix_lang_attribute' => false, // Repair configuration. 'skip_link_target' => '#content', 'skip_link_text' => '', 'focus_ring_color' => '#0b63ce', 'focus_ring_width' => 3, // Statement page. 'statement_page_id' => 0, // Housekeeping. 'delete_data_on_uninstall' => false, ); } /** * All settings, merged over the defaults. * * @return array */ public static function all() { $stored = get_option( self::OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } return wp_parse_args( $stored, self::defaults() ); } /** * One setting. * * @param string $key Setting name. * @param mixed $default Fallback when the key is unknown. * @return mixed */ public static function get( $key, $default = null ) { $all = self::all(); if ( array_key_exists( $key, $all ) ) { return $all[ $key ]; } return $default; } /** * Whether a repair is switched on. * * @param string $key Setting name. * @return bool */ public static function enabled( $key ) { return (bool) self::get( $key, false ); } /** * Persist a full settings array, sanitising every value. * * @param array $input Raw input. * @return array The saved settings. */ public static function save( array $input ) { $defaults = self::defaults(); $clean = array(); foreach ( $defaults as $key => $default ) { if ( is_bool( $default ) ) { $clean[ $key ] = ! empty( $input[ $key ] ); continue; } if ( ! isset( $input[ $key ] ) ) { $clean[ $key ] = $default; continue; } switch ( $key ) { case 'skip_link_target': $clean[ $key ] = self::sanitize_fragment( $input[ $key ], $default ); break; case 'focus_ring_color': $color = sanitize_hex_color( $input[ $key ] ); $clean[ $key ] = $color ? $color : $default; break; case 'focus_ring_width': $clean[ $key ] = max( 1, min( 6, absint( $input[ $key ] ) ) ); break; case 'statement_page_id': $clean[ $key ] = absint( $input[ $key ] ); break; default: $clean[ $key ] = sanitize_text_field( $input[ $key ] ); break; } } update_option( self::OPTION, $clean ); return $clean; } /** * Update a single setting without disturbing the rest. * * @param string $key Setting name. * @param mixed $value New value. * @return void */ public static function set( $key, $value ) { $all = self::all(); $all[ $key ] = $value; update_option( self::OPTION, $all ); } /** * Write defaults on activation without clobbering an existing configuration. * * @return void */ public static function seed_defaults() { if ( false === get_option( self::OPTION, false ) ) { add_option( self::OPTION, self::defaults() ); } } /** * Keep the skip-link target to a safe CSS id fragment. * * The value is printed into an href, so anything that is not a plain * "#identifier" is rejected rather than escaped — there is no legitimate * reason for it to contain anything else. * * @param string $value Raw value. * @param string $fallback Value to use when the input is unusable. * @return string */ public static function sanitize_fragment( $value, $fallback = '#content' ) { $value = ltrim( trim( (string) $value ), '#' ); $value = preg_replace( '/[^A-Za-z0-9_\-]/', '', $value ); if ( '' === $value ) { return $fallback; } return '#' . $value; } }