'', 'margin_top' => 0, 'auto_header_offset' => true, 'margin_bottom' => 0, 'stack_gap' => 16, 'stop_selectors' => '', 'auto_stop_footer' => true, 'devices' => array( 'desktop' => true, 'tablet' => true, 'mobile' => false, ), 'tablet_breakpoint' => 1024, 'mobile_breakpoint' => 768, 'min_viewport_width' => 0, 'min_viewport_height' => 0, 'z_index' => 99, ); } /** * Current settings merged with defaults. * * @return array */ public static function get() { $saved = get_option( self::OPTION, array() ); if ( ! is_array( $saved ) ) { $saved = array(); } $settings = array_merge( self::defaults(), $saved ); $settings['devices'] = array_merge( self::defaults()['devices'], (array) $settings['devices'] ); return $settings; } /** * Save settings (sanitized). * * @param array $settings Settings to save. * * @return array Saved settings. */ public static function update( array $settings ) { $clean = self::sanitize( $settings ); update_option( self::OPTION, $clean ); return $clean; } /** * Register the option so it can be read and saved via /wp/v2/settings. */ public static function register() { register_setting( 'stayrail', self::OPTION, array( 'type' => 'object', 'default' => self::defaults(), 'sanitize_callback' => array( __CLASS__, 'sanitize' ), 'show_in_rest' => array( 'schema' => self::schema(), ), ) ); } /** * REST schema for the settings object. * * @return array */ public static function schema() { return array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'custom_selectors' => array( 'type' => 'string' ), 'margin_top' => array( 'type' => 'integer' ), 'auto_header_offset' => array( 'type' => 'boolean' ), 'margin_bottom' => array( 'type' => 'integer' ), 'stack_gap' => array( 'type' => 'integer' ), 'stop_selectors' => array( 'type' => 'string' ), 'auto_stop_footer' => array( 'type' => 'boolean' ), 'devices' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'desktop' => array( 'type' => 'boolean' ), 'tablet' => array( 'type' => 'boolean' ), 'mobile' => array( 'type' => 'boolean' ), ), ), 'tablet_breakpoint' => array( 'type' => 'integer' ), 'mobile_breakpoint' => array( 'type' => 'integer' ), 'min_viewport_width' => array( 'type' => 'integer' ), 'min_viewport_height' => array( 'type' => 'integer' ), 'z_index' => array( 'type' => 'integer' ), ), ); } /** * Sanitize a settings array. * * @param mixed $input Raw input. * * @return array */ public static function sanitize( $input ) { $defaults = self::defaults(); $input = is_array( $input ) ? $input : array(); $out = array(); $out['custom_selectors'] = self::sanitize_selectors( isset( $input['custom_selectors'] ) ? $input['custom_selectors'] : '' ); $out['stop_selectors'] = self::sanitize_selectors( isset( $input['stop_selectors'] ) ? $input['stop_selectors'] : '' ); $ints = array( 'margin_top' => array( 0, 1000 ), 'margin_bottom' => array( 0, 2000 ), 'stack_gap' => array( 0, 500 ), 'tablet_breakpoint' => array( 320, 4000 ), 'mobile_breakpoint' => array( 240, 4000 ), 'min_viewport_width' => array( 0, 4000 ), 'min_viewport_height' => array( 0, 4000 ), 'z_index' => array( 0, 2147483647 ), ); foreach ( $ints as $key => $range ) { $value = isset( $input[ $key ] ) ? (int) $input[ $key ] : (int) $defaults[ $key ]; $out[ $key ] = max( $range[0], min( $range[1], $value ) ); } if ( $out['mobile_breakpoint'] >= $out['tablet_breakpoint'] ) { $out['mobile_breakpoint'] = max( 240, $out['tablet_breakpoint'] - 1 ); } foreach ( array( 'auto_header_offset', 'auto_stop_footer' ) as $key ) { $out[ $key ] = isset( $input[ $key ] ) ? rest_sanitize_boolean( $input[ $key ] ) : (bool) $defaults[ $key ]; } $devices = isset( $input['devices'] ) && is_array( $input['devices'] ) ? $input['devices'] : array(); $out['devices'] = array(); foreach ( $defaults['devices'] as $device => $default ) { $out['devices'][ $device ] = isset( $devices[ $device ] ) ? rest_sanitize_boolean( $devices[ $device ] ) : (bool) $default; } return $out; } /** * Keep one CSS selector per line and strip anything that can't be part of a selector list. * * @param mixed $value Raw textarea value. * * @return string */ public static function sanitize_selectors( $value ) { $value = sanitize_textarea_field( (string) $value ); $lines = preg_split( '/\r\n|\r|\n/', $value ); $clean = array(); foreach ( $lines as $line ) { // "{", "}", ";" and "<" never belong in a selector list. ">" is kept: it is the child combinator. $line = trim( str_replace( array( '{', '}', ';', '<' ), '', $line ) ); if ( '' !== $line ) { $clean[] = $line; } } return implode( "\n", array_unique( $clean ) ); } /** * Selector textarea value as an array. * * @param string $value Newline-separated selectors. * * @return string[] */ public static function lines( $value ) { return array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', (string) $value ) ) ) ); } }