|
1 <?php |
|
2 /** |
|
3 * Block level presets support. |
|
4 * |
|
5 * @package WordPress |
|
6 * @since 6.2.0 |
|
7 */ |
|
8 |
|
9 /** |
|
10 * Get the class name used on block level presets. |
|
11 * |
|
12 * @internal |
|
13 * |
|
14 * @since 6.2.0 |
|
15 * @access private |
|
16 * |
|
17 * @param array $block Block object. |
|
18 * @return string The unique class name. |
|
19 */ |
|
20 function _wp_get_presets_class_name( $block ) { |
|
21 return 'wp-settings-' . md5( serialize( $block ) ); |
|
22 } |
|
23 |
|
24 /** |
|
25 * Update the block content with block level presets class name. |
|
26 * |
|
27 * @internal |
|
28 * |
|
29 * @since 6.2.0 |
|
30 * @access private |
|
31 * |
|
32 * @param string $block_content Rendered block content. |
|
33 * @param array $block Block object. |
|
34 * @return string Filtered block content. |
|
35 */ |
|
36 function _wp_add_block_level_presets_class( $block_content, $block ) { |
|
37 if ( ! $block_content ) { |
|
38 return $block_content; |
|
39 } |
|
40 |
|
41 // return early if the block doesn't have support for settings. |
|
42 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
|
43 if ( ! block_has_support( $block_type, '__experimentalSettings', false ) ) { |
|
44 return $block_content; |
|
45 } |
|
46 |
|
47 // return early if no settings are found on the block attributes. |
|
48 $block_settings = isset( $block['attrs']['settings'] ) ? $block['attrs']['settings'] : null; |
|
49 if ( empty( $block_settings ) ) { |
|
50 return $block_content; |
|
51 } |
|
52 |
|
53 // Like the layout hook this assumes the hook only applies to blocks with a single wrapper. |
|
54 // Add the class name to the first element, presuming it's the wrapper, if it exists. |
|
55 $tags = new WP_HTML_Tag_Processor( $block_content ); |
|
56 if ( $tags->next_tag() ) { |
|
57 $tags->add_class( _wp_get_presets_class_name( $block ) ); |
|
58 } |
|
59 |
|
60 return $tags->get_updated_html(); |
|
61 } |
|
62 |
|
63 /** |
|
64 * Render the block level presets stylesheet. |
|
65 * |
|
66 * @internal |
|
67 * |
|
68 * @since 6.2.0 |
|
69 * @since 6.3.0 Updated preset styles to use Selectors API. |
|
70 * @access private |
|
71 * |
|
72 * @param string|null $pre_render The pre-rendered content. Default null. |
|
73 * @param array $block The block being rendered. |
|
74 * |
|
75 * @return null |
|
76 */ |
|
77 function _wp_add_block_level_preset_styles( $pre_render, $block ) { |
|
78 // Return early if the block has not support for descendent block styles. |
|
79 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
|
80 if ( ! block_has_support( $block_type, '__experimentalSettings', false ) ) { |
|
81 return null; |
|
82 } |
|
83 |
|
84 // return early if no settings are found on the block attributes. |
|
85 $block_settings = isset( $block['attrs']['settings'] ) ? $block['attrs']['settings'] : null; |
|
86 if ( empty( $block_settings ) ) { |
|
87 return null; |
|
88 } |
|
89 |
|
90 $class_name = '.' . _wp_get_presets_class_name( $block ); |
|
91 |
|
92 // the root selector for preset variables needs to target every possible block selector |
|
93 // in order for the general setting to override any bock specific setting of a parent block or |
|
94 // the site root. |
|
95 $variables_root_selector = '*,[class*="wp-block"]'; |
|
96 $registry = WP_Block_Type_Registry::get_instance(); |
|
97 $blocks = $registry->get_all_registered(); |
|
98 foreach ( $blocks as $block_type ) { |
|
99 /* |
|
100 * We only want to append selectors for blocks using custom selectors |
|
101 * i.e. not `wp-block-<name>`. |
|
102 */ |
|
103 $has_custom_selector = |
|
104 ( isset( $block_type->supports['__experimentalSelector'] ) && is_string( $block_type->supports['__experimentalSelector'] ) ) || |
|
105 ( isset( $block_type->selectors['root'] ) && is_string( $block_type->selectors['root'] ) ); |
|
106 |
|
107 if ( $has_custom_selector ) { |
|
108 $variables_root_selector .= ',' . wp_get_block_css_selector( $block_type ); |
|
109 } |
|
110 } |
|
111 $variables_root_selector = WP_Theme_JSON::scope_selector( $class_name, $variables_root_selector ); |
|
112 |
|
113 // Remove any potentially unsafe styles. |
|
114 $theme_json_shape = WP_Theme_JSON::remove_insecure_properties( |
|
115 array( |
|
116 'version' => WP_Theme_JSON::LATEST_SCHEMA, |
|
117 'settings' => $block_settings, |
|
118 ) |
|
119 ); |
|
120 $theme_json_object = new WP_Theme_JSON( $theme_json_shape ); |
|
121 |
|
122 $styles = ''; |
|
123 |
|
124 // include preset css variables declaration on the stylesheet. |
|
125 $styles .= $theme_json_object->get_stylesheet( |
|
126 array( 'variables' ), |
|
127 null, |
|
128 array( |
|
129 'root_selector' => $variables_root_selector, |
|
130 'scope' => $class_name, |
|
131 ) |
|
132 ); |
|
133 |
|
134 // include preset css classes on the the stylesheet. |
|
135 $styles .= $theme_json_object->get_stylesheet( |
|
136 array( 'presets' ), |
|
137 null, |
|
138 array( |
|
139 'root_selector' => $class_name . ',' . $class_name . ' *', |
|
140 'scope' => $class_name, |
|
141 ) |
|
142 ); |
|
143 |
|
144 if ( ! empty( $styles ) ) { |
|
145 wp_enqueue_block_support_styles( $styles ); |
|
146 } |
|
147 |
|
148 return null; |
|
149 } |
|
150 |
|
151 add_filter( 'render_block', '_wp_add_block_level_presets_class', 10, 2 ); |
|
152 add_filter( 'pre_render_block', '_wp_add_block_level_preset_styles', 10, 2 ); |