|
9 | 9 | } |
10 | 10 |
|
11 | 11 | class Sanitize { |
| 12 | + |
12 | 13 | public static function options( array $values ) { |
| 14 | + $defaults = Options::defaults(); |
13 | 15 | $sanitized = array(); |
14 | 16 |
|
15 | | - // Existing fields |
16 | | - $sanitized['example_text'] = isset( $values['example_text'] ) ? sanitize_text_field( $values['example_text'] ) : ''; |
17 | | - $sanitized['enable_feature'] = isset( $values['enable_feature'] ) ? (bool) $values['enable_feature'] : false; |
18 | | - |
19 | | - // Dark mode fields |
20 | | - $sanitized['enabled'] = isset( $values['enabled'] ) ? (bool) $values['enabled'] : false; |
21 | | - |
22 | | - $sanitized['default_mode'] = isset( $values['default_mode'] ) |
23 | | - && in_array( $values['default_mode'], array( 'system', 'dark', 'light' ), true ) |
24 | | - ? $values['default_mode'] |
25 | | - : 'system'; |
26 | | - |
27 | | - $sanitized['show_toggle'] = isset( $values['show_toggle'] ) ? (bool) $values['show_toggle'] : true; |
28 | | - |
29 | | - $sanitized['toggle_position'] = isset( $values['toggle_position'] ) |
30 | | - && in_array( $values['toggle_position'], array( 'bottom-right', 'bottom-left' ), true ) |
31 | | - ? $values['toggle_position'] |
32 | | - : 'bottom-right'; |
33 | | - |
34 | | - |
35 | | - $sanitized['toggle_style'] = isset( $values['toggle_style'] ) |
36 | | - && in_array( $values['toggle_style'], array( 'classic', 'pill', 'minimal' ), true ) |
37 | | - ? $values['toggle_style'] |
38 | | - : 'classic'; |
| 17 | + // Booleans |
| 18 | + foreach ( array( 'enabled', 'show_toggle', 'transition_enabled', 'schedule_enabled', 'keyboard_enabled' ) as $key ) { |
| 19 | + $sanitized[ $key ] = isset( $values[ $key ] ) ? (bool) $values[ $key ] : $defaults[ $key ]; |
| 20 | + } |
39 | 21 |
|
40 | | - $sanitized['toggle_size'] = isset( $values['toggle_size'] ) |
41 | | - && in_array( $values['toggle_size'], array( 'xs', 's', 'm', 'l', 'xl' ), true ) |
42 | | - ? $values['toggle_size'] |
43 | | - : 'm'; |
44 | | - // Filter settings (relative values: -50 to +50 for brightness/contrast, 0-100 for sepia/grayscale) |
45 | | - $sanitized['brightness'] = isset( $values['brightness'] ) |
46 | | - ? max( -50, min( 50, (int) $values['brightness'] ) ) |
47 | | - : 0; |
| 22 | + // Enums |
| 23 | + $enums = array( |
| 24 | + 'default_mode' => array( 'system', 'dark', 'light' ), |
| 25 | + 'toggle_position' => array( 'bottom-right', 'bottom-left' ), |
| 26 | + 'toggle_style' => array( 'classic', 'pill', 'minimal' ), |
| 27 | + 'toggle_size' => array( 'xs', 's', 'm', 'l', 'xl' ), |
| 28 | + 'theme' => array( 'classic', 'cool', 'warm', 'high-contrast', 'pure-black', 'custom' ), |
| 29 | + ); |
48 | 30 |
|
49 | | - $sanitized['contrast'] = isset( $values['contrast'] ) |
50 | | - ? max( -50, min( 50, (int) $values['contrast'] ) ) |
51 | | - : 0; |
| 31 | + foreach ( $enums as $key => $allowed ) { |
| 32 | + $sanitized[ $key ] = isset( $values[ $key ] ) && in_array( $values[ $key ], $allowed, true ) |
| 33 | + ? $values[ $key ] |
| 34 | + : $defaults[ $key ]; |
| 35 | + } |
52 | 36 |
|
53 | | - $sanitized['sepia'] = isset( $values['sepia'] ) |
54 | | - ? max( 0, min( 100, (int) $values['sepia'] ) ) |
55 | | - : 0; |
| 37 | + // Clamped integers |
| 38 | + $ranges = array( |
| 39 | + 'brightness' => array( -50, 50 ), |
| 40 | + 'contrast' => array( -50, 50 ), |
| 41 | + 'sepia' => array( 0, 100 ), |
| 42 | + 'grayscale' => array( 0, 100 ), |
| 43 | + 'transition_duration' => array( 0, 1000 ), |
| 44 | + 'image_brightness' => array( 50, 150 ), |
| 45 | + 'video_brightness' => array( 50, 150 ), |
| 46 | + 'background_brightness' => array( 50, 150 ), |
| 47 | + ); |
56 | 48 |
|
57 | | - $sanitized['grayscale'] = isset( $values['grayscale'] ) |
58 | | - ? max( 0, min( 100, (int) $values['grayscale'] ) ) |
59 | | - : 0; |
| 49 | + foreach ( $ranges as $key => $range ) { |
| 50 | + $sanitized[ $key ] = isset( $values[ $key ] ) |
| 51 | + ? max( $range[0], min( $range[1], (int) $values[ $key ] ) ) |
| 52 | + : $defaults[ $key ]; |
| 53 | + } |
60 | 54 |
|
61 | | - // Sanitize exclude_selectors with strict pattern |
| 55 | + // Exclude selectors (CSS whitelist pattern) |
62 | 56 | $exclude = isset( $values['exclude_selectors'] ) ? trim( $values['exclude_selectors'] ) : ''; |
63 | 57 | if ( ! empty( $exclude ) ) { |
64 | | - // Match frontend pattern: /^[a-zA-Z0-9#.\-_\s,:>\[\]\(\)="'~+*\\/]*$/ |
65 | | - if ( preg_match( '/^[a-zA-Z0-9#.\-_\s,:>\[\]\(\)="\'~+*\\/]*$/', $exclude ) ) { |
66 | | - $sanitized['exclude_selectors'] = $exclude; |
67 | | - } else { |
| 58 | + if ( ! preg_match( '/^[a-zA-Z0-9#.\-_\s,:>\[\]\(\)="\'~+*\\/]*$/', $exclude ) ) { |
68 | 59 | return new WP_Error( |
69 | 60 | 'nightly_invalid_selectors', |
70 | 61 | __( 'Invalid CSS selectors provided.', 'nightly' ), |
71 | 62 | array( 'status' => 400 ) |
72 | 63 | ); |
73 | 64 | } |
| 65 | + $sanitized['exclude_selectors'] = $exclude; |
74 | 66 | } else { |
75 | 67 | $sanitized['exclude_selectors'] = ''; |
76 | 68 | } |
77 | 69 |
|
78 | | - // Transition settings |
79 | | - $sanitized['transition_enabled'] = isset( $values['transition_enabled'] ) ? (bool) $values['transition_enabled'] : true; |
80 | | - |
81 | | - $sanitized['transition_duration'] = isset( $values['transition_duration'] ) |
82 | | - ? max( 0, min( 1000, (int) $values['transition_duration'] ) ) |
83 | | - : 300; |
84 | | - |
85 | | - // Schedule settings |
86 | | - $sanitized['schedule_enabled'] = isset( $values['schedule_enabled'] ) ? (bool) $values['schedule_enabled'] : false; |
87 | | - |
88 | | - // Validate time format HH:MM |
89 | | - $schedule_start = isset( $values['schedule_start'] ) ? trim( $values['schedule_start'] ) : '20:00'; |
90 | | - if ( preg_match( '/^([01][0-9]|2[0-3]):([0-5][0-9])$/', $schedule_start ) ) { |
91 | | - $sanitized['schedule_start'] = $schedule_start; |
92 | | - } else { |
93 | | - $sanitized['schedule_start'] = '20:00'; |
94 | | - } |
95 | | - |
96 | | - $schedule_end = isset( $values['schedule_end'] ) ? trim( $values['schedule_end'] ) : '06:00'; |
97 | | - if ( preg_match( '/^([01][0-9]|2[0-3]):([0-5][0-9])$/', $schedule_end ) ) { |
98 | | - $sanitized['schedule_end'] = $schedule_end; |
99 | | - } else { |
100 | | - $sanitized['schedule_end'] = '06:00'; |
101 | | - } |
102 | | - |
103 | | - // Keyboard shortcut settings |
104 | | - $sanitized['keyboard_enabled'] = isset( $values['keyboard_enabled'] ) ? (bool) $values['keyboard_enabled'] : true; |
105 | | - |
106 | | - // Validate keyboard shortcut format (e.g., "Ctrl+Shift+D", "Meta+K", "Alt+D") |
107 | | - $keyboard_shortcut = isset( $values['keyboard_shortcut'] ) ? trim( $values['keyboard_shortcut'] ) : 'Ctrl+Shift+D'; |
108 | | - if ( preg_match( '/^(Ctrl|Meta|Cmd|Alt|Shift)(\+(Ctrl|Meta|Cmd|Alt|Shift))*\+[A-Za-z0-9]$/', $keyboard_shortcut ) ) { |
109 | | - $sanitized['keyboard_shortcut'] = $keyboard_shortcut; |
110 | | - } else { |
111 | | - $sanitized['keyboard_shortcut'] = 'Ctrl+Shift+D'; |
| 70 | + // Time fields (HH:MM format) |
| 71 | + $time_fields = array( 'schedule_start', 'schedule_end' ); |
| 72 | + foreach ( $time_fields as $key ) { |
| 73 | + $time = isset( $values[ $key ] ) ? trim( $values[ $key ] ) : $defaults[ $key ]; |
| 74 | + $sanitized[ $key ] = preg_match( '/^([01][0-9]|2[0-3]):[0-5][0-9]$/', $time ) |
| 75 | + ? $time |
| 76 | + : $defaults[ $key ]; |
112 | 77 | } |
113 | 78 |
|
114 | | - // Media brightness settings |
115 | | - $sanitized['image_brightness'] = isset( $values['image_brightness'] ) |
116 | | - ? max( 50, min( 150, (int) $values['image_brightness'] ) ) |
117 | | - : 100; |
118 | | - |
119 | | - $sanitized['video_brightness'] = isset( $values['video_brightness'] ) |
120 | | - ? max( 50, min( 150, (int) $values['video_brightness'] ) ) |
121 | | - : 100; |
| 79 | + // Keyboard shortcut (e.g., "Ctrl+Shift+D") |
| 80 | + $shortcut = isset( $values['keyboard_shortcut'] ) ? trim( $values['keyboard_shortcut'] ) : $defaults['keyboard_shortcut']; |
| 81 | + $sanitized['keyboard_shortcut'] = preg_match( '/^(Ctrl|Meta|Cmd|Alt|Shift)(\+(Ctrl|Meta|Cmd|Alt|Shift))*\+[A-Za-z0-9]$/', $shortcut ) |
| 82 | + ? $shortcut |
| 83 | + : $defaults['keyboard_shortcut']; |
122 | 84 |
|
123 | | - $sanitized['background_brightness'] = isset( $values['background_brightness'] ) |
124 | | - ? max( 50, min( 150, (int) $values['background_brightness'] ) ) |
125 | | - : 100; |
126 | | - |
127 | | - // Theme settings |
128 | | - $sanitized['theme'] = isset( $values['theme'] ) |
129 | | - && in_array( $values['theme'], array( 'classic', 'cool', 'warm', 'high-contrast', 'pure-black', 'custom' ), true ) |
130 | | - ? $values['theme'] |
131 | | - : 'classic'; |
132 | | - |
133 | | - // Custom colors (only for custom theme) |
| 85 | + // Custom colors (hex values with fallback) |
| 86 | + $color_defaults = $defaults['custom_colors']; |
134 | 87 | $custom_colors = isset( $values['custom_colors'] ) && is_array( $values['custom_colors'] ) |
135 | 88 | ? $values['custom_colors'] |
136 | 89 | : array(); |
137 | 90 |
|
138 | | - $sanitized['custom_colors'] = array( |
139 | | - 'bg_primary' => isset( $custom_colors['bg_primary'] ) ? sanitize_hex_color( $custom_colors['bg_primary'] ) : '#000000', |
140 | | - 'bg_secondary' => isset( $custom_colors['bg_secondary'] ) ? sanitize_hex_color( $custom_colors['bg_secondary'] ) : '#0a0a0a', |
141 | | - 'text_primary' => isset( $custom_colors['text_primary'] ) ? sanitize_hex_color( $custom_colors['text_primary'] ) : '#ffffff', |
142 | | - 'text_secondary' => isset( $custom_colors['text_secondary'] ) ? sanitize_hex_color( $custom_colors['text_secondary'] ) : '#a0a0a0', |
143 | | - 'border' => isset( $custom_colors['border'] ) ? sanitize_hex_color( $custom_colors['border'] ) : '#1a1a1a', |
144 | | - 'accent' => isset( $custom_colors['accent'] ) ? sanitize_hex_color( $custom_colors['accent'] ) : '#4a9eff', |
145 | | - ); |
146 | | - |
147 | | - // Fallback to defaults if sanitize_hex_color returns null |
148 | | - foreach ( $sanitized['custom_colors'] as $key => $color ) { |
149 | | - if ( $color === null ) { |
150 | | - $defaults = array( |
151 | | - 'bg_primary' => '#000000', |
152 | | - 'bg_secondary' => '#0a0a0a', |
153 | | - 'text_primary' => '#ffffff', |
154 | | - 'text_secondary' => '#a0a0a0', |
155 | | - 'border' => '#1a1a1a', |
156 | | - 'accent' => '#4a9eff', |
157 | | - ); |
158 | | - $sanitized['custom_colors'][ $key ] = $defaults[ $key ]; |
159 | | - } |
160 | | - } |
161 | | - |
162 | | - // Existing validation |
163 | | - if ( strlen( $sanitized['example_text'] ) > 200 ) { |
164 | | - return new WP_Error( |
165 | | - 'nightly_invalid_text', |
166 | | - __( 'Example text is too long.', 'nightly' ), |
167 | | - array( 'status' => 400 ) |
168 | | - ); |
| 91 | + $sanitized['custom_colors'] = array(); |
| 92 | + foreach ( $color_defaults as $key => $default ) { |
| 93 | + $color = isset( $custom_colors[ $key ] ) ? sanitize_hex_color( $custom_colors[ $key ] ) : null; |
| 94 | + $sanitized['custom_colors'][ $key ] = $color ?? $default; |
169 | 95 | } |
170 | 96 |
|
171 | 97 | return $sanitized; |
|
0 commit comments