Wordpress 3.6
[autoinstalls/wordpress.git] / wp-includes / class-wp-customize-setting.php
1 <?php
2 /**
3  * Customize Setting Class.
4  *
5  * @package WordPress
6  * @subpackage Customize
7  * @since 3.4.0
8  */
9 class WP_Customize_Setting {
10         public $manager;
11         public $id;
12
13         public $type            = 'theme_mod';
14         public $capability      = 'edit_theme_options';
15         public $theme_supports  = '';
16         public $default         = '';
17         public $transport       = 'refresh';
18
19         public $sanitize_callback    = '';
20         public $sanitize_js_callback = '';
21
22         protected $id_data = array();
23         private $_post_value; // Cached, sanitized $_POST value.
24
25         /**
26          * Constructor.
27          *
28          * @since 3.4.0
29          *
30          * @param WP_Customize_Manager $manager
31          * @param string $id An specific ID of the setting. Can be a
32          *                   theme mod or option name.
33          * @param array $args Setting arguments.
34          * @return WP_Customize_Setting
35          */
36         function __construct( $manager, $id, $args = array() ) {
37                 $keys = array_keys( get_class_vars( __CLASS__ ) );
38                 foreach ( $keys as $key ) {
39                         if ( isset( $args[ $key ] ) )
40                                 $this->$key = $args[ $key ];
41                 }
42
43                 $this->manager = $manager;
44                 $this->id = $id;
45
46                 // Parse the ID for array keys.
47                 $this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
48                 $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] );
49
50                 // Rebuild the ID.
51                 $this->id = $this->id_data[ 'base' ];
52                 if ( ! empty( $this->id_data[ 'keys' ] ) )
53                         $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']';
54
55                 if ( $this->sanitize_callback )
56                         add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 );
57
58                 if ( $this->sanitize_js_callback )
59                         add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 );
60
61                 return $this;
62         }
63
64         /**
65          * Handle previewing the setting.
66          *
67          * @since 3.4.0
68          */
69         public function preview() {
70                 switch( $this->type ) {
71                         case 'theme_mod' :
72                                 add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
73                                 break;
74                         case 'option' :
75                                 if ( empty( $this->id_data[ 'keys' ] ) )
76                                         add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
77                                 else {
78                                         add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
79                                         add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
80                                 }
81                                 break;
82                         default :
83                                 do_action( 'customize_preview_' . $this->id );
84                 }
85         }
86
87         /**
88          * Callback function to filter the theme mods and options.
89          *
90          * @since 3.4.0
91          * @uses WP_Customize_Setting::multidimensional_replace()
92          *
93          * @param mixed $original Old value.
94          * @return mixed New or old value.
95          */
96         public function _preview_filter( $original ) {
97                 return $this->multidimensional_replace( $original, $this->id_data[ 'keys' ], $this->post_value() );
98         }
99
100         /**
101          * Set the value of the parameter for a specific theme.
102          *
103          * @since 3.4.0
104          *
105          * @return bool False if cap check fails or value isn't set.
106          */
107         public final function save() {
108                 $value = $this->post_value();
109
110                 if ( ! $this->check_capabilities() || ! isset( $value ) )
111                         return false;
112
113                 do_action( 'customize_save_' . $this->id_data[ 'base' ] );
114
115                 $this->update( $value );
116         }
117
118         /**
119          * Fetches, validates, and sanitizes the $_POST value.
120          *
121          * @since 3.4.0
122          *
123          * @param mixed $default A default value which is used as a fallback. Default is null.
124          * @return mixed The default value on failure, otherwise the sanitized value.
125          */
126         public final function post_value( $default = null ) {
127                 if ( isset( $this->_post_value ) )
128                         return $this->_post_value;
129
130                 $result = $this->manager->post_value( $this );
131
132                 if ( isset( $result ) )
133                         return $this->_post_value = $result;
134                 else
135                         return $default;
136         }
137
138         /**
139          * Sanitize an input.
140          *
141          * @since 3.4.0
142          *
143          * @param mixed $value The value to sanitize.
144          * @return mixed Null if an input isn't valid, otherwise the sanitized value.
145          */
146         public function sanitize( $value ) {
147                 $value = wp_unslash( $value );
148                 return apply_filters( "customize_sanitize_{$this->id}", $value, $this );
149         }
150
151         /**
152          * Set the value of the parameter for a specific theme.
153          *
154          * @since 3.4.0
155          *
156          * @param mixed $value The value to update.
157          * @return mixed The result of saving the value.
158          */
159         protected function update( $value ) {
160                 switch( $this->type ) {
161                         case 'theme_mod' :
162                                 return $this->_update_theme_mod( $value );
163                                 break;
164                         case 'option' :
165                                 return $this->_update_option( $value );
166                                 break;
167                         default :
168                                 return do_action( 'customize_update_' . $this->type, $value );
169                 }
170         }
171
172         /**
173          * Update the theme mod from the value of the parameter.
174          *
175          * @since 3.4.0
176          *
177          * @param mixed $value The value to update.
178          * @return mixed The result of saving the value.
179          */
180         protected function _update_theme_mod( $value ) {
181                 // Handle non-array theme mod.
182                 if ( empty( $this->id_data[ 'keys' ] ) )
183                         return set_theme_mod( $this->id_data[ 'base' ], $value );
184
185                 // Handle array-based theme mod.
186                 $mods = get_theme_mod( $this->id_data[ 'base' ] );
187                 $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value );
188                 if ( isset( $mods ) )
189                         return set_theme_mod( $this->id_data[ 'base' ], $mods );
190         }
191
192         /**
193          * Update the theme mod from the value of the parameter.
194          *
195          * @since 3.4.0
196          *
197          * @param mixed $value The value to update.
198          * @return mixed The result of saving the value.
199          */
200         protected function _update_option( $value ) {
201                 // Handle non-array option.
202                 if ( empty( $this->id_data[ 'keys' ] ) )
203                         return update_option( $this->id_data[ 'base' ], $value );
204
205                 // Handle array-based options.
206                 $options = get_option( $this->id_data[ 'base' ] );
207                 $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value );
208                 if ( isset( $options ) )
209                         return update_option( $this->id_data[ 'base' ], $options );
210         }
211
212         /**
213          * Fetch the value of the parameter for a specific theme.
214          *
215          * @since 3.4.0
216          *
217          * @return mixed The requested value.
218          */
219         public function value() {
220                 switch( $this->type ) {
221                         case 'theme_mod' :
222                                 $function = 'get_theme_mod';
223                                 break;
224                         case 'option' :
225                                 $function = 'get_option';
226                                 break;
227                         default :
228                                 return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default );
229                 }
230
231                 // Handle non-array value
232                 if ( empty( $this->id_data[ 'keys' ] ) )
233                         return $function( $this->id_data[ 'base' ], $this->default );
234
235                 // Handle array-based value
236                 $values = $function( $this->id_data[ 'base' ] );
237                 return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default );
238         }
239
240         /**
241          * Escape the parameter's value for use in JavaScript.
242          *
243          * @since 3.4.0
244          *
245          * @return mixed The requested escaped value.
246          */
247         public function js_value() {
248                 $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this );
249
250                 if ( is_string( $value ) )
251                         return html_entity_decode( $value, ENT_QUOTES, 'UTF-8');
252
253                 return $value;
254         }
255
256         /**
257          * Check if the theme supports the setting and check user capabilities.
258          *
259          * @since 3.4.0
260          *
261          * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
262          */
263         public final function check_capabilities() {
264                 if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
265                         return false;
266
267                 if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) )
268                         return false;
269
270                 return true;
271         }
272
273         /**
274          * Multidimensional helper function.
275          *
276          * @since 3.4.0
277          *
278          * @param $root
279          * @param $keys
280          * @param bool $create Default is false.
281          * @return null|array Keys are 'root', 'node', and 'key'.
282          */
283         final protected function multidimensional( &$root, $keys, $create = false ) {
284                 if ( $create && empty( $root ) )
285                         $root = array();
286
287                 if ( ! isset( $root ) || empty( $keys ) )
288                         return;
289
290                 $last = array_pop( $keys );
291                 $node = &$root;
292
293                 foreach ( $keys as $key ) {
294                         if ( $create && ! isset( $node[ $key ] ) )
295                                 $node[ $key ] = array();
296
297                         if ( ! is_array( $node ) || ! isset( $node[ $key ] ) )
298                                 return;
299
300                         $node = &$node[ $key ];
301                 }
302
303                 if ( $create && ! isset( $node[ $last ] ) )
304                         $node[ $last ] = array();
305
306                 if ( ! isset( $node[ $last ] ) )
307                         return;
308
309                 return array(
310                         'root' => &$root,
311                         'node' => &$node,
312                         'key'  => $last,
313                 );
314         }
315
316         /**
317          * Will attempt to replace a specific value in a multidimensional array.
318          *
319          * @since 3.4.0
320          *
321          * @param $root
322          * @param $keys
323          * @param mixed $value The value to update.
324          * @return
325          */
326         final protected function multidimensional_replace( $root, $keys, $value ) {
327                 if ( ! isset( $value ) )
328                         return $root;
329                 elseif ( empty( $keys ) ) // If there are no keys, we're replacing the root.
330                         return $value;
331
332                 $result = $this->multidimensional( $root, $keys, true );
333
334                 if ( isset( $result ) )
335                         $result['node'][ $result['key'] ] = $value;
336
337                 return $root;
338         }
339
340         /**
341          * Will attempt to fetch a specific value from a multidimensional array.
342          *
343          * @since 3.4.0
344          *
345          * @param $root
346          * @param $keys
347          * @param $default A default value which is used as a fallback. Default is null.
348          * @return mixed The requested value or the default value.
349          */
350         final protected function multidimensional_get( $root, $keys, $default = null ) {
351                 if ( empty( $keys ) ) // If there are no keys, test the root.
352                         return isset( $root ) ? $root : $default;
353
354                 $result = $this->multidimensional( $root, $keys );
355                 return isset( $result ) ? $result['node'][ $result['key'] ] : $default;
356         }
357
358         /**
359          * Will attempt to check if a specific value in a multidimensional array is set.
360          *
361          * @since 3.4.0
362          *
363          * @param $root
364          * @param $keys
365          * @return bool True if value is set, false if not.
366          */
367         final protected function multidimensional_isset( $root, $keys ) {
368                 $result = $this->multidimensional_get( $root, $keys );
369                 return isset( $result );
370         }
371 }
372
373 /**
374  * A setting that is used to filter a value, but will not save the results.
375  *
376  * Results should be properly handled using another setting or callback.
377  *
378  * @package WordPress
379  * @subpackage Customize
380  * @since 3.4.0
381  */
382 class WP_Customize_Filter_Setting extends WP_Customize_Setting {
383
384         /**
385          * @since 3.4.0
386          */
387         public function update( $value ) {}
388 }
389
390 /**
391  * A setting that is used to filter a value, but will not save the results.
392  *
393  * Results should be properly handled using another setting or callback.
394  *
395  * @package WordPress
396  * @subpackage Customize
397  * @since 3.4.0
398  */
399 final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting {
400         public $id = 'header_image_data';
401
402         /**
403          * @since 3.4.0
404          *
405          * @param $value
406          */
407         public function update( $value ) {
408                 global $custom_image_header;
409
410                 // If the value doesn't exist (removed or random),
411                 // use the header_image value.
412                 if ( ! $value )
413                         $value = $this->manager->get_setting('header_image')->post_value();
414
415                 if ( is_array( $value ) && isset( $value['choice'] ) )
416                         $custom_image_header->set_header_image( $value['choice'] );
417                 else
418                         $custom_image_header->set_header_image( $value );
419         }
420 }
421
422 /**
423  * @package WordPress
424  * @subpackage Customize
425  * @since 3.4.0
426  */
427 final class WP_Customize_Background_Image_Setting extends WP_Customize_Setting {
428         public $id = 'background_image_thumb';
429
430         /**
431          * @since 3.4.0
432          * @uses remove_theme_mod()
433          *
434          * @param $value
435          */
436         public function update( $value ) {
437                 remove_theme_mod( 'background_image_thumb' );
438         }
439 }