18
|
1 |
<?php |
|
2 |
/** |
|
3 |
* WP_Theme_JSON class |
|
4 |
* |
|
5 |
* @package WordPress |
|
6 |
* @subpackage Theme |
|
7 |
* @since 5.8.0 |
|
8 |
*/ |
|
9 |
|
|
10 |
/** |
|
11 |
* Class that encapsulates the processing of structures that adhere to the theme.json spec. |
|
12 |
* |
|
13 |
* @access private |
|
14 |
*/ |
|
15 |
class WP_Theme_JSON { |
|
16 |
|
|
17 |
/** |
|
18 |
* Container of data in theme.json format. |
|
19 |
* |
|
20 |
* @since 5.8.0 |
|
21 |
* @var array |
|
22 |
*/ |
|
23 |
private $theme_json = null; |
|
24 |
|
|
25 |
/** |
|
26 |
* Holds block metadata extracted from block.json |
|
27 |
* to be shared among all instances so we don't |
|
28 |
* process it twice. |
|
29 |
* |
|
30 |
* @since 5.8.0 |
|
31 |
* @var array |
|
32 |
*/ |
|
33 |
private static $blocks_metadata = null; |
|
34 |
|
|
35 |
/** |
|
36 |
* The CSS selector for the top-level styles. |
|
37 |
* |
|
38 |
* @since 5.8.0 |
|
39 |
* @var string |
|
40 |
*/ |
|
41 |
const ROOT_BLOCK_SELECTOR = 'body'; |
|
42 |
|
|
43 |
/** |
|
44 |
* The sources of data this object can represent. |
|
45 |
* |
|
46 |
* @since 5.8.0 |
|
47 |
* @var array |
|
48 |
*/ |
|
49 |
const VALID_ORIGINS = array( |
|
50 |
'core', |
|
51 |
'theme', |
|
52 |
'user', |
|
53 |
); |
|
54 |
|
|
55 |
/** |
|
56 |
* Presets are a set of values that serve |
|
57 |
* to bootstrap some styles: colors, font sizes, etc. |
|
58 |
* |
|
59 |
* They are a unkeyed array of values such as: |
|
60 |
* |
|
61 |
* ```php |
|
62 |
* array( |
|
63 |
* array( |
|
64 |
* 'slug' => 'unique-name-within-the-set', |
|
65 |
* 'name' => 'Name for the UI', |
|
66 |
* <value_key> => 'value' |
|
67 |
* ), |
|
68 |
* ) |
|
69 |
* ``` |
|
70 |
* |
|
71 |
* This contains the necessary metadata to process them: |
|
72 |
* |
|
73 |
* - path => where to find the preset within the settings section |
|
74 |
* |
|
75 |
* - value_key => the key that represents the value |
|
76 |
* |
|
77 |
* - css_var_infix => infix to use in generating the CSS Custom Property. Example: |
|
78 |
* --wp--preset--<preset_infix>--<slug>: <preset_value> |
|
79 |
* |
|
80 |
* - classes => array containing a structure with the classes to |
|
81 |
* generate for the presets. Each class should have |
|
82 |
* the class suffix and the property name. Example: |
|
83 |
* |
|
84 |
* .has-<slug>-<class_suffix> { |
|
85 |
* <property_name>: <preset_value> |
|
86 |
* } |
|
87 |
* |
|
88 |
* @since 5.8.0 |
|
89 |
* @var array |
|
90 |
*/ |
|
91 |
const PRESETS_METADATA = array( |
|
92 |
array( |
|
93 |
'path' => array( 'color', 'palette' ), |
|
94 |
'value_key' => 'color', |
|
95 |
'css_var_infix' => 'color', |
|
96 |
'classes' => array( |
|
97 |
array( |
|
98 |
'class_suffix' => 'color', |
|
99 |
'property_name' => 'color', |
|
100 |
), |
|
101 |
array( |
|
102 |
'class_suffix' => 'background-color', |
|
103 |
'property_name' => 'background-color', |
|
104 |
), |
|
105 |
), |
|
106 |
), |
|
107 |
array( |
|
108 |
'path' => array( 'color', 'gradients' ), |
|
109 |
'value_key' => 'gradient', |
|
110 |
'css_var_infix' => 'gradient', |
|
111 |
'classes' => array( |
|
112 |
array( |
|
113 |
'class_suffix' => 'gradient-background', |
|
114 |
'property_name' => 'background', |
|
115 |
), |
|
116 |
), |
|
117 |
), |
|
118 |
array( |
|
119 |
'path' => array( 'typography', 'fontSizes' ), |
|
120 |
'value_key' => 'size', |
|
121 |
'css_var_infix' => 'font-size', |
|
122 |
'classes' => array( |
|
123 |
array( |
|
124 |
'class_suffix' => 'font-size', |
|
125 |
'property_name' => 'font-size', |
|
126 |
), |
|
127 |
), |
|
128 |
), |
|
129 |
); |
|
130 |
|
|
131 |
/** |
|
132 |
* Metadata for style properties. |
|
133 |
* |
|
134 |
* Each property declares: |
|
135 |
* |
|
136 |
* - 'value': path to the value in theme.json and block attributes. |
|
137 |
* |
|
138 |
* @since 5.8.0 |
|
139 |
* @var array |
|
140 |
*/ |
|
141 |
const PROPERTIES_METADATA = array( |
|
142 |
'background' => array( |
|
143 |
'value' => array( 'color', 'gradient' ), |
|
144 |
), |
|
145 |
'background-color' => array( |
|
146 |
'value' => array( 'color', 'background' ), |
|
147 |
), |
|
148 |
'color' => array( |
|
149 |
'value' => array( 'color', 'text' ), |
|
150 |
), |
|
151 |
'font-size' => array( |
|
152 |
'value' => array( 'typography', 'fontSize' ), |
|
153 |
), |
|
154 |
'line-height' => array( |
|
155 |
'value' => array( 'typography', 'lineHeight' ), |
|
156 |
), |
|
157 |
'margin' => array( |
|
158 |
'value' => array( 'spacing', 'margin' ), |
|
159 |
'properties' => array( 'top', 'right', 'bottom', 'left' ), |
|
160 |
), |
|
161 |
'padding' => array( |
|
162 |
'value' => array( 'spacing', 'padding' ), |
|
163 |
'properties' => array( 'top', 'right', 'bottom', 'left' ), |
|
164 |
), |
|
165 |
); |
|
166 |
|
|
167 |
/** |
|
168 |
* @since 5.8.0 |
|
169 |
* @var array |
|
170 |
*/ |
|
171 |
const ALLOWED_TOP_LEVEL_KEYS = array( |
|
172 |
'settings', |
|
173 |
'styles', |
|
174 |
'version', |
|
175 |
); |
|
176 |
|
|
177 |
/** |
|
178 |
* @since 5.8.0 |
|
179 |
* @var array |
|
180 |
*/ |
|
181 |
const ALLOWED_SETTINGS = array( |
|
182 |
'border' => array( |
|
183 |
'customRadius' => null, |
|
184 |
), |
|
185 |
'color' => array( |
|
186 |
'custom' => null, |
|
187 |
'customDuotone' => null, |
|
188 |
'customGradient' => null, |
|
189 |
'duotone' => null, |
|
190 |
'gradients' => null, |
|
191 |
'link' => null, |
|
192 |
'palette' => null, |
|
193 |
), |
|
194 |
'custom' => null, |
|
195 |
'layout' => array( |
|
196 |
'contentSize' => null, |
|
197 |
'wideSize' => null, |
|
198 |
), |
|
199 |
'spacing' => array( |
|
200 |
'customMargin' => null, |
|
201 |
'customPadding' => null, |
|
202 |
'units' => null, |
|
203 |
), |
|
204 |
'typography' => array( |
|
205 |
'customFontSize' => null, |
|
206 |
'customLineHeight' => null, |
|
207 |
'dropCap' => null, |
|
208 |
'fontSizes' => null, |
|
209 |
), |
|
210 |
); |
|
211 |
|
|
212 |
/** |
|
213 |
* @since 5.8.0 |
|
214 |
* @var array |
|
215 |
*/ |
|
216 |
const ALLOWED_STYLES = array( |
|
217 |
'border' => array( |
|
218 |
'radius' => null, |
|
219 |
), |
|
220 |
'color' => array( |
|
221 |
'background' => null, |
|
222 |
'gradient' => null, |
|
223 |
'text' => null, |
|
224 |
), |
|
225 |
'spacing' => array( |
|
226 |
'margin' => array( |
|
227 |
'top' => null, |
|
228 |
'right' => null, |
|
229 |
'bottom' => null, |
|
230 |
'left' => null, |
|
231 |
), |
|
232 |
'padding' => array( |
|
233 |
'bottom' => null, |
|
234 |
'left' => null, |
|
235 |
'right' => null, |
|
236 |
'top' => null, |
|
237 |
), |
|
238 |
), |
|
239 |
'typography' => array( |
|
240 |
'fontSize' => null, |
|
241 |
'lineHeight' => null, |
|
242 |
), |
|
243 |
); |
|
244 |
|
|
245 |
/** |
|
246 |
* @since 5.8.0 |
|
247 |
* @var array |
|
248 |
*/ |
|
249 |
const ELEMENTS = array( |
|
250 |
'link' => 'a', |
|
251 |
'h1' => 'h1', |
|
252 |
'h2' => 'h2', |
|
253 |
'h3' => 'h3', |
|
254 |
'h4' => 'h4', |
|
255 |
'h5' => 'h5', |
|
256 |
'h6' => 'h6', |
|
257 |
); |
|
258 |
|
|
259 |
/** |
|
260 |
* @since 5.8.0 |
|
261 |
* @var int |
|
262 |
*/ |
|
263 |
const LATEST_SCHEMA = 1; |
|
264 |
|
|
265 |
/** |
|
266 |
* Constructor. |
|
267 |
* |
|
268 |
* @since 5.8.0 |
|
269 |
* |
|
270 |
* @param array $theme_json A structure that follows the theme.json schema. |
|
271 |
* @param string $origin Optional. What source of data this object represents. |
|
272 |
* One of 'core', 'theme', or 'user'. Default 'theme'. |
|
273 |
*/ |
|
274 |
public function __construct( $theme_json = array(), $origin = 'theme' ) { |
|
275 |
if ( ! in_array( $origin, self::VALID_ORIGINS, true ) ) { |
|
276 |
$origin = 'theme'; |
|
277 |
} |
|
278 |
|
|
279 |
if ( ! isset( $theme_json['version'] ) || self::LATEST_SCHEMA !== $theme_json['version'] ) { |
|
280 |
$this->theme_json = array(); |
|
281 |
return; |
|
282 |
} |
|
283 |
|
|
284 |
$this->theme_json = self::sanitize( $theme_json ); |
|
285 |
|
|
286 |
// Internally, presets are keyed by origin. |
|
287 |
$nodes = self::get_setting_nodes( $this->theme_json ); |
|
288 |
foreach ( $nodes as $node ) { |
|
289 |
foreach ( self::PRESETS_METADATA as $preset ) { |
|
290 |
$path = array_merge( $node['path'], $preset['path'] ); |
|
291 |
$preset = _wp_array_get( $this->theme_json, $path, null ); |
|
292 |
if ( null !== $preset ) { |
|
293 |
_wp_array_set( $this->theme_json, $path, array( $origin => $preset ) ); |
|
294 |
} |
|
295 |
} |
|
296 |
} |
|
297 |
} |
|
298 |
|
|
299 |
/** |
|
300 |
* Sanitizes the input according to the schemas. |
|
301 |
* |
|
302 |
* @since 5.8.0 |
|
303 |
* |
|
304 |
* @param array $input Structure to sanitize. |
|
305 |
* @return array The sanitized output. |
|
306 |
*/ |
|
307 |
private static function sanitize( $input ) { |
|
308 |
$output = array(); |
|
309 |
|
|
310 |
if ( ! is_array( $input ) ) { |
|
311 |
return $output; |
|
312 |
} |
|
313 |
|
|
314 |
$allowed_top_level_keys = self::ALLOWED_TOP_LEVEL_KEYS; |
|
315 |
$allowed_settings = self::ALLOWED_SETTINGS; |
|
316 |
$allowed_styles = self::ALLOWED_STYLES; |
|
317 |
$allowed_blocks = array_keys( self::get_blocks_metadata() ); |
|
318 |
$allowed_elements = array_keys( self::ELEMENTS ); |
|
319 |
|
|
320 |
$output = array_intersect_key( $input, array_flip( $allowed_top_level_keys ) ); |
|
321 |
|
|
322 |
// Build the schema. |
|
323 |
$schema = array(); |
|
324 |
$schema_styles_elements = array(); |
|
325 |
foreach ( $allowed_elements as $element ) { |
|
326 |
$schema_styles_elements[ $element ] = $allowed_styles; |
|
327 |
} |
|
328 |
$schema_styles_blocks = array(); |
|
329 |
$schema_settings_blocks = array(); |
|
330 |
foreach ( $allowed_blocks as $block ) { |
|
331 |
$schema_settings_blocks[ $block ] = $allowed_settings; |
|
332 |
$schema_styles_blocks[ $block ] = $allowed_styles; |
|
333 |
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; |
|
334 |
} |
|
335 |
$schema['styles'] = $allowed_styles; |
|
336 |
$schema['styles']['blocks'] = $schema_styles_blocks; |
|
337 |
$schema['styles']['elements'] = $schema_styles_elements; |
|
338 |
$schema['settings'] = $allowed_settings; |
|
339 |
$schema['settings']['blocks'] = $schema_settings_blocks; |
|
340 |
|
|
341 |
// Remove anything that's not present in the schema. |
|
342 |
foreach ( array( 'styles', 'settings' ) as $subtree ) { |
|
343 |
if ( ! isset( $input[ $subtree ] ) ) { |
|
344 |
continue; |
|
345 |
} |
|
346 |
|
|
347 |
if ( ! is_array( $input[ $subtree ] ) ) { |
|
348 |
unset( $output[ $subtree ] ); |
|
349 |
continue; |
|
350 |
} |
|
351 |
|
|
352 |
$result = self::remove_keys_not_in_schema( $input[ $subtree ], $schema[ $subtree ] ); |
|
353 |
|
|
354 |
if ( empty( $result ) ) { |
|
355 |
unset( $output[ $subtree ] ); |
|
356 |
} else { |
|
357 |
$output[ $subtree ] = $result; |
|
358 |
} |
|
359 |
} |
|
360 |
|
|
361 |
return $output; |
|
362 |
} |
|
363 |
|
|
364 |
/** |
|
365 |
* Returns the metadata for each block. |
|
366 |
* |
|
367 |
* Example: |
|
368 |
* |
|
369 |
* { |
|
370 |
* 'core/paragraph': { |
|
371 |
* 'selector': 'p', |
|
372 |
* 'elements': { |
|
373 |
* 'link' => 'link selector', |
|
374 |
* 'etc' => 'element selector' |
|
375 |
* } |
|
376 |
* }, |
|
377 |
* 'core/heading': { |
|
378 |
* 'selector': 'h1', |
|
379 |
* 'elements': {} |
|
380 |
* } |
|
381 |
* 'core/group': { |
|
382 |
* 'selector': '.wp-block-group', |
|
383 |
* 'elements': {} |
|
384 |
* } |
|
385 |
* } |
|
386 |
* |
|
387 |
* @since 5.8.0 |
|
388 |
* |
|
389 |
* @return array Block metadata. |
|
390 |
*/ |
|
391 |
private static function get_blocks_metadata() { |
|
392 |
if ( null !== self::$blocks_metadata ) { |
|
393 |
return self::$blocks_metadata; |
|
394 |
} |
|
395 |
|
|
396 |
self::$blocks_metadata = array(); |
|
397 |
|
|
398 |
$registry = WP_Block_Type_Registry::get_instance(); |
|
399 |
$blocks = $registry->get_all_registered(); |
|
400 |
foreach ( $blocks as $block_name => $block_type ) { |
|
401 |
if ( |
|
402 |
isset( $block_type->supports['__experimentalSelector'] ) && |
|
403 |
is_string( $block_type->supports['__experimentalSelector'] ) |
|
404 |
) { |
|
405 |
self::$blocks_metadata[ $block_name ]['selector'] = $block_type->supports['__experimentalSelector']; |
|
406 |
} else { |
|
407 |
self::$blocks_metadata[ $block_name ]['selector'] = '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) ); |
|
408 |
} |
|
409 |
|
|
410 |
/* |
|
411 |
* Assign defaults, then overwrite those that the block sets by itself. |
|
412 |
* If the block selector is compounded, will append the element to each |
|
413 |
* individual block selector. |
|
414 |
*/ |
|
415 |
$block_selectors = explode( ',', self::$blocks_metadata[ $block_name ]['selector'] ); |
|
416 |
foreach ( self::ELEMENTS as $el_name => $el_selector ) { |
|
417 |
$element_selector = array(); |
|
418 |
foreach ( $block_selectors as $selector ) { |
|
419 |
$element_selector[] = $selector . ' ' . $el_selector; |
|
420 |
} |
|
421 |
self::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector ); |
|
422 |
} |
|
423 |
} |
|
424 |
|
|
425 |
return self::$blocks_metadata; |
|
426 |
} |
|
427 |
|
|
428 |
/** |
|
429 |
* Given a tree, removes the keys that are not present in the schema. |
|
430 |
* |
|
431 |
* It is recursive and modifies the input in-place. |
|
432 |
* |
|
433 |
* @since 5.8.0 |
|
434 |
* |
|
435 |
* @param array $tree Input to process. |
|
436 |
* @param array $schema Schema to adhere to. |
|
437 |
* @return array Returns the modified $tree. |
|
438 |
*/ |
|
439 |
private static function remove_keys_not_in_schema( $tree, $schema ) { |
|
440 |
$tree = array_intersect_key( $tree, $schema ); |
|
441 |
|
|
442 |
foreach ( $schema as $key => $data ) { |
|
443 |
if ( ! isset( $tree[ $key ] ) ) { |
|
444 |
continue; |
|
445 |
} |
|
446 |
|
|
447 |
if ( is_array( $schema[ $key ] ) && is_array( $tree[ $key ] ) ) { |
|
448 |
$tree[ $key ] = self::remove_keys_not_in_schema( $tree[ $key ], $schema[ $key ] ); |
|
449 |
|
|
450 |
if ( empty( $tree[ $key ] ) ) { |
|
451 |
unset( $tree[ $key ] ); |
|
452 |
} |
|
453 |
} elseif ( is_array( $schema[ $key ] ) && ! is_array( $tree[ $key ] ) ) { |
|
454 |
unset( $tree[ $key ] ); |
|
455 |
} |
|
456 |
} |
|
457 |
|
|
458 |
return $tree; |
|
459 |
} |
|
460 |
|
|
461 |
/** |
|
462 |
* Returns the existing settings for each block. |
|
463 |
* |
|
464 |
* Example: |
|
465 |
* |
|
466 |
* { |
|
467 |
* 'root': { |
|
468 |
* 'color': { |
|
469 |
* 'custom': true |
|
470 |
* } |
|
471 |
* }, |
|
472 |
* 'core/paragraph': { |
|
473 |
* 'spacing': { |
|
474 |
* 'customPadding': true |
|
475 |
* } |
|
476 |
* } |
|
477 |
* } |
|
478 |
* |
|
479 |
* @since 5.8.0 |
|
480 |
* |
|
481 |
* @return array Settings per block. |
|
482 |
*/ |
|
483 |
public function get_settings() { |
|
484 |
if ( ! isset( $this->theme_json['settings'] ) ) { |
|
485 |
return array(); |
|
486 |
} else { |
|
487 |
return $this->theme_json['settings']; |
|
488 |
} |
|
489 |
} |
|
490 |
|
|
491 |
/** |
|
492 |
* Returns the stylesheet that results of processing |
|
493 |
* the theme.json structure this object represents. |
|
494 |
* |
|
495 |
* @since 5.8.0 |
|
496 |
* |
|
497 |
* @param string $type Optional. Type of stylesheet we want. Accepts 'all', |
|
498 |
* 'block_styles', and 'css_variables'. Default 'all'. |
|
499 |
* @return string Stylesheet. |
|
500 |
*/ |
|
501 |
public function get_stylesheet( $type = 'all' ) { |
|
502 |
$blocks_metadata = self::get_blocks_metadata(); |
|
503 |
$style_nodes = self::get_style_nodes( $this->theme_json, $blocks_metadata ); |
|
504 |
$setting_nodes = self::get_setting_nodes( $this->theme_json, $blocks_metadata ); |
|
505 |
|
|
506 |
switch ( $type ) { |
|
507 |
case 'block_styles': |
|
508 |
return $this->get_block_styles( $style_nodes, $setting_nodes ); |
|
509 |
case 'css_variables': |
|
510 |
return $this->get_css_variables( $setting_nodes ); |
|
511 |
default: |
|
512 |
return $this->get_css_variables( $setting_nodes ) . $this->get_block_styles( $style_nodes, $setting_nodes ); |
|
513 |
} |
|
514 |
|
|
515 |
} |
|
516 |
|
|
517 |
/** |
|
518 |
* Converts each style section into a list of rulesets |
|
519 |
* containing the block styles to be appended to the stylesheet. |
|
520 |
* |
|
521 |
* See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax |
|
522 |
* |
|
523 |
* For each section this creates a new ruleset such as: |
|
524 |
* |
|
525 |
* block-selector { |
|
526 |
* style-property-one: value; |
|
527 |
* } |
|
528 |
* |
|
529 |
* Additionally, it'll also create new rulesets |
|
530 |
* as classes for each preset value such as: |
|
531 |
* |
|
532 |
* .has-value-color { |
|
533 |
* color: value; |
|
534 |
* } |
|
535 |
* |
|
536 |
* .has-value-background-color { |
|
537 |
* background-color: value; |
|
538 |
* } |
|
539 |
* |
|
540 |
* .has-value-font-size { |
|
541 |
* font-size: value; |
|
542 |
* } |
|
543 |
* |
|
544 |
* .has-value-gradient-background { |
|
545 |
* background: value; |
|
546 |
* } |
|
547 |
* |
|
548 |
* p.has-value-gradient-background { |
|
549 |
* background: value; |
|
550 |
* } |
|
551 |
* |
|
552 |
* @since 5.8.0 |
|
553 |
* |
|
554 |
* @param array $style_nodes Nodes with styles. |
|
555 |
* @param array $setting_nodes Nodes with settings. |
|
556 |
* @return string The new stylesheet. |
|
557 |
*/ |
|
558 |
private function get_block_styles( $style_nodes, $setting_nodes ) { |
|
559 |
$block_rules = ''; |
|
560 |
foreach ( $style_nodes as $metadata ) { |
|
561 |
if ( null === $metadata['selector'] ) { |
|
562 |
continue; |
|
563 |
} |
|
564 |
|
|
565 |
$node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); |
|
566 |
$selector = $metadata['selector']; |
|
567 |
$declarations = self::compute_style_properties( $node ); |
|
568 |
$block_rules .= self::to_ruleset( $selector, $declarations ); |
|
569 |
} |
|
570 |
|
|
571 |
$preset_rules = ''; |
|
572 |
foreach ( $setting_nodes as $metadata ) { |
|
573 |
if ( null === $metadata['selector'] ) { |
|
574 |
continue; |
|
575 |
} |
|
576 |
|
|
577 |
$selector = $metadata['selector']; |
|
578 |
$node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); |
|
579 |
$preset_rules .= self::compute_preset_classes( $node, $selector ); |
|
580 |
} |
|
581 |
|
|
582 |
return $block_rules . $preset_rules; |
|
583 |
} |
|
584 |
|
|
585 |
/** |
|
586 |
* Converts each styles section into a list of rulesets |
|
587 |
* to be appended to the stylesheet. |
|
588 |
* These rulesets contain all the css variables (custom variables and preset variables). |
|
589 |
* |
|
590 |
* See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax |
|
591 |
* |
|
592 |
* For each section this creates a new ruleset such as: |
|
593 |
* |
|
594 |
* block-selector { |
|
595 |
* --wp--preset--category--slug: value; |
|
596 |
* --wp--custom--variable: value; |
|
597 |
* } |
|
598 |
* |
|
599 |
* @since 5.8.0 |
|
600 |
* |
|
601 |
* @param array $nodes Nodes with settings. |
|
602 |
* @return string The new stylesheet. |
|
603 |
*/ |
|
604 |
private function get_css_variables( $nodes ) { |
|
605 |
$stylesheet = ''; |
|
606 |
foreach ( $nodes as $metadata ) { |
|
607 |
if ( null === $metadata['selector'] ) { |
|
608 |
continue; |
|
609 |
} |
|
610 |
|
|
611 |
$selector = $metadata['selector']; |
|
612 |
|
|
613 |
$node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); |
|
614 |
$declarations = array_merge( self::compute_preset_vars( $node ), self::compute_theme_vars( $node ) ); |
|
615 |
|
|
616 |
$stylesheet .= self::to_ruleset( $selector, $declarations ); |
|
617 |
} |
|
618 |
|
|
619 |
return $stylesheet; |
|
620 |
} |
|
621 |
|
|
622 |
/** |
|
623 |
* Given a selector and a declaration list, |
|
624 |
* creates the corresponding ruleset. |
|
625 |
* |
|
626 |
* @since 5.8.0 |
|
627 |
* |
|
628 |
* @param string $selector CSS selector. |
|
629 |
* @param array $declarations List of declarations. |
|
630 |
* @return string CSS ruleset. |
|
631 |
*/ |
|
632 |
private static function to_ruleset( $selector, $declarations ) { |
|
633 |
if ( empty( $declarations ) ) { |
|
634 |
return ''; |
|
635 |
} |
|
636 |
|
|
637 |
$declaration_block = array_reduce( |
|
638 |
$declarations, |
|
639 |
function ( $carry, $element ) { |
|
640 |
return $carry .= $element['name'] . ': ' . $element['value'] . ';'; }, |
|
641 |
'' |
|
642 |
); |
|
643 |
|
|
644 |
return $selector . '{' . $declaration_block . '}'; |
|
645 |
} |
|
646 |
|
|
647 |
/** |
|
648 |
* Function that appends a sub-selector to a existing one. |
|
649 |
* |
|
650 |
* Given the compounded $selector "h1, h2, h3" |
|
651 |
* and the $to_append selector ".some-class" the result will be |
|
652 |
* "h1.some-class, h2.some-class, h3.some-class". |
|
653 |
* |
|
654 |
* @since 5.8.0 |
|
655 |
* |
|
656 |
* @param string $selector Original selector. |
|
657 |
* @param string $to_append Selector to append. |
|
658 |
* @return string |
|
659 |
*/ |
|
660 |
private static function append_to_selector( $selector, $to_append ) { |
|
661 |
$new_selectors = array(); |
|
662 |
$selectors = explode( ',', $selector ); |
|
663 |
foreach ( $selectors as $sel ) { |
|
664 |
$new_selectors[] = $sel . $to_append; |
|
665 |
} |
|
666 |
|
|
667 |
return implode( ',', $new_selectors ); |
|
668 |
} |
|
669 |
|
|
670 |
/** |
|
671 |
* Given an array of presets keyed by origin and the value key of the preset, |
|
672 |
* it returns an array where each key is the preset slug and each value the preset value. |
|
673 |
* |
|
674 |
* @since 5.8.0 |
|
675 |
* |
|
676 |
* @param array $preset_per_origin Array of presets keyed by origin. |
|
677 |
* @param string $value_key The property of the preset that contains its value. |
|
678 |
* @return array Array of presets where each key is a slug and each value is the preset value. |
|
679 |
*/ |
|
680 |
private static function get_merged_preset_by_slug( $preset_per_origin, $value_key ) { |
|
681 |
$result = array(); |
|
682 |
foreach ( self::VALID_ORIGINS as $origin ) { |
|
683 |
if ( ! isset( $preset_per_origin[ $origin ] ) ) { |
|
684 |
continue; |
|
685 |
} |
|
686 |
foreach ( $preset_per_origin[ $origin ] as $preset ) { |
|
687 |
/* |
|
688 |
* We don't want to use kebabCase here, |
|
689 |
* see https://github.com/WordPress/gutenberg/issues/32347 |
|
690 |
* However, we need to make sure the generated class or CSS variable |
|
691 |
* doesn't contain spaces. |
|
692 |
*/ |
|
693 |
$result[ preg_replace( '/\s+/', '-', $preset['slug'] ) ] = $preset[ $value_key ]; |
|
694 |
} |
|
695 |
} |
|
696 |
return $result; |
|
697 |
} |
|
698 |
|
|
699 |
/** |
|
700 |
* Given a settings array, it returns the generated rulesets |
|
701 |
* for the preset classes. |
|
702 |
* |
|
703 |
* @since 5.8.0 |
|
704 |
* |
|
705 |
* @param array $settings Settings to process. |
|
706 |
* @param string $selector Selector wrapping the classes. |
|
707 |
* @return string The result of processing the presets. |
|
708 |
*/ |
|
709 |
private static function compute_preset_classes( $settings, $selector ) { |
|
710 |
if ( self::ROOT_BLOCK_SELECTOR === $selector ) { |
|
711 |
// Classes at the global level do not need any CSS prefixed, |
|
712 |
// and we don't want to increase its specificity. |
|
713 |
$selector = ''; |
|
714 |
} |
|
715 |
|
|
716 |
$stylesheet = ''; |
|
717 |
foreach ( self::PRESETS_METADATA as $preset ) { |
|
718 |
$preset_per_origin = _wp_array_get( $settings, $preset['path'], array() ); |
|
719 |
$preset_by_slug = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] ); |
|
720 |
foreach ( $preset['classes'] as $class ) { |
|
721 |
foreach ( $preset_by_slug as $slug => $value ) { |
|
722 |
$stylesheet .= self::to_ruleset( |
|
723 |
self::append_to_selector( $selector, '.has-' . _wp_to_kebab_case( $slug ) . '-' . $class['class_suffix'] ), |
|
724 |
array( |
|
725 |
array( |
|
726 |
'name' => $class['property_name'], |
|
727 |
'value' => 'var(--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ) . ') !important', |
|
728 |
), |
|
729 |
) |
|
730 |
); |
|
731 |
} |
|
732 |
} |
|
733 |
} |
|
734 |
|
|
735 |
return $stylesheet; |
|
736 |
} |
|
737 |
|
|
738 |
/** |
|
739 |
* Given the block settings, it extracts the CSS Custom Properties |
|
740 |
* for the presets and adds them to the $declarations array |
|
741 |
* following the format: |
|
742 |
* |
|
743 |
* array( |
|
744 |
* 'name' => 'property_name', |
|
745 |
* 'value' => 'property_value, |
|
746 |
* ) |
|
747 |
* |
|
748 |
* @since 5.8.0 |
|
749 |
* |
|
750 |
* @param array $settings Settings to process. |
|
751 |
* @return array Returns the modified $declarations. |
|
752 |
*/ |
|
753 |
private static function compute_preset_vars( $settings ) { |
|
754 |
$declarations = array(); |
|
755 |
foreach ( self::PRESETS_METADATA as $preset ) { |
|
756 |
$preset_per_origin = _wp_array_get( $settings, $preset['path'], array() ); |
|
757 |
$preset_by_slug = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] ); |
|
758 |
foreach ( $preset_by_slug as $slug => $value ) { |
|
759 |
$declarations[] = array( |
|
760 |
'name' => '--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ), |
|
761 |
'value' => $value, |
|
762 |
); |
|
763 |
} |
|
764 |
} |
|
765 |
|
|
766 |
return $declarations; |
|
767 |
} |
|
768 |
|
|
769 |
/** |
|
770 |
* Given an array of settings, it extracts the CSS Custom Properties |
|
771 |
* for the custom values and adds them to the $declarations |
|
772 |
* array following the format: |
|
773 |
* |
|
774 |
* array( |
|
775 |
* 'name' => 'property_name', |
|
776 |
* 'value' => 'property_value, |
|
777 |
* ) |
|
778 |
* |
|
779 |
* @since 5.8.0 |
|
780 |
* |
|
781 |
* @param array $settings Settings to process. |
|
782 |
* @return array Returns the modified $declarations. |
|
783 |
*/ |
|
784 |
private static function compute_theme_vars( $settings ) { |
|
785 |
$declarations = array(); |
|
786 |
$custom_values = _wp_array_get( $settings, array( 'custom' ), array() ); |
|
787 |
$css_vars = self::flatten_tree( $custom_values ); |
|
788 |
foreach ( $css_vars as $key => $value ) { |
|
789 |
$declarations[] = array( |
|
790 |
'name' => '--wp--custom--' . $key, |
|
791 |
'value' => $value, |
|
792 |
); |
|
793 |
} |
|
794 |
|
|
795 |
return $declarations; |
|
796 |
} |
|
797 |
|
|
798 |
/** |
|
799 |
* Given a tree, it creates a flattened one |
|
800 |
* by merging the keys and binding the leaf values |
|
801 |
* to the new keys. |
|
802 |
* |
|
803 |
* It also transforms camelCase names into kebab-case |
|
804 |
* and substitutes '/' by '-'. |
|
805 |
* |
|
806 |
* This is thought to be useful to generate |
|
807 |
* CSS Custom Properties from a tree, |
|
808 |
* although there's nothing in the implementation |
|
809 |
* of this function that requires that format. |
|
810 |
* |
|
811 |
* For example, assuming the given prefix is '--wp' |
|
812 |
* and the token is '--', for this input tree: |
|
813 |
* |
|
814 |
* { |
|
815 |
* 'some/property': 'value', |
|
816 |
* 'nestedProperty': { |
|
817 |
* 'sub-property': 'value' |
|
818 |
* } |
|
819 |
* } |
|
820 |
* |
|
821 |
* it'll return this output: |
|
822 |
* |
|
823 |
* { |
|
824 |
* '--wp--some-property': 'value', |
|
825 |
* '--wp--nested-property--sub-property': 'value' |
|
826 |
* } |
|
827 |
* |
|
828 |
* @since 5.8.0 |
|
829 |
* |
|
830 |
* @param array $tree Input tree to process. |
|
831 |
* @param string $prefix Optional. Prefix to prepend to each variable. Default empty string. |
|
832 |
* @param string $token Optional. Token to use between levels. Default '--'. |
|
833 |
* @return array The flattened tree. |
|
834 |
*/ |
|
835 |
private static function flatten_tree( $tree, $prefix = '', $token = '--' ) { |
|
836 |
$result = array(); |
|
837 |
foreach ( $tree as $property => $value ) { |
|
838 |
$new_key = $prefix . str_replace( |
|
839 |
'/', |
|
840 |
'-', |
|
841 |
strtolower( preg_replace( '/(?<!^)[A-Z]/', '-$0', $property ) ) // CamelCase to kebab-case. |
|
842 |
); |
|
843 |
|
|
844 |
if ( is_array( $value ) ) { |
|
845 |
$new_prefix = $new_key . $token; |
|
846 |
$result = array_merge( |
|
847 |
$result, |
|
848 |
self::flatten_tree( $value, $new_prefix, $token ) |
|
849 |
); |
|
850 |
} else { |
|
851 |
$result[ $new_key ] = $value; |
|
852 |
} |
|
853 |
} |
|
854 |
return $result; |
|
855 |
} |
|
856 |
|
|
857 |
/** |
|
858 |
* Given a styles array, it extracts the style properties |
|
859 |
* and adds them to the $declarations array following the format: |
|
860 |
* |
|
861 |
* array( |
|
862 |
* 'name' => 'property_name', |
|
863 |
* 'value' => 'property_value, |
|
864 |
* ) |
|
865 |
* |
|
866 |
* @since 5.8.0 |
|
867 |
* |
|
868 |
* @param array $styles Styles to process. |
|
869 |
* @return array Returns the modified $declarations. |
|
870 |
*/ |
|
871 |
private static function compute_style_properties( $styles ) { |
|
872 |
$declarations = array(); |
|
873 |
if ( empty( $styles ) ) { |
|
874 |
return $declarations; |
|
875 |
} |
|
876 |
|
|
877 |
$properties = array(); |
|
878 |
foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { |
|
879 |
/* |
|
880 |
* Some properties can be shorthand properties, meaning that |
|
881 |
* they contain multiple values instead of a single one. |
|
882 |
* An example of this is the padding property. |
|
883 |
*/ |
|
884 |
if ( self::has_properties( $metadata ) ) { |
|
885 |
foreach ( $metadata['properties'] as $property ) { |
|
886 |
$properties[] = array( |
|
887 |
'name' => $name . '-' . $property, |
|
888 |
'value' => array_merge( $metadata['value'], array( $property ) ), |
|
889 |
); |
|
890 |
} |
|
891 |
} else { |
|
892 |
$properties[] = array( |
|
893 |
'name' => $name, |
|
894 |
'value' => $metadata['value'], |
|
895 |
); |
|
896 |
} |
|
897 |
} |
|
898 |
|
|
899 |
foreach ( $properties as $prop ) { |
|
900 |
$value = self::get_property_value( $styles, $prop['value'] ); |
|
901 |
if ( empty( $value ) ) { |
|
902 |
continue; |
|
903 |
} |
|
904 |
|
|
905 |
$declarations[] = array( |
|
906 |
'name' => $prop['name'], |
|
907 |
'value' => $value, |
|
908 |
); |
|
909 |
} |
|
910 |
|
|
911 |
return $declarations; |
|
912 |
} |
|
913 |
|
|
914 |
/** |
|
915 |
* Whether the metadata contains a key named properties. |
|
916 |
* |
|
917 |
* @since 5.8.0 |
|
918 |
* |
|
919 |
* @param array $metadata Description of the style property. |
|
920 |
* @return bool True if properties exists, false otherwise. |
|
921 |
*/ |
|
922 |
private static function has_properties( $metadata ) { |
|
923 |
if ( array_key_exists( 'properties', $metadata ) ) { |
|
924 |
return true; |
|
925 |
} |
|
926 |
|
|
927 |
return false; |
|
928 |
} |
|
929 |
|
|
930 |
/** |
|
931 |
* Returns the style property for the given path. |
|
932 |
* |
|
933 |
* It also converts CSS Custom Property stored as |
|
934 |
* "var:preset|color|secondary" to the form |
|
935 |
* "--wp--preset--color--secondary". |
|
936 |
* |
|
937 |
* @since 5.8.0 |
|
938 |
* |
|
939 |
* @param array $styles Styles subtree. |
|
940 |
* @param array $path Which property to process. |
|
941 |
* @return string Style property value. |
|
942 |
*/ |
|
943 |
private static function get_property_value( $styles, $path ) { |
|
944 |
$value = _wp_array_get( $styles, $path, '' ); |
|
945 |
|
|
946 |
if ( '' === $value ) { |
|
947 |
return $value; |
|
948 |
} |
|
949 |
|
|
950 |
$prefix = 'var:'; |
|
951 |
$prefix_len = strlen( $prefix ); |
|
952 |
$token_in = '|'; |
|
953 |
$token_out = '--'; |
|
954 |
if ( 0 === strncmp( $value, $prefix, $prefix_len ) ) { |
|
955 |
$unwrapped_name = str_replace( |
|
956 |
$token_in, |
|
957 |
$token_out, |
|
958 |
substr( $value, $prefix_len ) |
|
959 |
); |
|
960 |
$value = "var(--wp--$unwrapped_name)"; |
|
961 |
} |
|
962 |
|
|
963 |
return $value; |
|
964 |
} |
|
965 |
|
|
966 |
/** |
|
967 |
* Builds metadata for the setting nodes, which returns in the form of: |
|
968 |
* |
|
969 |
* [ |
|
970 |
* [ |
|
971 |
* 'path' => ['path', 'to', 'some', 'node' ], |
|
972 |
* 'selector' => 'CSS selector for some node' |
|
973 |
* ], |
|
974 |
* [ |
|
975 |
* 'path' => [ 'path', 'to', 'other', 'node' ], |
|
976 |
* 'selector' => 'CSS selector for other node' |
|
977 |
* ], |
|
978 |
* ] |
|
979 |
* |
|
980 |
* @since 5.8.0 |
|
981 |
* |
|
982 |
* @param array $theme_json The tree to extract setting nodes from. |
|
983 |
* @param array $selectors List of selectors per block. |
|
984 |
* @return array |
|
985 |
*/ |
|
986 |
private static function get_setting_nodes( $theme_json, $selectors = array() ) { |
|
987 |
$nodes = array(); |
|
988 |
if ( ! isset( $theme_json['settings'] ) ) { |
|
989 |
return $nodes; |
|
990 |
} |
|
991 |
|
|
992 |
// Top-level. |
|
993 |
$nodes[] = array( |
|
994 |
'path' => array( 'settings' ), |
|
995 |
'selector' => self::ROOT_BLOCK_SELECTOR, |
|
996 |
); |
|
997 |
|
|
998 |
// Calculate paths for blocks. |
|
999 |
if ( ! isset( $theme_json['settings']['blocks'] ) ) { |
|
1000 |
return $nodes; |
|
1001 |
} |
|
1002 |
|
|
1003 |
foreach ( $theme_json['settings']['blocks'] as $name => $node ) { |
|
1004 |
$selector = null; |
|
1005 |
if ( isset( $selectors[ $name ]['selector'] ) ) { |
|
1006 |
$selector = $selectors[ $name ]['selector']; |
|
1007 |
} |
|
1008 |
|
|
1009 |
$nodes[] = array( |
|
1010 |
'path' => array( 'settings', 'blocks', $name ), |
|
1011 |
'selector' => $selector, |
|
1012 |
); |
|
1013 |
} |
|
1014 |
|
|
1015 |
return $nodes; |
|
1016 |
} |
|
1017 |
|
|
1018 |
|
|
1019 |
/** |
|
1020 |
* Builds metadata for the style nodes, which returns in the form of: |
|
1021 |
* |
|
1022 |
* [ |
|
1023 |
* [ |
|
1024 |
* 'path' => [ 'path', 'to', 'some', 'node' ], |
|
1025 |
* 'selector' => 'CSS selector for some node' |
|
1026 |
* ], |
|
1027 |
* [ |
|
1028 |
* 'path' => ['path', 'to', 'other', 'node' ], |
|
1029 |
* 'selector' => 'CSS selector for other node' |
|
1030 |
* ], |
|
1031 |
* ] |
|
1032 |
* |
|
1033 |
* @since 5.8.0 |
|
1034 |
* |
|
1035 |
* @param array $theme_json The tree to extract style nodes from. |
|
1036 |
* @param array $selectors List of selectors per block. |
|
1037 |
* @return array |
|
1038 |
*/ |
|
1039 |
private static function get_style_nodes( $theme_json, $selectors = array() ) { |
|
1040 |
$nodes = array(); |
|
1041 |
if ( ! isset( $theme_json['styles'] ) ) { |
|
1042 |
return $nodes; |
|
1043 |
} |
|
1044 |
|
|
1045 |
// Top-level. |
|
1046 |
$nodes[] = array( |
|
1047 |
'path' => array( 'styles' ), |
|
1048 |
'selector' => self::ROOT_BLOCK_SELECTOR, |
|
1049 |
); |
|
1050 |
|
|
1051 |
if ( isset( $theme_json['styles']['elements'] ) ) { |
|
1052 |
foreach ( $theme_json['styles']['elements'] as $element => $node ) { |
|
1053 |
$nodes[] = array( |
|
1054 |
'path' => array( 'styles', 'elements', $element ), |
|
1055 |
'selector' => self::ELEMENTS[ $element ], |
|
1056 |
); |
|
1057 |
} |
|
1058 |
} |
|
1059 |
|
|
1060 |
// Blocks. |
|
1061 |
if ( ! isset( $theme_json['styles']['blocks'] ) ) { |
|
1062 |
return $nodes; |
|
1063 |
} |
|
1064 |
|
|
1065 |
foreach ( $theme_json['styles']['blocks'] as $name => $node ) { |
|
1066 |
$selector = null; |
|
1067 |
if ( isset( $selectors[ $name ]['selector'] ) ) { |
|
1068 |
$selector = $selectors[ $name ]['selector']; |
|
1069 |
} |
|
1070 |
|
|
1071 |
$nodes[] = array( |
|
1072 |
'path' => array( 'styles', 'blocks', $name ), |
|
1073 |
'selector' => $selector, |
|
1074 |
); |
|
1075 |
|
|
1076 |
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { |
|
1077 |
foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { |
|
1078 |
$nodes[] = array( |
|
1079 |
'path' => array( 'styles', 'blocks', $name, 'elements', $element ), |
|
1080 |
'selector' => $selectors[ $name ]['elements'][ $element ], |
|
1081 |
); |
|
1082 |
} |
|
1083 |
} |
|
1084 |
} |
|
1085 |
|
|
1086 |
return $nodes; |
|
1087 |
} |
|
1088 |
|
|
1089 |
/** |
|
1090 |
* Merge new incoming data. |
|
1091 |
* |
|
1092 |
* @since 5.8.0 |
|
1093 |
* |
|
1094 |
* @param WP_Theme_JSON $incoming Data to merge. |
|
1095 |
*/ |
|
1096 |
public function merge( $incoming ) { |
|
1097 |
$incoming_data = $incoming->get_raw_data(); |
|
1098 |
$this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data ); |
|
1099 |
|
|
1100 |
/* |
|
1101 |
* The array_replace_recursive() algorithm merges at the leaf level. |
|
1102 |
* For leaf values that are arrays it will use the numeric indexes for replacement. |
|
1103 |
* In those cases, we want to replace the existing with the incoming value, if it exists. |
|
1104 |
*/ |
|
1105 |
$to_replace = array(); |
|
1106 |
$to_replace[] = array( 'spacing', 'units' ); |
|
1107 |
$to_replace[] = array( 'color', 'duotone' ); |
|
1108 |
foreach ( self::VALID_ORIGINS as $origin ) { |
|
1109 |
$to_replace[] = array( 'color', 'palette', $origin ); |
|
1110 |
$to_replace[] = array( 'color', 'gradients', $origin ); |
|
1111 |
$to_replace[] = array( 'typography', 'fontSizes', $origin ); |
|
1112 |
$to_replace[] = array( 'typography', 'fontFamilies', $origin ); |
|
1113 |
} |
|
1114 |
|
|
1115 |
$nodes = self::get_setting_nodes( $this->theme_json ); |
|
1116 |
foreach ( $nodes as $metadata ) { |
|
1117 |
foreach ( $to_replace as $property_path ) { |
|
1118 |
$path = array_merge( $metadata['path'], $property_path ); |
|
1119 |
$node = _wp_array_get( $incoming_data, $path, null ); |
|
1120 |
if ( isset( $node ) ) { |
|
1121 |
_wp_array_set( $this->theme_json, $path, $node ); |
|
1122 |
} |
|
1123 |
} |
|
1124 |
} |
|
1125 |
} |
|
1126 |
|
|
1127 |
/** |
|
1128 |
* Returns the raw data. |
|
1129 |
* |
|
1130 |
* @since 5.8.0 |
|
1131 |
* |
|
1132 |
* @return array Raw data. |
|
1133 |
*/ |
|
1134 |
public function get_raw_data() { |
|
1135 |
return $this->theme_json; |
|
1136 |
} |
|
1137 |
|
|
1138 |
/** |
|
1139 |
* Transforms the given editor settings according the |
|
1140 |
* add_theme_support format to the theme.json format. |
|
1141 |
* |
|
1142 |
* @since 5.8.0 |
|
1143 |
* |
|
1144 |
* @param array $settings Existing editor settings. |
|
1145 |
* @return array Config that adheres to the theme.json schema. |
|
1146 |
*/ |
|
1147 |
public static function get_from_editor_settings( $settings ) { |
|
1148 |
$theme_settings = array( |
|
1149 |
'version' => self::LATEST_SCHEMA, |
|
1150 |
'settings' => array(), |
|
1151 |
); |
|
1152 |
|
|
1153 |
// Deprecated theme supports. |
|
1154 |
if ( isset( $settings['disableCustomColors'] ) ) { |
|
1155 |
if ( ! isset( $theme_settings['settings']['color'] ) ) { |
|
1156 |
$theme_settings['settings']['color'] = array(); |
|
1157 |
} |
|
1158 |
$theme_settings['settings']['color']['custom'] = ! $settings['disableCustomColors']; |
|
1159 |
} |
|
1160 |
|
|
1161 |
if ( isset( $settings['disableCustomGradients'] ) ) { |
|
1162 |
if ( ! isset( $theme_settings['settings']['color'] ) ) { |
|
1163 |
$theme_settings['settings']['color'] = array(); |
|
1164 |
} |
|
1165 |
$theme_settings['settings']['color']['customGradient'] = ! $settings['disableCustomGradients']; |
|
1166 |
} |
|
1167 |
|
|
1168 |
if ( isset( $settings['disableCustomFontSizes'] ) ) { |
|
1169 |
if ( ! isset( $theme_settings['settings']['typography'] ) ) { |
|
1170 |
$theme_settings['settings']['typography'] = array(); |
|
1171 |
} |
|
1172 |
$theme_settings['settings']['typography']['customFontSize'] = ! $settings['disableCustomFontSizes']; |
|
1173 |
} |
|
1174 |
|
|
1175 |
if ( isset( $settings['enableCustomLineHeight'] ) ) { |
|
1176 |
if ( ! isset( $theme_settings['settings']['typography'] ) ) { |
|
1177 |
$theme_settings['settings']['typography'] = array(); |
|
1178 |
} |
|
1179 |
$theme_settings['settings']['typography']['customLineHeight'] = $settings['enableCustomLineHeight']; |
|
1180 |
} |
|
1181 |
|
|
1182 |
if ( isset( $settings['enableCustomUnits'] ) ) { |
|
1183 |
if ( ! isset( $theme_settings['settings']['spacing'] ) ) { |
|
1184 |
$theme_settings['settings']['spacing'] = array(); |
|
1185 |
} |
|
1186 |
$theme_settings['settings']['spacing']['units'] = ( true === $settings['enableCustomUnits'] ) ? |
|
1187 |
array( 'px', 'em', 'rem', 'vh', 'vw', '%' ) : |
|
1188 |
$settings['enableCustomUnits']; |
|
1189 |
} |
|
1190 |
|
|
1191 |
if ( isset( $settings['colors'] ) ) { |
|
1192 |
if ( ! isset( $theme_settings['settings']['color'] ) ) { |
|
1193 |
$theme_settings['settings']['color'] = array(); |
|
1194 |
} |
|
1195 |
$theme_settings['settings']['color']['palette'] = $settings['colors']; |
|
1196 |
} |
|
1197 |
|
|
1198 |
if ( isset( $settings['gradients'] ) ) { |
|
1199 |
if ( ! isset( $theme_settings['settings']['color'] ) ) { |
|
1200 |
$theme_settings['settings']['color'] = array(); |
|
1201 |
} |
|
1202 |
$theme_settings['settings']['color']['gradients'] = $settings['gradients']; |
|
1203 |
} |
|
1204 |
|
|
1205 |
if ( isset( $settings['fontSizes'] ) ) { |
|
1206 |
$font_sizes = $settings['fontSizes']; |
|
1207 |
// Back-compatibility for presets without units. |
|
1208 |
foreach ( $font_sizes as $key => $font_size ) { |
|
1209 |
if ( is_numeric( $font_size['size'] ) ) { |
|
1210 |
$font_sizes[ $key ]['size'] = $font_size['size'] . 'px'; |
|
1211 |
} |
|
1212 |
} |
|
1213 |
if ( ! isset( $theme_settings['settings']['typography'] ) ) { |
|
1214 |
$theme_settings['settings']['typography'] = array(); |
|
1215 |
} |
|
1216 |
$theme_settings['settings']['typography']['fontSizes'] = $font_sizes; |
|
1217 |
} |
|
1218 |
|
|
1219 |
if ( isset( $settings['enableCustomSpacing'] ) ) { |
|
1220 |
if ( ! isset( $theme_settings['settings']['spacing'] ) ) { |
|
1221 |
$theme_settings['settings']['spacing'] = array(); |
|
1222 |
} |
|
1223 |
$theme_settings['settings']['spacing']['customPadding'] = $settings['enableCustomSpacing']; |
|
1224 |
} |
|
1225 |
|
|
1226 |
// Things that didn't land in core yet, so didn't have a setting assigned. |
|
1227 |
if ( current( (array) get_theme_support( 'experimental-link-color' ) ) ) { |
|
1228 |
if ( ! isset( $theme_settings['settings']['color'] ) ) { |
|
1229 |
$theme_settings['settings']['color'] = array(); |
|
1230 |
} |
|
1231 |
$theme_settings['settings']['color']['link'] = true; |
|
1232 |
} |
|
1233 |
|
|
1234 |
return $theme_settings; |
|
1235 |
} |
|
1236 |
|
|
1237 |
} |