Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-customize-setting.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* WordPress Customize Setting classes
4
+
*
5
+
* @package WordPress
6
+
* @subpackage Customize
7
+
* @since 3.4.0
8
+
*/
9
+
10
+
// Don't load directly.
11
+
if ( ! defined( 'ABSPATH' ) ) {
12
+
die( '-1' );
13
+
}
14
+
15
+
/**
16
+
* Customize Setting class.
17
+
*
18
+
* Handles saving and sanitizing of settings.
19
+
*
20
+
* @since 3.4.0
21
+
*
22
+
* @see WP_Customize_Manager
23
+
* @link https://developer.wordpress.org/themes/customize-api
24
+
*/
25
+
#[AllowDynamicProperties]
26
+
class WP_Customize_Setting {
27
+
/**
28
+
* Customizer bootstrap instance.
29
+
*
30
+
* @since 3.4.0
31
+
* @var WP_Customize_Manager
32
+
*/
33
+
public $manager;
34
+
35
+
/**
36
+
* Unique string identifier for the setting.
37
+
*
38
+
* @since 3.4.0
39
+
* @var string
40
+
*/
41
+
public $id;
42
+
43
+
/**
44
+
* Type of customize settings.
45
+
*
46
+
* @since 3.4.0
47
+
* @var string
48
+
*/
49
+
public $type = 'theme_mod';
50
+
51
+
/**
52
+
* Capability required to edit this setting.
53
+
*
54
+
* @since 3.4.0
55
+
* @var string|array
56
+
*/
57
+
public $capability = 'edit_theme_options';
58
+
59
+
/**
60
+
* Theme features required to support the setting.
61
+
*
62
+
* @since 3.4.0
63
+
* @var string|string[]
64
+
*/
65
+
public $theme_supports = '';
66
+
67
+
/**
68
+
* The default value for the setting.
69
+
*
70
+
* @since 3.4.0
71
+
* @var string
72
+
*/
73
+
public $default = '';
74
+
75
+
/**
76
+
* Options for rendering the live preview of changes in Customizer.
77
+
*
78
+
* Set this value to 'postMessage' to enable a custom JavaScript handler to render changes to this setting
79
+
* as opposed to reloading the whole page.
80
+
*
81
+
* @since 3.4.0
82
+
* @var string
83
+
*/
84
+
public $transport = 'refresh';
85
+
86
+
/**
87
+
* Server-side validation callback for the setting's value.
88
+
*
89
+
* @since 4.6.0
90
+
* @var callable
91
+
*/
92
+
public $validate_callback = '';
93
+
94
+
/**
95
+
* Callback to filter a Customize setting value in un-slashed form.
96
+
*
97
+
* @since 3.4.0
98
+
* @var callable
99
+
*/
100
+
public $sanitize_callback = '';
101
+
102
+
/**
103
+
* Callback to convert a Customize PHP setting value to a value that is JSON serializable.
104
+
*
105
+
* @since 3.4.0
106
+
* @var callable
107
+
*/
108
+
public $sanitize_js_callback = '';
109
+
110
+
/**
111
+
* Whether or not the setting is initially dirty when created.
112
+
*
113
+
* This is used to ensure that a setting will be sent from the pane to the
114
+
* preview when loading the Customizer. Normally a setting only is synced to
115
+
* the preview if it has been changed. This allows the setting to be sent
116
+
* from the start.
117
+
*
118
+
* @since 4.2.0
119
+
* @var bool
120
+
*/
121
+
public $dirty = false;
122
+
123
+
/**
124
+
* ID Data.
125
+
*
126
+
* @since 3.4.0
127
+
* @var array
128
+
*/
129
+
protected $id_data = array();
130
+
131
+
/**
132
+
* Whether or not preview() was called.
133
+
*
134
+
* @since 4.4.0
135
+
* @var bool
136
+
*/
137
+
protected $is_previewed = false;
138
+
139
+
/**
140
+
* Cache of multidimensional values to improve performance.
141
+
*
142
+
* @since 4.4.0
143
+
* @var array
144
+
*/
145
+
protected static $aggregated_multidimensionals = array();
146
+
147
+
/**
148
+
* Whether the multidimensional setting is aggregated.
149
+
*
150
+
* @since 4.4.0
151
+
* @var bool
152
+
*/
153
+
protected $is_multidimensional_aggregated = false;
154
+
155
+
/**
156
+
* Constructor.
157
+
*
158
+
* Any supplied $args override class property defaults.
159
+
*
160
+
* @since 3.4.0
161
+
*
162
+
* @param WP_Customize_Manager $manager Customizer bootstrap instance.
163
+
* @param string $id A specific ID of the setting.
164
+
* Can be a theme mod or option name.
165
+
* @param array $args {
166
+
* Optional. Array of properties for the new Setting object. Default empty array.
167
+
*
168
+
* @type string $type Type of the setting. Default 'theme_mod'.
169
+
* @type string $capability Capability required for the setting. Default 'edit_theme_options'
170
+
* @type string|string[] $theme_supports Theme features required to support the panel. Default is none.
171
+
* @type string $default Default value for the setting. Default is empty string.
172
+
* @type string $transport Options for rendering the live preview of changes in Customizer.
173
+
* Using 'refresh' makes the change visible by reloading the whole preview.
174
+
* Using 'postMessage' allows a custom JavaScript to handle live changes.
175
+
* Default is 'refresh'.
176
+
* @type callable $validate_callback Server-side validation callback for the setting's value.
177
+
* @type callable $sanitize_callback Callback to filter a Customize setting value in un-slashed form.
178
+
* @type callable $sanitize_js_callback Callback to convert a Customize PHP setting value to a value that is
179
+
* JSON serializable.
180
+
* @type bool $dirty Whether or not the setting is initially dirty when created.
181
+
* }
182
+
*/
183
+
public function __construct( $manager, $id, $args = array() ) {
184
+
$keys = array_keys( get_object_vars( $this ) );
185
+
foreach ( $keys as $key ) {
186
+
if ( isset( $args[ $key ] ) ) {
187
+
$this->$key = $args[ $key ];
188
+
}
189
+
}
190
+
191
+
$this->manager = $manager;
192
+
$this->id = $id;
193
+
194
+
// Parse the ID for array keys.
195
+
$this->id_data['keys'] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
196
+
$this->id_data['base'] = array_shift( $this->id_data['keys'] );
197
+
198
+
// Rebuild the ID.
199
+
$this->id = $this->id_data['base'];
200
+
if ( ! empty( $this->id_data['keys'] ) ) {
201
+
$this->id .= '[' . implode( '][', $this->id_data['keys'] ) . ']';
202
+
}
203
+
204
+
if ( $this->validate_callback ) {
205
+
add_filter( "customize_validate_{$this->id}", $this->validate_callback, 10, 3 );
206
+
}
207
+
if ( $this->sanitize_callback ) {
208
+
add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 );
209
+
}
210
+
if ( $this->sanitize_js_callback ) {
211
+
add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 );
212
+
}
213
+
214
+
if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
215
+
// Other setting types can opt-in to aggregate multidimensional explicitly.
216
+
$this->aggregate_multidimensional();
217
+
218
+
// Allow option settings to indicate whether they should be autoloaded.
219
+
if ( 'option' === $this->type && isset( $args['autoload'] ) ) {
220
+
self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] = $args['autoload'];
221
+
}
222
+
}
223
+
}
224
+
225
+
/**
226
+
* Get parsed ID data for multidimensional setting.
227
+
*
228
+
* @since 4.4.0
229
+
*
230
+
* @return array {
231
+
* ID data for multidimensional setting.
232
+
*
233
+
* @type string $base ID base
234
+
* @type array $keys Keys for multidimensional array.
235
+
* }
236
+
*/
237
+
final public function id_data() {
238
+
return $this->id_data;
239
+
}
240
+
241
+
/**
242
+
* Set up the setting for aggregated multidimensional values.
243
+
*
244
+
* When a multidimensional setting gets aggregated, all of its preview and update
245
+
* calls get combined into one call, greatly improving performance.
246
+
*
247
+
* @since 4.4.0
248
+
*/
249
+
protected function aggregate_multidimensional() {
250
+
$id_base = $this->id_data['base'];
251
+
if ( ! isset( self::$aggregated_multidimensionals[ $this->type ] ) ) {
252
+
self::$aggregated_multidimensionals[ $this->type ] = array();
253
+
}
254
+
if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) {
255
+
self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array(
256
+
'previewed_instances' => array(), // Calling preview() will add the $setting to the array.
257
+
'preview_applied_instances' => array(), // Flags for which settings have had their values applied.
258
+
'root_value' => $this->get_root_value( array() ), // Root value for initial state, manipulated by preview and update calls.
259
+
);
260
+
}
261
+
262
+
if ( ! empty( $this->id_data['keys'] ) ) {
263
+
// Note the preview-applied flag is cleared at priority 9 to ensure it is cleared before a deferred-preview runs.
264
+
add_action( "customize_post_value_set_{$this->id}", array( $this, '_clear_aggregated_multidimensional_preview_applied_flag' ), 9 );
265
+
$this->is_multidimensional_aggregated = true;
266
+
}
267
+
}
268
+
269
+
/**
270
+
* Reset `$aggregated_multidimensionals` static variable.
271
+
*
272
+
* This is intended only for use by unit tests.
273
+
*
274
+
* @since 4.5.0
275
+
* @ignore
276
+
*/
277
+
public static function reset_aggregated_multidimensionals() {
278
+
self::$aggregated_multidimensionals = array();
279
+
}
280
+
281
+
/**
282
+
* The ID for the current site when the preview() method was called.
283
+
*
284
+
* @since 4.2.0
285
+
* @var int
286
+
*/
287
+
protected $_previewed_blog_id;
288
+
289
+
/**
290
+
* Return true if the current site is not the same as the previewed site.
291
+
*
292
+
* @since 4.2.0
293
+
*
294
+
* @return bool If preview() has been called.
295
+
*/
296
+
public function is_current_blog_previewed() {
297
+
if ( ! isset( $this->_previewed_blog_id ) ) {
298
+
return false;
299
+
}
300
+
return ( get_current_blog_id() === $this->_previewed_blog_id );
301
+
}
302
+
303
+
/**
304
+
* Original non-previewed value stored by the preview method.
305
+
*
306
+
* @see WP_Customize_Setting::preview()
307
+
* @since 4.1.1
308
+
* @var mixed
309
+
*/
310
+
protected $_original_value;
311
+
312
+
/**
313
+
* Add filters to supply the setting's value when accessed.
314
+
*
315
+
* If the setting already has a pre-existing value and there is no incoming
316
+
* post value for the setting, then this method will short-circuit since
317
+
* there is no change to preview.
318
+
*
319
+
* @since 3.4.0
320
+
* @since 4.4.0 Added boolean return value.
321
+
*
322
+
* @return bool False when preview short-circuits due no change needing to be previewed.
323
+
*/
324
+
public function preview() {
325
+
if ( ! isset( $this->_previewed_blog_id ) ) {
326
+
$this->_previewed_blog_id = get_current_blog_id();
327
+
}
328
+
329
+
// Prevent re-previewing an already-previewed setting.
330
+
if ( $this->is_previewed ) {
331
+
return true;
332
+
}
333
+
334
+
$id_base = $this->id_data['base'];
335
+
$is_multidimensional = ! empty( $this->id_data['keys'] );
336
+
$multidimensional_filter = array( $this, '_multidimensional_preview_filter' );
337
+
338
+
/*
339
+
* Check if the setting has a pre-existing value (an isset check),
340
+
* and if doesn't have any incoming post value. If both checks are true,
341
+
* then the preview short-circuits because there is nothing that needs
342
+
* to be previewed.
343
+
*/
344
+
$undefined = new stdClass();
345
+
$needs_preview = ( $undefined !== $this->post_value( $undefined ) );
346
+
$value = null;
347
+
348
+
// Since no post value was defined, check if we have an initial value set.
349
+
if ( ! $needs_preview ) {
350
+
if ( $this->is_multidimensional_aggregated ) {
351
+
$root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
352
+
$value = $this->multidimensional_get( $root, $this->id_data['keys'], $undefined );
353
+
} else {
354
+
$default = $this->default;
355
+
$this->default = $undefined; // Temporarily set default to undefined so we can detect if existing value is set.
356
+
$value = $this->value();
357
+
$this->default = $default;
358
+
}
359
+
$needs_preview = ( $undefined === $value ); // Because the default needs to be supplied.
360
+
}
361
+
362
+
// If the setting does not need previewing now, defer to when it has a value to preview.
363
+
if ( ! $needs_preview ) {
364
+
if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) {
365
+
add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) );
366
+
}
367
+
return false;
368
+
}
369
+
370
+
switch ( $this->type ) {
371
+
case 'theme_mod':
372
+
if ( ! $is_multidimensional ) {
373
+
add_filter( "theme_mod_{$id_base}", array( $this, '_preview_filter' ) );
374
+
} else {
375
+
if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
376
+
// Only add this filter once for this ID base.
377
+
add_filter( "theme_mod_{$id_base}", $multidimensional_filter );
378
+
}
379
+
self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
380
+
}
381
+
break;
382
+
case 'option':
383
+
if ( ! $is_multidimensional ) {
384
+
add_filter( "pre_option_{$id_base}", array( $this, '_preview_filter' ) );
385
+
} else {
386
+
if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
387
+
// Only add these filters once for this ID base.
388
+
add_filter( "option_{$id_base}", $multidimensional_filter );
389
+
add_filter( "default_option_{$id_base}", $multidimensional_filter );
390
+
}
391
+
self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
392
+
}
393
+
break;
394
+
default:
395
+
/**
396
+
* Fires when the WP_Customize_Setting::preview() method is called for settings
397
+
* not handled as theme_mods or options.
398
+
*
399
+
* The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
400
+
*
401
+
* @since 3.4.0
402
+
*
403
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
404
+
*/
405
+
do_action( "customize_preview_{$this->id}", $this );
406
+
407
+
/**
408
+
* Fires when the WP_Customize_Setting::preview() method is called for settings
409
+
* not handled as theme_mods or options.
410
+
*
411
+
* The dynamic portion of the hook name, `$this->type`, refers to the setting type.
412
+
*
413
+
* @since 4.1.0
414
+
*
415
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
416
+
*/
417
+
do_action( "customize_preview_{$this->type}", $this );
418
+
}
419
+
420
+
$this->is_previewed = true;
421
+
422
+
return true;
423
+
}
424
+
425
+
/**
426
+
* Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated.
427
+
*
428
+
* This ensures that the new value will get sanitized and used the next time
429
+
* that `WP_Customize_Setting::_multidimensional_preview_filter()`
430
+
* is called for this setting.
431
+
*
432
+
* @since 4.4.0
433
+
*
434
+
* @see WP_Customize_Manager::set_post_value()
435
+
* @see WP_Customize_Setting::_multidimensional_preview_filter()
436
+
*/
437
+
final public function _clear_aggregated_multidimensional_preview_applied_flag() {
438
+
unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $this->id ] );
439
+
}
440
+
441
+
/**
442
+
* Callback function to filter non-multidimensional theme mods and options.
443
+
*
444
+
* If switch_to_blog() was called after the preview() method, and the current
445
+
* site is now not the same site, then this method does a no-op and returns
446
+
* the original value.
447
+
*
448
+
* @since 3.4.0
449
+
*
450
+
* @param mixed $original Old value.
451
+
* @return mixed New or old value.
452
+
*/
453
+
public function _preview_filter( $original ) {
454
+
if ( ! $this->is_current_blog_previewed() ) {
455
+
return $original;
456
+
}
457
+
458
+
$undefined = new stdClass(); // Symbol hack.
459
+
$post_value = $this->post_value( $undefined );
460
+
if ( $undefined !== $post_value ) {
461
+
$value = $post_value;
462
+
} else {
463
+
/*
464
+
* Note that we don't use $original here because preview() will
465
+
* not add the filter in the first place if it has an initial value
466
+
* and there is no post value.
467
+
*/
468
+
$value = $this->default;
469
+
}
470
+
return $value;
471
+
}
472
+
473
+
/**
474
+
* Callback function to filter multidimensional theme mods and options.
475
+
*
476
+
* For all multidimensional settings of a given type, the preview filter for
477
+
* the first setting previewed will be used to apply the values for the others.
478
+
*
479
+
* @since 4.4.0
480
+
*
481
+
* @see WP_Customize_Setting::$aggregated_multidimensionals
482
+
* @param mixed $original Original root value.
483
+
* @return mixed New or old value.
484
+
*/
485
+
final public function _multidimensional_preview_filter( $original ) {
486
+
if ( ! $this->is_current_blog_previewed() ) {
487
+
return $original;
488
+
}
489
+
490
+
$id_base = $this->id_data['base'];
491
+
492
+
// If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value.
493
+
if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
494
+
return $original;
495
+
}
496
+
497
+
foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) {
498
+
// Skip applying previewed value for any settings that have already been applied.
499
+
if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) {
500
+
continue;
501
+
}
502
+
503
+
// Do the replacements of the posted/default sub value into the root value.
504
+
$value = $previewed_setting->post_value( $previewed_setting->default );
505
+
$root = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'];
506
+
$root = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value );
507
+
self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root;
508
+
509
+
// Mark this setting having been applied so that it will be skipped when the filter is called again.
510
+
self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true;
511
+
}
512
+
513
+
return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
514
+
}
515
+
516
+
/**
517
+
* Checks user capabilities and theme supports, and then saves
518
+
* the value of the setting.
519
+
*
520
+
* @since 3.4.0
521
+
*
522
+
* @return void|false Void on success, false if cap check fails
523
+
* or value isn't set or is invalid.
524
+
*/
525
+
final public function save() {
526
+
$value = $this->post_value();
527
+
528
+
if ( ! $this->check_capabilities() || ! isset( $value ) ) {
529
+
return false;
530
+
}
531
+
532
+
$id_base = $this->id_data['base'];
533
+
534
+
/**
535
+
* Fires when the WP_Customize_Setting::save() method is called.
536
+
*
537
+
* The dynamic portion of the hook name, `$id_base` refers to
538
+
* the base slug of the setting name.
539
+
*
540
+
* @since 3.4.0
541
+
*
542
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
543
+
*/
544
+
do_action( "customize_save_{$id_base}", $this );
545
+
546
+
$this->update( $value );
547
+
}
548
+
549
+
/**
550
+
* Fetch and sanitize the $_POST value for the setting.
551
+
*
552
+
* During a save request prior to save, post_value() provides the new value while value() does not.
553
+
*
554
+
* @since 3.4.0
555
+
*
556
+
* @param mixed $default_value A default value which is used as a fallback. Default null.
557
+
* @return mixed The default value on failure, otherwise the sanitized and validated value.
558
+
*/
559
+
final public function post_value( $default_value = null ) {
560
+
return $this->manager->post_value( $this, $default_value );
561
+
}
562
+
563
+
/**
564
+
* Sanitize an input.
565
+
*
566
+
* @since 3.4.0
567
+
*
568
+
* @param string|array $value The value to sanitize.
569
+
* @return string|array|null|WP_Error Sanitized value, or `null`/`WP_Error` if invalid.
570
+
*/
571
+
public function sanitize( $value ) {
572
+
573
+
/**
574
+
* Filters a Customize setting value in un-slashed form.
575
+
*
576
+
* @since 3.4.0
577
+
*
578
+
* @param mixed $value Value of the setting.
579
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
580
+
*/
581
+
return apply_filters( "customize_sanitize_{$this->id}", $value, $this );
582
+
}
583
+
584
+
/**
585
+
* Validates an input.
586
+
*
587
+
* @since 4.6.0
588
+
*
589
+
* @see WP_REST_Request::has_valid_params()
590
+
*
591
+
* @param mixed $value Value to validate.
592
+
* @return true|WP_Error True if the input was validated, otherwise WP_Error.
593
+
*/
594
+
public function validate( $value ) {
595
+
if ( is_wp_error( $value ) ) {
596
+
return $value;
597
+
}
598
+
if ( is_null( $value ) ) {
599
+
return new WP_Error( 'invalid_value', __( 'Invalid value.' ) );
600
+
}
601
+
602
+
$validity = new WP_Error();
603
+
604
+
/**
605
+
* Validates a Customize setting value.
606
+
*
607
+
* Plugins should amend the `$validity` object via its `WP_Error::add()` method.
608
+
*
609
+
* The dynamic portion of the hook name, `$this->ID`, refers to the setting ID.
610
+
*
611
+
* @since 4.6.0
612
+
*
613
+
* @param WP_Error $validity Filtered from `true` to `WP_Error` when invalid.
614
+
* @param mixed $value Value of the setting.
615
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
616
+
*/
617
+
$validity = apply_filters( "customize_validate_{$this->id}", $validity, $value, $this );
618
+
619
+
if ( is_wp_error( $validity ) && ! $validity->has_errors() ) {
620
+
$validity = true;
621
+
}
622
+
return $validity;
623
+
}
624
+
625
+
/**
626
+
* Get the root value for a setting, especially for multidimensional ones.
627
+
*
628
+
* @since 4.4.0
629
+
*
630
+
* @param mixed $default_value Value to return if root does not exist.
631
+
* @return mixed
632
+
*/
633
+
protected function get_root_value( $default_value = null ) {
634
+
$id_base = $this->id_data['base'];
635
+
if ( 'option' === $this->type ) {
636
+
return get_option( $id_base, $default_value );
637
+
} elseif ( 'theme_mod' === $this->type ) {
638
+
return get_theme_mod( $id_base, $default_value );
639
+
} else {
640
+
/*
641
+
* Any WP_Customize_Setting subclass implementing aggregate multidimensional
642
+
* will need to override this method to obtain the data from the appropriate
643
+
* location.
644
+
*/
645
+
return $default_value;
646
+
}
647
+
}
648
+
649
+
/**
650
+
* Set the root value for a setting, especially for multidimensional ones.
651
+
*
652
+
* @since 4.4.0
653
+
*
654
+
* @param mixed $value Value to set as root of multidimensional setting.
655
+
* @return bool Whether the multidimensional root was updated successfully.
656
+
*/
657
+
protected function set_root_value( $value ) {
658
+
$id_base = $this->id_data['base'];
659
+
if ( 'option' === $this->type ) {
660
+
$autoload = true;
661
+
if ( isset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] ) ) {
662
+
$autoload = self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'];
663
+
}
664
+
return update_option( $id_base, $value, $autoload );
665
+
} elseif ( 'theme_mod' === $this->type ) {
666
+
set_theme_mod( $id_base, $value );
667
+
return true;
668
+
} else {
669
+
/*
670
+
* Any WP_Customize_Setting subclass implementing aggregate multidimensional
671
+
* will need to override this method to obtain the data from the appropriate
672
+
* location.
673
+
*/
674
+
return false;
675
+
}
676
+
}
677
+
678
+
/**
679
+
* Save the value of the setting, using the related API.
680
+
*
681
+
* @since 3.4.0
682
+
*
683
+
* @param mixed $value The value to update.
684
+
* @return bool The result of saving the value.
685
+
*/
686
+
protected function update( $value ) {
687
+
$id_base = $this->id_data['base'];
688
+
if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
689
+
if ( ! $this->is_multidimensional_aggregated ) {
690
+
return $this->set_root_value( $value );
691
+
} else {
692
+
$root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
693
+
$root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value );
694
+
self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root;
695
+
return $this->set_root_value( $root );
696
+
}
697
+
} else {
698
+
/**
699
+
* Fires when the WP_Customize_Setting::update() method is called for settings
700
+
* not handled as theme_mods or options.
701
+
*
702
+
* The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
703
+
*
704
+
* @since 3.4.0
705
+
*
706
+
* @param mixed $value Value of the setting.
707
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
708
+
*/
709
+
do_action( "customize_update_{$this->type}", $value, $this );
710
+
711
+
return has_action( "customize_update_{$this->type}" );
712
+
}
713
+
}
714
+
715
+
/**
716
+
* Deprecated method.
717
+
*
718
+
* @since 3.4.0
719
+
* @deprecated 4.4.0 Deprecated in favor of update() method.
720
+
*/
721
+
protected function _update_theme_mod() {
722
+
_deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
723
+
}
724
+
725
+
/**
726
+
* Deprecated method.
727
+
*
728
+
* @since 3.4.0
729
+
* @deprecated 4.4.0 Deprecated in favor of update() method.
730
+
*/
731
+
protected function _update_option() {
732
+
_deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
733
+
}
734
+
735
+
/**
736
+
* Fetch the value of the setting.
737
+
*
738
+
* @since 3.4.0
739
+
*
740
+
* @return mixed The value.
741
+
*/
742
+
public function value() {
743
+
$id_base = $this->id_data['base'];
744
+
$is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type );
745
+
746
+
if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) {
747
+
748
+
// Use post value if previewed and a post value is present.
749
+
if ( $this->is_previewed ) {
750
+
$value = $this->post_value( null );
751
+
if ( null !== $value ) {
752
+
return $value;
753
+
}
754
+
}
755
+
756
+
$value = $this->get_root_value( $this->default );
757
+
758
+
/**
759
+
* Filters a Customize setting value not handled as a theme_mod or option.
760
+
*
761
+
* The dynamic portion of the hook name, `$id_base`, refers to
762
+
* the base slug of the setting name, initialized from `$this->id_data['base']`.
763
+
*
764
+
* For settings handled as theme_mods or options, see those corresponding
765
+
* functions for available hooks.
766
+
*
767
+
* @since 3.4.0
768
+
* @since 4.6.0 Added the `$this` setting instance as the second parameter.
769
+
*
770
+
* @param mixed $default_value The setting default value. Default empty.
771
+
* @param WP_Customize_Setting $setting The setting instance.
772
+
*/
773
+
$value = apply_filters( "customize_value_{$id_base}", $value, $this );
774
+
} elseif ( $this->is_multidimensional_aggregated ) {
775
+
$root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
776
+
$value = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default );
777
+
778
+
// Ensure that the post value is used if the setting is previewed, since preview filters aren't applying on cached $root_value.
779
+
if ( $this->is_previewed ) {
780
+
$value = $this->post_value( $value );
781
+
}
782
+
} else {
783
+
$value = $this->get_root_value( $this->default );
784
+
}
785
+
return $value;
786
+
}
787
+
788
+
/**
789
+
* Sanitize the setting's value for use in JavaScript.
790
+
*
791
+
* @since 3.4.0
792
+
*
793
+
* @return mixed The requested escaped value.
794
+
*/
795
+
public function js_value() {
796
+
797
+
/**
798
+
* Filters a Customize setting value for use in JavaScript.
799
+
*
800
+
* The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
801
+
*
802
+
* @since 3.4.0
803
+
*
804
+
* @param mixed $value The setting value.
805
+
* @param WP_Customize_Setting $setting WP_Customize_Setting instance.
806
+
*/
807
+
$value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this );
808
+
809
+
if ( is_string( $value ) ) {
810
+
return html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
811
+
}
812
+
813
+
return $value;
814
+
}
815
+
816
+
/**
817
+
* Retrieves the data to export to the client via JSON.
818
+
*
819
+
* @since 4.6.0
820
+
*
821
+
* @return array Array of parameters passed to JavaScript.
822
+
*/
823
+
public function json() {
824
+
return array(
825
+
'value' => $this->js_value(),
826
+
'transport' => $this->transport,
827
+
'dirty' => $this->dirty,
828
+
'type' => $this->type,
829
+
);
830
+
}
831
+
832
+
/**
833
+
* Validate user capabilities whether the theme supports the setting.
834
+
*
835
+
* @since 3.4.0
836
+
*
837
+
* @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
838
+
*/
839
+
final public function check_capabilities() {
840
+
if ( $this->capability && ! current_user_can( $this->capability ) ) {
841
+
return false;
842
+
}
843
+
844
+
if ( $this->theme_supports && ! current_theme_supports( ...(array) $this->theme_supports ) ) {
845
+
return false;
846
+
}
847
+
848
+
return true;
849
+
}
850
+
851
+
/**
852
+
* Multidimensional helper function.
853
+
*
854
+
* @since 3.4.0
855
+
*
856
+
* @param array $root
857
+
* @param array $keys
858
+
* @param bool $create Default false.
859
+
* @return array|void Keys are 'root', 'node', and 'key'.
860
+
*/
861
+
final protected function multidimensional( &$root, $keys, $create = false ) {
862
+
if ( $create && empty( $root ) ) {
863
+
$root = array();
864
+
}
865
+
866
+
if ( ! isset( $root ) || empty( $keys ) ) {
867
+
return;
868
+
}
869
+
870
+
$last = array_pop( $keys );
871
+
$node = &$root;
872
+
873
+
foreach ( $keys as $key ) {
874
+
if ( $create && ! isset( $node[ $key ] ) ) {
875
+
$node[ $key ] = array();
876
+
}
877
+
878
+
if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) {
879
+
return;
880
+
}
881
+
882
+
$node = &$node[ $key ];
883
+
}
884
+
885
+
if ( $create ) {
886
+
if ( ! is_array( $node ) ) {
887
+
// Account for an array overriding a string or object value.
888
+
$node = array();
889
+
}
890
+
if ( ! isset( $node[ $last ] ) ) {
891
+
$node[ $last ] = array();
892
+
}
893
+
}
894
+
895
+
if ( ! isset( $node[ $last ] ) ) {
896
+
return;
897
+
}
898
+
899
+
return array(
900
+
'root' => &$root,
901
+
'node' => &$node,
902
+
'key' => $last,
903
+
);
904
+
}
905
+
906
+
/**
907
+
* Will attempt to replace a specific value in a multidimensional array.
908
+
*
909
+
* @since 3.4.0
910
+
*
911
+
* @param array $root
912
+
* @param array $keys
913
+
* @param mixed $value The value to update.
914
+
* @return mixed
915
+
*/
916
+
final protected function multidimensional_replace( $root, $keys, $value ) {
917
+
if ( ! isset( $value ) ) {
918
+
return $root;
919
+
} elseif ( empty( $keys ) ) { // If there are no keys, we're replacing the root.
920
+
return $value;
921
+
}
922
+
923
+
$result = $this->multidimensional( $root, $keys, true );
924
+
925
+
if ( isset( $result ) ) {
926
+
$result['node'][ $result['key'] ] = $value;
927
+
}
928
+
929
+
return $root;
930
+
}
931
+
932
+
/**
933
+
* Will attempt to fetch a specific value from a multidimensional array.
934
+
*
935
+
* @since 3.4.0
936
+
*
937
+
* @param array $root
938
+
* @param array $keys
939
+
* @param mixed $default_value A default value which is used as a fallback. Default null.
940
+
* @return mixed The requested value or the default value.
941
+
*/
942
+
final protected function multidimensional_get( $root, $keys, $default_value = null ) {
943
+
if ( empty( $keys ) ) { // If there are no keys, test the root.
944
+
return isset( $root ) ? $root : $default_value;
945
+
}
946
+
947
+
$result = $this->multidimensional( $root, $keys );
948
+
return isset( $result ) ? $result['node'][ $result['key'] ] : $default_value;
949
+
}
950
+
951
+
/**
952
+
* Will attempt to check if a specific value in a multidimensional array is set.
953
+
*
954
+
* @since 3.4.0
955
+
*
956
+
* @param array $root
957
+
* @param array $keys
958
+
* @return bool True if value is set, false if not.
959
+
*/
960
+
final protected function multidimensional_isset( $root, $keys ) {
961
+
$result = $this->multidimensional_get( $root, $keys );
962
+
return isset( $result );
963
+
}
964
+
}
965
+
966
+
/**
967
+
* WP_Customize_Filter_Setting class.
968
+
*/
969
+
require_once ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php';
970
+
971
+
/**
972
+
* WP_Customize_Header_Image_Setting class.
973
+
*/
974
+
require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php';
975
+
976
+
/**
977
+
* WP_Customize_Background_Image_Setting class.
978
+
*/
979
+
require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php';
980
+
981
+
/**
982
+
* WP_Customize_Nav_Menu_Item_Setting class.
983
+
*/
984
+
require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php';
985
+
986
+
/**
987
+
* WP_Customize_Nav_Menu_Setting class.
988
+
*/
989
+
require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php';
990
+