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