PluginProbe
Gutenberg / 9.0.0
Gutenberg v9.0.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / global-styles.php

global-styles.php in Gutenberg 9.0.0, at lib/global-styles.php

810 lines 25.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Bootstraps Global Styles.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Whether the current theme has a theme.json file.
10 *
11 * @return boolean
12 */
13 function gutenberg_experimental_global_styles_has_theme_json_support() {
14 return is_readable( locate_template( 'experimental-theme.json' ) );
15 }
16
17 /**
18 * Given a tree, it creates a flattened one
19 * by merging the keys and binding the leaf values
20 * to the new keys.
21 *
22 * This is thought to be useful to generate
23 * CSS Custom Properties from a tree,
24 * although there's nothing in the implementation
25 * of this function that requires that format.
26 *
27 * For example, assuming the given prefix is '--wp'
28 * and the token is '--', for this input tree:
29 *
30 * {
31 * 'property': 'value',
32 * 'nested-property': {
33 * 'sub-property': 'value'
34 * }
35 * }
36 *
37 * it'll return this output:
38 *
39 * {
40 * '--wp--property': 'value',
41 * '--wp--nested-property--sub-property': 'value'
42 * }
43 *
44 * @param array $tree Input tree to process.
45 * @param string $prefix Prefix to prepend to each variable. '' by default.
46 * @param string $token Token to use between levels. '--' by default.
47 *
48 * @return array The flattened tree.
49 */
50 function gutenberg_experimental_global_styles_get_css_vars( $tree, $prefix = '', $token = '--' ) {
51 $result = array();
52 foreach ( $tree as $property => $value ) {
53 $new_key = $prefix . str_replace( '/', '-', $property );
54
55 if ( is_array( $value ) ) {
56 $new_prefix = $new_key . $token;
57 $result = array_merge(
58 $result,
59 gutenberg_experimental_global_styles_get_css_vars( $value, $new_prefix, $token )
60 );
61 } else {
62 $result[ $new_key ] = $value;
63 }
64 }
65 return $result;
66 }
67
68 /**
69 * Processes a file that adheres to the theme.json
70 * schema and returns an array with its contents,
71 * or a void array if none found.
72 *
73 * @param string $file_path Path to file.
74 * @return array Contents that adhere to the theme.json schema.
75 */
76 function gutenberg_experimental_global_styles_get_from_file( $file_path ) {
77 $config = array();
78 if ( file_exists( $file_path ) ) {
79 $decoded_file = json_decode(
80 file_get_contents( $file_path ),
81 true
82 );
83
84 $json_decoding_error = json_last_error();
85 if ( JSON_ERROR_NONE !== $json_decoding_error ) {
86 error_log( 'Error when decoding file schema: ' . json_last_error_msg() );
87 return $config;
88 }
89
90 if ( is_array( $decoded_file ) ) {
91 $config = $decoded_file;
92 }
93 }
94 return $config;
95 }
96
97 /**
98 * Returns the user's origin config.
99 *
100 * @return array Config that adheres to the theme.json schema.
101 */
102 function gutenberg_experimental_global_styles_get_user() {
103 $config = array();
104 $user_cpt = gutenberg_experimental_global_styles_get_user_cpt();
105 if ( array_key_exists( 'post_content', $user_cpt ) ) {
106 $decoded_data = json_decode( $user_cpt['post_content'], true );
107
108 $json_decoding_error = json_last_error();
109 if ( JSON_ERROR_NONE !== $json_decoding_error ) {
110 error_log( 'Error when decoding user schema: ' . json_last_error_msg() );
111 return $config;
112 }
113
114 if ( is_array( $decoded_data ) ) {
115 $config = $decoded_data;
116 }
117 }
118
119 return $config;
120 }
121
122 /**
123 * Returns the CPT that contains the user's origin config
124 * for the current theme or a void array if none found.
125 *
126 * It can also create and return a new draft CPT.
127 *
128 * @param bool $should_create_cpt Whether a new CPT should be created if no one was found.
129 * False by default.
130 * @param array $post_status_filter Filter CPT by post status.
131 * ['publish'] by default, so it only fetches published posts.
132 * @return array Custom Post Type for the user's origin config.
133 */
134 function gutenberg_experimental_global_styles_get_user_cpt( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) {
135 $user_cpt = array();
136 $post_type_filter = 'wp_global_styles';
137 $post_name_filter = 'wp-global-styles-' . strtolower( wp_get_theme()->get( 'TextDomain' ) );
138 $recent_posts = wp_get_recent_posts(
139 array(
140 'numberposts' => 1,
141 'orderby' => 'date',
142 'order' => 'desc',
143 'post_type' => $post_type_filter,
144 'post_status' => $post_status_filter,
145 'name' => $post_name_filter,
146 )
147 );
148
149 if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
150 $user_cpt = $recent_posts[0];
151 } elseif ( $should_create_cpt ) {
152 $cpt_post_id = wp_insert_post(
153 array(
154 'post_content' => '{}',
155 'post_status' => 'publish',
156 'post_type' => $post_type_filter,
157 'post_name' => $post_name_filter,
158 ),
159 true
160 );
161 $user_cpt = get_post( $cpt_post_id, ARRAY_A );
162 }
163
164 return $user_cpt;
165 }
166
167 /**
168 * Returns the post ID of the CPT containing the user's origin config.
169 *
170 * @return integer
171 */
172 function gutenberg_experimental_global_styles_get_user_cpt_id() {
173 $user_cpt_id = null;
174 $user_cpt = gutenberg_experimental_global_styles_get_user_cpt( true );
175 if ( array_key_exists( 'ID', $user_cpt ) ) {
176 $user_cpt_id = $user_cpt['ID'];
177 }
178 return $user_cpt_id;
179 }
180
181 /**
182 * Return core's origin config.
183 *
184 * @return array Config that adheres to the theme.json schema.
185 */
186 function gutenberg_experimental_global_styles_get_core() {
187 $config = gutenberg_experimental_global_styles_get_from_file(
188 __DIR__ . '/experimental-default-theme.json'
189 );
190
191 return $config;
192 }
193
194 /**
195 * Returns the theme presets registered via add_theme_support, if any.
196 *
197 * @return array Config that adheres to the theme.json schema.
198 */
199 function gutenberg_experimental_global_styles_get_theme_presets() {
200 $theme_presets = array();
201
202 $theme_colors = gutenberg_experimental_get( get_theme_support( 'editor-color-palette' ), array( '0' ) );
203 foreach ( $theme_colors as $color ) {
204 $theme_presets['global']['presets']['color'][] = array(
205 'slug' => $color['slug'],
206 'value' => $color['color'],
207 );
208 }
209
210 $theme_gradients = gutenberg_experimental_get( get_theme_support( 'editor-gradient-presets' ), array( '0' ) );
211 foreach ( $theme_gradients as $gradient ) {
212 $theme_presets['global']['presets']['gradient'][] = array(
213 'slug' => $gradient['slug'],
214 'value' => $gradient['gradient'],
215 );
216 }
217
218 $theme_font_sizes = gutenberg_experimental_get( get_theme_support( 'editor-font-sizes' ), array( '0' ) );
219 foreach ( $theme_font_sizes as $font_size ) {
220 $theme_presets['global']['presets']['font-size'][] = array(
221 'slug' => $font_size['slug'],
222 'value' => $font_size['size'],
223 );
224 }
225
226 return $theme_presets;
227 }
228
229 /**
230 * Returns the theme's origin config.
231 *
232 * It also fetches the existing presets the theme declared via add_theme_support
233 * and uses them if the theme hasn't declared any via theme.json.
234 *
235 * @return array Config that adheres to the theme.json schema.
236 */
237 function gutenberg_experimental_global_styles_get_theme() {
238 $theme_presets = gutenberg_experimental_global_styles_get_theme_presets();
239 $theme_config = gutenberg_experimental_global_styles_get_from_file(
240 locate_template( 'experimental-theme.json' )
241 );
242
243 /*
244 * We want the presets declared in theme.json
245 * to take precedence over the ones declared via add_theme_support.
246 *
247 * Note that merging happens at the preset category level. Example:
248 *
249 * - if the theme declares a color palette via add_theme_support &
250 * a set of font sizes via theme.json, both will be included in the output.
251 *
252 * - if the theme declares a color palette both via add_theme_support &
253 * via theme.json, the later takes precedence.
254 *
255 */
256 $theme_config = gutenberg_experimental_global_styles_merge_trees(
257 $theme_presets,
258 $theme_config
259 );
260
261 return $theme_config;
262 }
263
264 /**
265 * Convert style property to its CSS name.
266 *
267 * @param string $style_property Style property name.
268 * @return string CSS property name.
269 */
270 function gutenberg_experimental_global_styles_get_css_property( $style_property ) {
271 switch ( $style_property ) {
272 case 'backgroundColor':
273 return 'background-color';
274 case 'fontSize':
275 return 'font-size';
276 case 'lineHeight':
277 return 'line-height';
278 default:
279 return $style_property;
280 }
281 }
282
283 /**
284 * Return how the style property is structured.
285 *
286 * @return array Style property structure.
287 */
288 function gutenberg_experimental_global_styles_get_style_property() {
289 return array(
290 '--wp--style--color--link' => array( 'color', 'link' ),
291 'background' => array( 'color', 'gradient' ),
292 'backgroundColor' => array( 'color', 'background' ),
293 'color' => array( 'color', 'text' ),
294 'fontSize' => array( 'typography', 'fontSize' ),
295 'lineHeight' => array( 'typography', 'lineHeight' ),
296 );
297 }
298
299 /**
300 * Return how the support keys are structured.
301 *
302 * @return array Support keys structure.
303 */
304 function gutenberg_experimental_global_styles_get_support_keys() {
305 return array(
306 '--wp--style--color--link' => array( '__experimentalColor', 'linkColor' ),
307 'background' => array( '__experimentalColor', 'gradients' ),
308 'backgroundColor' => array( '__experimentalColor' ),
309 'color' => array( '__experimentalColor' ),
310 'fontSize' => array( '__experimentalFontSize' ),
311 'lineHeight' => array( '__experimentalLineHeight' ),
312 );
313 }
314
315 /**
316 * Returns the style features a particular block supports.
317 *
318 * @param array $supports The block supports array.
319 *
320 * @return array Style features supported by the block.
321 */
322 function gutenberg_experimental_global_styles_get_supported_styles( $supports ) {
323 $support_keys = gutenberg_experimental_global_styles_get_support_keys();
324 $supported_features = array();
325 foreach ( $support_keys as $key => $path ) {
326 if ( gutenberg_experimental_get( $supports, $path ) ) {
327 $supported_features[] = $key;
328 }
329 }
330
331 return $supported_features;
332 }
333
334 /**
335 * Retrieves the block data (selector/supports).
336 *
337 * @return array
338 */
339 function gutenberg_experimental_global_styles_get_block_data() {
340 $block_data = array();
341
342 $registry = WP_Block_Type_Registry::get_instance();
343 $blocks = array_merge(
344 $registry->get_all_registered(),
345 array(
346 'global' => new WP_Block_Type(
347 'global',
348 array(
349 'supports' => array(
350 '__experimentalSelector' => ':root',
351 '__experimentalFontSize' => true,
352 '__experimentalColor' => array(
353 'linkColor' => true,
354 'gradients' => true,
355 ),
356 ),
357 )
358 ),
359 )
360 );
361 foreach ( $blocks as $block_name => $block_type ) {
362 if ( ! property_exists( $block_type, 'supports' ) || empty( $block_type->supports ) || ! is_array( $block_type->supports ) ) {
363 continue;
364 }
365
366 $supports = gutenberg_experimental_global_styles_get_supported_styles( $block_type->supports );
367 if ( empty( $supports ) ) {
368 continue;
369 }
370
371 /*
372 * Assign the selector for the block.
373 *
374 * Some blocks can declare multiple selectors:
375 *
376 * - core/heading represents the H1-H6 HTML elements
377 * - core/list represents the UL and OL HTML elements
378 * - core/group is meant to represent DIV and other HTML elements
379 *
380 * Some other blocks don't provide a selector,
381 * so we generate a class for them based on their name:
382 *
383 * - 'core/group' => '.wp-block-group'
384 * - 'my-custom-library/block-name' => '.wp-block-my-custom-library-block-name'
385 *
386 * Note that, for core blocks, we don't add the `core/` prefix to its class name.
387 * This is for historical reasons, as they come with a class without that infix.
388 *
389 */
390 if (
391 isset( $block_type->supports['__experimentalSelector'] ) &&
392 is_string( $block_type->supports['__experimentalSelector'] )
393 ) {
394 $block_data[ $block_name ] = array(
395 'selector' => $block_type->supports['__experimentalSelector'],
396 'supports' => $supports,
397 'blockName' => $block_name,
398 );
399 } elseif (
400 isset( $block_type->supports['__experimentalSelector'] ) &&
401 is_array( $block_type->supports['__experimentalSelector'] )
402 ) {
403 foreach ( $block_type->supports['__experimentalSelector'] as $key => $selector ) {
404 $block_data[ $key ] = array(
405 'selector' => $selector,
406 'supports' => $supports,
407 'blockName' => $block_name,
408 );
409 }
410 } else {
411 $block_data[ $block_name ] = array(
412 'selector' => '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) ),
413 'supports' => $supports,
414 'blockName' => $block_name,
415 );
416 }
417 }
418
419 return $block_data;
420 }
421
422 /**
423 * Given an array contain the styles shape returns the css for this styles.
424 * A similar function exists on the client at /packages/block-editor/src/hooks/style.js.
425 *
426 * @param array $styles Array containing the styles shape from global styles.
427 *
428 * @return array Containing a set of css rules.
429 */
430 function gutenberg_experimental_global_styles_flatten_styles_tree( $styles ) {
431 $mappings = gutenberg_experimental_global_styles_get_style_property();
432
433 $result = array();
434 foreach ( $mappings as $key => $path ) {
435 $value = gutenberg_experimental_get( $styles, $path, null );
436 if ( null !== $value ) {
437 $result[ $key ] = $value;
438 }
439 }
440 return $result;
441
442 }
443
444 /**
445 * Takes a tree adhering to the theme.json schema and generates
446 * the corresponding stylesheet.
447 *
448 * @param array $tree Input tree.
449 *
450 * @return string Stylesheet.
451 */
452 function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
453 $stylesheet = '';
454 $block_data = gutenberg_experimental_global_styles_get_block_data();
455 foreach ( array_keys( $tree ) as $block_name ) {
456 if (
457 ! array_key_exists( $block_name, $block_data ) ||
458 ! array_key_exists( 'selector', $block_data[ $block_name ] ) ||
459 ! array_key_exists( 'supports', $block_data[ $block_name ] )
460 ) {
461 // Skip blocks that haven't declared support,
462 // because we don't know to process them.
463 continue;
464 }
465
466 // Extract the relevant preset info before converting them to CSS Custom Properties.
467 foreach ( array_keys( $tree[ $block_name ]['presets'] ) as $preset_category ) {
468 $flattened_values = array();
469 foreach ( $tree[ $block_name ]['presets'][ $preset_category ] as $preset_value ) {
470 $flattened_values[ $preset_value['slug'] ] = $preset_value['value'];
471 }
472 $tree[ $block_name ]['presets'][ $preset_category ] = $flattened_values;
473 }
474
475 $token = '--';
476 $prefix = '--wp--preset' . $token;
477 $css_variables = gutenberg_experimental_global_styles_get_css_vars( $tree[ $block_name ]['presets'], $prefix, $token );
478
479 $stylesheet .= gutenberg_experimental_global_styles_resolver_styles(
480 $block_data[ $block_name ]['selector'],
481 $block_data[ $block_name ]['supports'],
482 array_merge(
483 gutenberg_experimental_global_styles_flatten_styles_tree( $tree[ $block_name ]['styles'] ),
484 $css_variables
485 )
486 );
487 }
488
489 if ( gutenberg_experimental_global_styles_has_theme_json_support() ) {
490 // To support all themes, we added in the block-library stylesheet
491 // a style rule such as .has-link-color a { color: var(--wp--style--color--link, #00e); }
492 // so that existing link colors themes used didn't break.
493 // We add this here to make it work for themes that opt-in to theme.json
494 // In the future, we may do this differently.
495 $stylesheet .= 'a{color:var(--wp--style--color--link, #00e);}';
496 }
497
498 return $stylesheet;
499 }
500
501 /**
502 * Generates CSS declarations for a block.
503 *
504 * @param string $block_selector CSS selector for the block.
505 * @param array $block_supports A list of properties supported by the block.
506 * @param array $block_styles The list of properties/values to be converted to CSS.
507 *
508 * @return string The corresponding CSS rule.
509 */
510 function gutenberg_experimental_global_styles_resolver_styles( $block_selector, $block_supports, $block_styles ) {
511 $css_property = '';
512 $css_rule = '';
513 $css_declarations = '';
514
515 foreach ( $block_styles as $property => $value ) {
516 // Only convert to CSS:
517 //
518 // 1) The style attributes the block has declared support for.
519 // 2) Any CSS custom property attached to the node.
520 if (
521 in_array( $property, $block_supports, true ) ||
522 strstr( $property, '--' )
523 ) {
524 $css_property = gutenberg_experimental_global_styles_get_css_property( $property );
525
526 // Add whitespace if SCRIPT_DEBUG is defined and set to true.
527 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
528 $css_declarations .= "\t" . $css_property . ': ' . $value . ";\n";
529 } else {
530 $css_declarations .= $css_property . ':' . $value . ';';
531 }
532 }
533 }
534
535 if ( '' !== $css_declarations ) {
536
537 // Add whitespace if SCRIPT_DEBUG is defined and set to true.
538 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
539 $css_rule .= $block_selector . " {\n";
540 $css_rule .= $css_declarations;
541 $css_rule .= "}\n";
542 } else {
543 $css_rule .= $block_selector . '{' . $css_declarations . '}';
544 }
545 }
546
547 return $css_rule;
548 }
549
550 /**
551 * Helper function that merges trees that adhere to the theme.json schema.
552 *
553 * @param array $core Core origin.
554 * @param array $theme Theme origin.
555 * @param array $user User origin. An empty array by default.
556 *
557 * @return array The merged result.
558 */
559 function gutenberg_experimental_global_styles_merge_trees( $core, $theme, $user = array() ) {
560 $core = gutenberg_experimental_global_styles_normalize_schema( $core );
561 $theme = gutenberg_experimental_global_styles_normalize_schema( $theme );
562 $user = gutenberg_experimental_global_styles_normalize_schema( $user );
563 $result = gutenberg_experimental_global_styles_normalize_schema( array() );
564
565 foreach ( array_keys( $core ) as $block_name ) {
566 foreach ( array_keys( $core[ $block_name ]['presets'] ) as $subtree ) {
567 $result[ $block_name ]['presets'][ $subtree ] = array_merge(
568 $core[ $block_name ]['presets'][ $subtree ],
569 $theme[ $block_name ]['presets'][ $subtree ],
570 $user[ $block_name ]['presets'][ $subtree ]
571 );
572 }
573 foreach ( array_keys( $core[ $block_name ]['features'] ) as $subtree ) {
574 $result[ $block_name ]['features'][ $subtree ] = array_merge(
575 $core[ $block_name ]['features'][ $subtree ],
576 $theme[ $block_name ]['features'][ $subtree ],
577 $user[ $block_name ]['features'][ $subtree ]
578 );
579 }
580 foreach ( array_keys( $core[ $block_name ]['styles'] ) as $subtree ) {
581 $result[ $block_name ]['styles'][ $subtree ] = array_merge(
582 $core[ $block_name ]['styles'][ $subtree ],
583 $theme[ $block_name ]['styles'][ $subtree ],
584 $user[ $block_name ]['styles'][ $subtree ]
585 );
586 }
587 }
588
589 return $result;
590 }
591
592 /**
593 * Given a tree, it normalizes it to the expected schema.
594 *
595 * @param array $tree Source tree to normalize.
596 *
597 * @return array Normalized tree.
598 */
599 function gutenberg_experimental_global_styles_normalize_schema( $tree ) {
600 $block_schema = array(
601 'styles' => array(
602 'typography' => array(),
603 'color' => array(),
604 ),
605 'features' => array(
606 'typography' => array(),
607 'color' => array(),
608 'gradient' => array(),
609 'fontSize' => array(),
610 'lineHeight' => array(),
611 'spacing' => array(),
612 ),
613 'presets' => array(
614 'color' => array(),
615 'font-size' => array(),
616 'gradient' => array(),
617 ),
618 );
619
620 $normalized_tree = array();
621 $block_data = gutenberg_experimental_global_styles_get_block_data();
622 foreach ( array_keys( $block_data ) as $block_name ) {
623 $normalized_tree[ $block_name ] = $block_schema;
624 }
625
626 $tree = array_merge_recursive(
627 $normalized_tree,
628 $tree
629 );
630
631 return $tree;
632 }
633
634 /**
635 * Takes data from the different origins (core, theme, and user)
636 * and returns the merged result.
637 *
638 * @return array Merged trees
639 */
640 function gutenberg_experimental_global_styles_get_merged_origins() {
641 $core = gutenberg_experimental_global_styles_get_core();
642 $theme = gutenberg_experimental_global_styles_get_theme();
643 $user = gutenberg_experimental_global_styles_get_user();
644
645 return gutenberg_experimental_global_styles_merge_trees( $core, $theme, $user );
646 }
647
648 /**
649 * Fetches the preferences for each origin (core, theme, user)
650 * and enqueues the resulting stylesheet.
651 */
652 function gutenberg_experimental_global_styles_enqueue_assets() {
653 $merged = gutenberg_experimental_global_styles_get_merged_origins();
654 $stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $merged );
655 if ( empty( $stylesheet ) ) {
656 return;
657 }
658
659 wp_register_style( 'global-styles', false, array(), true, true );
660 wp_add_inline_style( 'global-styles', $stylesheet );
661 wp_enqueue_style( 'global-styles' );
662 }
663
664 /**
665 * Returns the default config for editor features,
666 * or an empty array if none found.
667 *
668 * @param array $config Config to extract values from.
669 * @return array Default features config for the editor.
670 */
671 function gutenberg_experimental_global_styles_get_editor_features( $config ) {
672 $features = array();
673 foreach ( array_keys( $config ) as $context ) {
674 if (
675 empty( $config[ $context ]['features'] ) ||
676 ! is_array( $config[ $context ]['features'] )
677 ) {
678 $features[ $context ] = array();
679 } else {
680 $features[ $context ] = $config[ $context ]['features'];
681 }
682 }
683
684 // Deprecated theme supports.
685 if ( get_theme_support( 'disable-custom-colors' ) ) {
686 if ( ! isset( $features['global']['color'] ) ) {
687 $features['global']['color'] = array();
688 }
689 $features['global']['color']['custom'] = false;
690 }
691 if ( get_theme_support( 'disable-custom-gradients' ) ) {
692 if ( ! isset( $features['global']['gradient'] ) ) {
693 $features['global']['gradient'] = array();
694 }
695 $features['global']['gradient']['custom'] = false;
696 }
697 if ( get_theme_support( 'disable-custom-font-sizes' ) ) {
698 if ( ! isset( $features['global']['fontSize'] ) ) {
699 $features['global']['fontSize'] = array();
700 }
701 $features['global']['fontSize']['custom'] = false;
702 }
703 if ( get_theme_support( 'custom-line-height' ) ) {
704 if ( ! isset( $features['global']['lineHeight'] ) ) {
705 $features['global']['lineHeight'] = array();
706 }
707 $features['global']['lineHeight']['custom'] = true;
708 }
709 if ( get_theme_support( 'experimental-custom-spacing' ) ) {
710 if ( ! isset( $features['global']['spacing'] ) ) {
711 $features['global']['spacing'] = array();
712 }
713 $features['global']['spacing']['custom'] = true;
714 }
715 if ( get_theme_support( 'experimental-link-color' ) ) {
716 if ( ! isset( $features['global']['color'] ) ) {
717 $features['global']['color'] = array();
718 }
719 $features['global']['color']['link'] = true;
720 }
721
722 return $features;
723 }
724
725 /**
726 * Adds the necessary data for the Global Styles client UI to the block settings.
727 *
728 * @param array $settings Existing block editor settings.
729 * @return array New block editor settings
730 */
731 function gutenberg_experimental_global_styles_settings( $settings ) {
732 $merged = gutenberg_experimental_global_styles_get_merged_origins();
733
734 // STEP 1: ADD FEATURES
735 // These need to be added to settings always.
736 // We also need to unset the deprecated settings defined by core.
737 $settings['__experimentalFeatures'] = gutenberg_experimental_global_styles_get_editor_features( $merged );
738
739 unset( $settings['disableCustomColors'] );
740 unset( $settings['disableCustomGradients'] );
741 unset( $settings['disableCustomFontSizes'] );
742 unset( $settings['enableCustomLineHeight'] );
743
744 // STEP 2 - IF EDIT-SITE, ADD DATA REQUIRED FOR GLOBAL STYLES SIDEBAR
745 // The client needs some information to be able to access/update the user styles.
746 // We only do this if the theme has support for theme.json, though,
747 // as an indicator that the theme will know how to combine this with its stylesheet.
748 $screen = get_current_screen();
749 if (
750 ! empty( $screen ) &&
751 function_exists( 'gutenberg_is_edit_site_page' ) &&
752 gutenberg_is_edit_site_page( $screen->id ) &&
753 gutenberg_experimental_global_styles_has_theme_json_support()
754 ) {
755 $settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id();
756 $settings['__experimentalGlobalStylesContexts'] = gutenberg_experimental_global_styles_get_block_data();
757 $settings['__experimentalGlobalStylesBaseStyles'] = gutenberg_experimental_global_styles_merge_trees(
758 gutenberg_experimental_global_styles_get_core(),
759 gutenberg_experimental_global_styles_get_theme()
760 );
761 } else {
762 // STEP 3 - OTHERWISE, ADD STYLES
763 //
764 // If we are in a block editor context, but not in edit-site,
765 // we need to add the styles via the settings. This is because
766 // we want them processed as if they were added via add_editor_styles,
767 // which adds the editor wrapper class.
768 $settings['styles'][] = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $merged ) );
769 }
770
771 return $settings;
772 }
773
774 /**
775 * Registers a Custom Post Type to store the user's origin config.
776 */
777 function gutenberg_experimental_global_styles_register_cpt() {
778 if ( ! gutenberg_experimental_global_styles_has_theme_json_support() ) {
779 return;
780 }
781
782 $args = array(
783 'label' => __( 'Global Styles', 'gutenberg' ),
784 'description' => 'CPT to store user design tokens',
785 'public' => false,
786 'show_ui' => false,
787 'show_in_rest' => true,
788 'rest_base' => '__experimental/global-styles',
789 'capabilities' => array(
790 'read' => 'edit_theme_options',
791 'create_posts' => 'edit_theme_options',
792 'edit_posts' => 'edit_theme_options',
793 'edit_published_posts' => 'edit_theme_options',
794 'delete_published_posts' => 'edit_theme_options',
795 'edit_others_posts' => 'edit_theme_options',
796 'delete_others_posts' => 'edit_theme_options',
797 ),
798 'map_meta_cap' => true,
799 'supports' => array(
800 'editor',
801 'revisions',
802 ),
803 );
804 register_post_type( 'wp_global_styles', $args );
805 }
806
807 add_action( 'init', 'gutenberg_experimental_global_styles_register_cpt' );
808 add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings' );
809 add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
810