1 | <?php |
---|
2 | /** |
---|
3 | * Customize Setting Class. |
---|
4 | * |
---|
5 | * Handles saving and sanitizing of settings. |
---|
6 | * |
---|
7 | * @package WordPress |
---|
8 | * @subpackage Customize |
---|
9 | * @since 3.4.0 |
---|
10 | */ |
---|
11 | class WP_Customize_Setting { |
---|
12 | /** |
---|
13 | * @access public |
---|
14 | * @var WP_Customize_Manager |
---|
15 | */ |
---|
16 | public $manager; |
---|
17 | |
---|
18 | /** |
---|
19 | * @access public |
---|
20 | * @var string |
---|
21 | */ |
---|
22 | public $id; |
---|
23 | |
---|
24 | /** |
---|
25 | * @access public |
---|
26 | * @var string |
---|
27 | */ |
---|
28 | public $type = 'theme_mod'; |
---|
29 | |
---|
30 | /** |
---|
31 | * Capability required to edit this setting. |
---|
32 | * |
---|
33 | * @var string |
---|
34 | */ |
---|
35 | public $capability = 'edit_theme_options'; |
---|
36 | |
---|
37 | /** |
---|
38 | * Feature a theme is required to support to enable this setting. |
---|
39 | * |
---|
40 | * @access public |
---|
41 | * @var string |
---|
42 | */ |
---|
43 | public $theme_supports = ''; |
---|
44 | public $default = ''; |
---|
45 | public $transport = 'refresh'; |
---|
46 | |
---|
47 | /** |
---|
48 | * Server-side sanitization callback for the setting's value. |
---|
49 | * |
---|
50 | * @var callback |
---|
51 | */ |
---|
52 | public $sanitize_callback = ''; |
---|
53 | public $sanitize_js_callback = ''; |
---|
54 | |
---|
55 | protected $id_data = array(); |
---|
56 | |
---|
57 | /** |
---|
58 | * Cached and sanitized $_POST value for the setting. |
---|
59 | * |
---|
60 | * @access private |
---|
61 | * @var mixed |
---|
62 | */ |
---|
63 | private $_post_value; |
---|
64 | |
---|
65 | /** |
---|
66 | * Constructor. |
---|
67 | * |
---|
68 | * Any supplied $args override class property defaults. |
---|
69 | * |
---|
70 | * @since 3.4.0 |
---|
71 | * |
---|
72 | * @param WP_Customize_Manager $manager |
---|
73 | * @param string $id An specific ID of the setting. Can be a |
---|
74 | * theme mod or option name. |
---|
75 | * @param array $args Setting arguments. |
---|
76 | * @return WP_Customize_Setting $setting |
---|
77 | */ |
---|
78 | public function __construct( $manager, $id, $args = array() ) { |
---|
79 | $keys = array_keys( get_class_vars( __CLASS__ ) ); |
---|
80 | foreach ( $keys as $key ) { |
---|
81 | if ( isset( $args[ $key ] ) ) |
---|
82 | $this->$key = $args[ $key ]; |
---|
83 | } |
---|
84 | |
---|
85 | $this->manager = $manager; |
---|
86 | $this->id = $id; |
---|
87 | |
---|
88 | // Parse the ID for array keys. |
---|
89 | $this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) ); |
---|
90 | $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] ); |
---|
91 | |
---|
92 | // Rebuild the ID. |
---|
93 | $this->id = $this->id_data[ 'base' ]; |
---|
94 | if ( ! empty( $this->id_data[ 'keys' ] ) ) |
---|
95 | $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']'; |
---|
96 | |
---|
97 | if ( $this->sanitize_callback ) |
---|
98 | add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 ); |
---|
99 | |
---|
100 | if ( $this->sanitize_js_callback ) |
---|
101 | add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 ); |
---|
102 | |
---|
103 | return $this; |
---|
104 | } |
---|
105 | |
---|
106 | /** |
---|
107 | * Handle previewing the setting. |
---|
108 | * |
---|
109 | * @since 3.4.0 |
---|
110 | */ |
---|
111 | public function preview() { |
---|
112 | switch( $this->type ) { |
---|
113 | case 'theme_mod' : |
---|
114 | add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); |
---|
115 | break; |
---|
116 | case 'option' : |
---|
117 | if ( empty( $this->id_data[ 'keys' ] ) ) |
---|
118 | add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); |
---|
119 | else { |
---|
120 | add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); |
---|
121 | add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); |
---|
122 | } |
---|
123 | break; |
---|
124 | default : |
---|
125 | |
---|
126 | /** |
---|
127 | * Fires when the WP_Customize_Setting::preview() method is called for settings |
---|
128 | * not handled as theme_mods or options. |
---|
129 | * |
---|
130 | * The dynamic portion of the hook name, $this->id, refers to the setting ID. |
---|
131 | * |
---|
132 | * @since 3.4.0 |
---|
133 | */ |
---|
134 | do_action( 'customize_preview_' . $this->id, $this ); |
---|
135 | } |
---|
136 | } |
---|
137 | |
---|
138 | /** |
---|
139 | * Callback function to filter the theme mods and options. |
---|
140 | * |
---|
141 | * @since 3.4.0 |
---|
142 | * @uses WP_Customize_Setting::multidimensional_replace() |
---|
143 | * |
---|
144 | * @param mixed $original Old value. |
---|
145 | * @return mixed New or old value. |
---|
146 | */ |
---|
147 | public function _preview_filter( $original ) { |
---|
148 | return $this->multidimensional_replace( $original, $this->id_data[ 'keys' ], $this->post_value() ); |
---|
149 | } |
---|
150 | |
---|
151 | /** |
---|
152 | * Check user capabilities and theme supports, and then save |
---|
153 | * the value of the setting. |
---|
154 | * |
---|
155 | * @since 3.4.0 |
---|
156 | * |
---|
157 | * @return bool False if cap check fails or value isn't set. |
---|
158 | */ |
---|
159 | public final function save() { |
---|
160 | $value = $this->post_value(); |
---|
161 | |
---|
162 | if ( ! $this->check_capabilities() || ! isset( $value ) ) |
---|
163 | return false; |
---|
164 | |
---|
165 | /** |
---|
166 | * Fires when the WP_Customize_Setting::save() method is called for settings |
---|
167 | * not handled as theme_mods or options. |
---|
168 | * |
---|
169 | * The dynamic portion of the hook name, $this->id_data['base'] refers to |
---|
170 | * the base slug of the setting name. |
---|
171 | * |
---|
172 | * @since 3.4.0 |
---|
173 | */ |
---|
174 | do_action( 'customize_save_' . $this->id_data[ 'base' ] ); |
---|
175 | |
---|
176 | $this->update( $value ); |
---|
177 | } |
---|
178 | |
---|
179 | /** |
---|
180 | * Fetch and sanitize the $_POST value for the setting. |
---|
181 | * |
---|
182 | * @since 3.4.0 |
---|
183 | * |
---|
184 | * @param mixed $default A default value which is used as a fallback. Default is null. |
---|
185 | * @return mixed The default value on failure, otherwise the sanitized value. |
---|
186 | */ |
---|
187 | public final function post_value( $default = null ) { |
---|
188 | // Check for a cached value |
---|
189 | if ( isset( $this->_post_value ) ) |
---|
190 | return $this->_post_value; |
---|
191 | |
---|
192 | // Call the manager for the post value |
---|
193 | $result = $this->manager->post_value( $this ); |
---|
194 | |
---|
195 | if ( isset( $result ) ) |
---|
196 | return $this->_post_value = $result; |
---|
197 | else |
---|
198 | return $default; |
---|
199 | } |
---|
200 | |
---|
201 | /** |
---|
202 | * Sanitize an input. |
---|
203 | * |
---|
204 | * @since 3.4.0 |
---|
205 | * |
---|
206 | * @param mixed $value The value to sanitize. |
---|
207 | * @return mixed Null if an input isn't valid, otherwise the sanitized value. |
---|
208 | */ |
---|
209 | public function sanitize( $value ) { |
---|
210 | $value = wp_unslash( $value ); |
---|
211 | |
---|
212 | /** |
---|
213 | * Filter a Customize setting value in un-slashed form. |
---|
214 | * |
---|
215 | * @since 3.4.0 |
---|
216 | * |
---|
217 | * @param mixed $value Value of the setting. |
---|
218 | * @param WP_Customize_Setting $this WP_Customize_Setting instance. |
---|
219 | */ |
---|
220 | return apply_filters( "customize_sanitize_{$this->id}", $value, $this ); |
---|
221 | } |
---|
222 | |
---|
223 | /** |
---|
224 | * Save the value of the setting, using the related API. |
---|
225 | * |
---|
226 | * @since 3.4.0 |
---|
227 | * |
---|
228 | * @param mixed $value The value to update. |
---|
229 | * @return mixed The result of saving the value. |
---|
230 | */ |
---|
231 | protected function update( $value ) { |
---|
232 | switch( $this->type ) { |
---|
233 | case 'theme_mod' : |
---|
234 | return $this->_update_theme_mod( $value ); |
---|
235 | |
---|
236 | case 'option' : |
---|
237 | return $this->_update_option( $value ); |
---|
238 | |
---|
239 | default : |
---|
240 | |
---|
241 | /** |
---|
242 | * Fires when the WP_Customize_Setting::update() method is called for settings |
---|
243 | * not handled as theme_mods or options. |
---|
244 | * |
---|
245 | * The dynamic portion of the hook name, $this->type, refers to the type of setting. |
---|
246 | * |
---|
247 | * @since 3.4.0 |
---|
248 | * |
---|
249 | * @param mixed $value Value of the setting. |
---|
250 | */ |
---|
251 | return do_action( 'customize_update_' . $this->type, $value, $this ); |
---|
252 | } |
---|
253 | } |
---|
254 | |
---|
255 | /** |
---|
256 | * Update the theme mod from the value of the parameter. |
---|
257 | * |
---|
258 | * @since 3.4.0 |
---|
259 | * |
---|
260 | * @param mixed $value The value to update. |
---|
261 | * @return mixed The result of saving the value. |
---|
262 | */ |
---|
263 | protected function _update_theme_mod( $value ) { |
---|
264 | // Handle non-array theme mod. |
---|
265 | if ( empty( $this->id_data[ 'keys' ] ) ) |
---|
266 | return set_theme_mod( $this->id_data[ 'base' ], $value ); |
---|
267 | |
---|
268 | // Handle array-based theme mod. |
---|
269 | $mods = get_theme_mod( $this->id_data[ 'base' ] ); |
---|
270 | $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value ); |
---|
271 | if ( isset( $mods ) ) |
---|
272 | return set_theme_mod( $this->id_data[ 'base' ], $mods ); |
---|
273 | } |
---|
274 | |
---|
275 | /** |
---|
276 | * Update the option from the value of the setting. |
---|
277 | * |
---|
278 | * @since 3.4.0 |
---|
279 | * |
---|
280 | * @param mixed $value The value to update. |
---|
281 | * @return mixed The result of saving the value. |
---|
282 | */ |
---|
283 | protected function _update_option( $value ) { |
---|
284 | // Handle non-array option. |
---|
285 | if ( empty( $this->id_data[ 'keys' ] ) ) |
---|
286 | return update_option( $this->id_data[ 'base' ], $value ); |
---|
287 | |
---|
288 | // Handle array-based options. |
---|
289 | $options = get_option( $this->id_data[ 'base' ] ); |
---|
290 | $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value ); |
---|
291 | if ( isset( $options ) ) |
---|
292 | return update_option( $this->id_data[ 'base' ], $options ); |
---|
293 | } |
---|
294 | |
---|
295 | /** |
---|
296 | * Fetch the value of the setting. |
---|
297 | * |
---|
298 | * @since 3.4.0 |
---|
299 | * |
---|
300 | * @return mixed The value. |
---|
301 | */ |
---|
302 | public function value() { |
---|
303 | // Get the callback that corresponds to the setting type. |
---|
304 | switch( $this->type ) { |
---|
305 | case 'theme_mod' : |
---|
306 | $function = 'get_theme_mod'; |
---|
307 | break; |
---|
308 | case 'option' : |
---|
309 | $function = 'get_option'; |
---|
310 | break; |
---|
311 | default : |
---|
312 | |
---|
313 | /** |
---|
314 | * Filter a Customize setting value not handled as a theme_mod or option. |
---|
315 | * |
---|
316 | * The dynamic portion of the hook name, $this->id_date['base'], refers to |
---|
317 | * the base slug of the setting name. |
---|
318 | * |
---|
319 | * For settings handled as theme_mods or options, see those corresponding |
---|
320 | * functions for available hooks. |
---|
321 | * |
---|
322 | * @since 3.4.0 |
---|
323 | * |
---|
324 | * @param mixed $default The setting default value. Default empty. |
---|
325 | */ |
---|
326 | return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default ); |
---|
327 | } |
---|
328 | |
---|
329 | // Handle non-array value |
---|
330 | if ( empty( $this->id_data[ 'keys' ] ) ) |
---|
331 | return $function( $this->id_data[ 'base' ], $this->default ); |
---|
332 | |
---|
333 | // Handle array-based value |
---|
334 | $values = $function( $this->id_data[ 'base' ] ); |
---|
335 | return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default ); |
---|
336 | } |
---|
337 | |
---|
338 | /** |
---|
339 | * Sanitize the setting's value for use in JavaScript. |
---|
340 | * |
---|
341 | * @since 3.4.0 |
---|
342 | * |
---|
343 | * @return mixed The requested escaped value. |
---|
344 | */ |
---|
345 | public function js_value() { |
---|
346 | |
---|
347 | /** |
---|
348 | * Filter a Customize setting value for use in JavaScript. |
---|
349 | * |
---|
350 | * The dynamic portion of the hook name, $this->id, refers to the setting ID. |
---|
351 | * |
---|
352 | * @since 3.4.0 |
---|
353 | * |
---|
354 | * @param mixed $value The setting value. |
---|
355 | * @param WP_Customize_Setting $this WP_Customize_Setting instance. |
---|
356 | */ |
---|
357 | $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this ); |
---|
358 | |
---|
359 | if ( is_string( $value ) ) |
---|
360 | return html_entity_decode( $value, ENT_QUOTES, 'UTF-8'); |
---|
361 | |
---|
362 | return $value; |
---|
363 | } |
---|
364 | |
---|
365 | /** |
---|
366 | * Validate user capabilities whether the theme supports the setting. |
---|
367 | * |
---|
368 | * @since 3.4.0 |
---|
369 | * |
---|
370 | * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true. |
---|
371 | */ |
---|
372 | public final function check_capabilities() { |
---|
373 | if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) |
---|
374 | return false; |
---|
375 | |
---|
376 | if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) ) |
---|
377 | return false; |
---|
378 | |
---|
379 | return true; |
---|
380 | } |
---|
381 | |
---|
382 | /** |
---|
383 | * Multidimensional helper function. |
---|
384 | * |
---|
385 | * @since 3.4.0 |
---|
386 | * |
---|
387 | * @param $root |
---|
388 | * @param $keys |
---|
389 | * @param bool $create Default is false. |
---|
390 | * @return null|array Keys are 'root', 'node', and 'key'. |
---|
391 | */ |
---|
392 | final protected function multidimensional( &$root, $keys, $create = false ) { |
---|
393 | if ( $create && empty( $root ) ) |
---|
394 | $root = array(); |
---|
395 | |
---|
396 | if ( ! isset( $root ) || empty( $keys ) ) |
---|
397 | return; |
---|
398 | |
---|
399 | $last = array_pop( $keys ); |
---|
400 | $node = &$root; |
---|
401 | |
---|
402 | foreach ( $keys as $key ) { |
---|
403 | if ( $create && ! isset( $node[ $key ] ) ) |
---|
404 | $node[ $key ] = array(); |
---|
405 | |
---|
406 | if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) |
---|
407 | return; |
---|
408 | |
---|
409 | $node = &$node[ $key ]; |
---|
410 | } |
---|
411 | |
---|
412 | if ( $create && ! isset( $node[ $last ] ) ) |
---|
413 | $node[ $last ] = array(); |
---|
414 | |
---|
415 | if ( ! isset( $node[ $last ] ) ) |
---|
416 | return; |
---|
417 | |
---|
418 | return array( |
---|
419 | 'root' => &$root, |
---|
420 | 'node' => &$node, |
---|
421 | 'key' => $last, |
---|
422 | ); |
---|
423 | } |
---|
424 | |
---|
425 | /** |
---|
426 | * Will attempt to replace a specific value in a multidimensional array. |
---|
427 | * |
---|
428 | * @since 3.4.0 |
---|
429 | * |
---|
430 | * @param $root |
---|
431 | * @param $keys |
---|
432 | * @param mixed $value The value to update. |
---|
433 | * @return |
---|
434 | */ |
---|
435 | final protected function multidimensional_replace( $root, $keys, $value ) { |
---|
436 | if ( ! isset( $value ) ) |
---|
437 | return $root; |
---|
438 | elseif ( empty( $keys ) ) // If there are no keys, we're replacing the root. |
---|
439 | return $value; |
---|
440 | |
---|
441 | $result = $this->multidimensional( $root, $keys, true ); |
---|
442 | |
---|
443 | if ( isset( $result ) ) |
---|
444 | $result['node'][ $result['key'] ] = $value; |
---|
445 | |
---|
446 | return $root; |
---|
447 | } |
---|
448 | |
---|
449 | /** |
---|
450 | * Will attempt to fetch a specific value from a multidimensional array. |
---|
451 | * |
---|
452 | * @since 3.4.0 |
---|
453 | * |
---|
454 | * @param $root |
---|
455 | * @param $keys |
---|
456 | * @param $default A default value which is used as a fallback. Default is null. |
---|
457 | * @return mixed The requested value or the default value. |
---|
458 | */ |
---|
459 | final protected function multidimensional_get( $root, $keys, $default = null ) { |
---|
460 | if ( empty( $keys ) ) // If there are no keys, test the root. |
---|
461 | return isset( $root ) ? $root : $default; |
---|
462 | |
---|
463 | $result = $this->multidimensional( $root, $keys ); |
---|
464 | return isset( $result ) ? $result['node'][ $result['key'] ] : $default; |
---|
465 | } |
---|
466 | |
---|
467 | /** |
---|
468 | * Will attempt to check if a specific value in a multidimensional array is set. |
---|
469 | * |
---|
470 | * @since 3.4.0 |
---|
471 | * |
---|
472 | * @param $root |
---|
473 | * @param $keys |
---|
474 | * @return bool True if value is set, false if not. |
---|
475 | */ |
---|
476 | final protected function multidimensional_isset( $root, $keys ) { |
---|
477 | $result = $this->multidimensional_get( $root, $keys ); |
---|
478 | return isset( $result ); |
---|
479 | } |
---|
480 | } |
---|
481 | |
---|
482 | /** |
---|
483 | * A setting that is used to filter a value, but will not save the results. |
---|
484 | * |
---|
485 | * Results should be properly handled using another setting or callback. |
---|
486 | * |
---|
487 | * @package WordPress |
---|
488 | * @subpackage Customize |
---|
489 | * @since 3.4.0 |
---|
490 | */ |
---|
491 | class WP_Customize_Filter_Setting extends WP_Customize_Setting { |
---|
492 | |
---|
493 | /** |
---|
494 | * @since 3.4.0 |
---|
495 | */ |
---|
496 | public function update( $value ) {} |
---|
497 | } |
---|
498 | |
---|
499 | /** |
---|
500 | * A setting that is used to filter a value, but will not save the results. |
---|
501 | * |
---|
502 | * Results should be properly handled using another setting or callback. |
---|
503 | * |
---|
504 | * @package WordPress |
---|
505 | * @subpackage Customize |
---|
506 | * @since 3.4.0 |
---|
507 | */ |
---|
508 | final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting { |
---|
509 | public $id = 'header_image_data'; |
---|
510 | |
---|
511 | /** |
---|
512 | * @since 3.4.0 |
---|
513 | * |
---|
514 | * @param $value |
---|
515 | */ |
---|
516 | public function update( $value ) { |
---|
517 | global $custom_image_header; |
---|
518 | |
---|
519 | // If the value doesn't exist (removed or random), |
---|
520 | // use the header_image value. |
---|
521 | if ( ! $value ) |
---|
522 | $value = $this->manager->get_setting('header_image')->post_value(); |
---|
523 | |
---|
524 | if ( is_array( $value ) && isset( $value['choice'] ) ) |
---|
525 | $custom_image_header->set_header_image( $value['choice'] ); |
---|
526 | else |
---|
527 | $custom_image_header->set_header_image( $value ); |
---|
528 | } |
---|
529 | } |
---|
530 | |
---|
531 | /** |
---|
532 | * Class WP_Customize_Background_Image_Setting |
---|
533 | * |
---|
534 | * @package WordPress |
---|
535 | * @subpackage Customize |
---|
536 | * @since 3.4.0 |
---|
537 | */ |
---|
538 | final class WP_Customize_Background_Image_Setting extends WP_Customize_Setting { |
---|
539 | public $id = 'background_image_thumb'; |
---|
540 | |
---|
541 | /** |
---|
542 | * @since 3.4.0 |
---|
543 | * @uses remove_theme_mod() |
---|
544 | * |
---|
545 | * @param $value |
---|
546 | */ |
---|
547 | public function update( $value ) { |
---|
548 | remove_theme_mod( 'background_image_thumb' ); |
---|
549 | } |
---|
550 | } |
---|