PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.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
← All changes | lib/global-styles.php +263 -88 7.4.012.1.0 View file →
@@ -1,131 +1,306 @@
1 1 <?php
2 2 /**
3 - * Global styles functions, filters and actions, etc.
3 + * Bootstraps Global Styles.
4 4 *
5 5 * @package gutenberg
6 6 */
7 7
8 8 /**
9 - * Function that given a branch of the global styles object recursively generates
10 - * an array defining all the css vars that the global styles object represents.
11 - *
12 - * @param array $global_styles_branch Array representing a brach of the global styles object.
13 - * @param string $prefix Prefix to append to each variable.
14 - * @param bool $is_start Indicates if we are on the first call to gutenberg_get_css_vars (outside the recursion).
15 - * @return array An array whose keys are css variable names and whose values are the css variables value.
9 + * Fetches the preferences for each origin (core, theme, user)
10 + * and enqueues the resulting stylesheet.
16 11 */
17 -function gutenberg_get_css_vars( $global_styles_branch, $prefix = '', $is_start = true ) {
18 - $result = array();
19 - foreach ( $global_styles_branch as $key => $value ) {
20 - $processed_key = str_replace( '/', '-', $key );
21 - $separator = $is_start ? '' : '--';
22 - $new_key = ( $prefix ? $prefix . $separator : '' ) . $processed_key;
12 +function gutenberg_experimental_global_styles_enqueue_assets() {
13 + $stylesheet = wp_get_global_stylesheet();
14 + if ( empty( $stylesheet ) ) {
15 + return;
16 + }
23 17
24 - if ( is_array( $value ) ) {
25 - $result = array_merge(
26 - $result,
27 - gutenberg_get_css_vars( $value, $new_key, false )
28 - );
29 - } else {
30 - $result[ $new_key ] = $value;
31 - }
18 + if ( isset( wp_styles()->registered['global-styles'] ) ) {
19 + wp_styles()->registered['global-styles']->extra['after'][0] = $stylesheet;
20 + } else {
21 + wp_register_style( 'global-styles', false, array(), true, true );
22 + wp_add_inline_style( 'global-styles', $stylesheet );
23 + wp_enqueue_style( 'global-styles' );
32 24 }
33 - return $result;
34 25 }
35 26
36 27 /**
37 - * Function responsible for enqueuing the style that define the global styles css variables.
28 + * Adds the necessary settings for the Global Styles client UI.
29 + *
30 + * @param array $settings Existing block editor settings.
31 + *
32 + * @return array New block editor settings.
38 33 */
39 -function gutenberg_enqueue_global_styles_assets() {
40 - if ( ! locate_template( 'experimental-theme.json' ) ) {
41 - return;
34 +function gutenberg_experimental_global_styles_settings( $settings ) {
35 + // Set what is the context for this data request.
36 + $context = 'other';
37 + if (
38 + is_callable( 'get_current_screen' ) &&
39 + function_exists( 'gutenberg_is_edit_site_page' ) &&
40 + gutenberg_is_edit_site_page( get_current_screen()->id )
41 + ) {
42 + $context = 'site-editor';
42 43 }
43 - $default_global_styles_path = dirname( dirname( __FILE__ ) ) . '/experimental-default-global-styles.json';
44 - $default_global_styles = null;
45 44
46 - if ( file_exists( $default_global_styles_path ) ) {
47 - $default_global_styles = json_decode(
48 - file_get_contents( $default_global_styles_path ),
49 - true
45 + if (
46 + defined( 'REST_REQUEST' ) &&
47 + REST_REQUEST &&
48 + isset( $_GET['context'] ) &&
49 + 'mobile' === $_GET['context']
50 + ) {
51 + $context = 'mobile';
52 + }
53 +
54 + if ( 'mobile' === $context && WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
55 + $settings['__experimentalStyles'] = wp_get_global_styles();
56 + }
57 +
58 + if ( 'other' === $context ) {
59 + // Make sure the styles array exists.
60 + // In some contexts, like the navigation editor, it doesn't.
61 + if ( ! isset( $settings['styles'] ) ) {
62 + $settings['styles'] = array();
63 + }
64 +
65 + // Reset existing global styles.
66 + $styles_without_existing_global_styles = array();
67 + foreach ( $settings['styles'] as $style ) {
68 + if ( ! isset( $style['__unstableType'] ) || 'globalStyles' !== $style['__unstableType'] ) {
69 + $styles_without_existing_global_styles[] = $style;
70 + }
71 + }
72 +
73 + $new_global_styles = array();
74 + $new_presets = array(
75 + array(
76 + 'css' => 'variables',
77 + '__unstableType' => 'presets',
78 + '__experimentalNoWrapper' => true,
79 + ),
80 + array(
81 + 'css' => 'presets',
82 + '__unstableType' => 'presets',
83 + ),
50 84 );
85 + foreach ( $new_presets as $new_style ) {
86 + $style_css = wp_get_global_stylesheet( array( $new_style['css'] ) );
87 + if ( '' !== $style_css ) {
88 + $new_style['css'] = $style_css;
89 + $new_global_styles[] = $new_style;
90 + }
91 + }
92 +
93 + $new_block_classes = array(
94 + 'css' => 'styles',
95 + '__unstableType' => 'theme',
96 + );
97 + if ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
98 + $style_css = wp_get_global_stylesheet( array( $new_block_classes['css'] ) );
99 + if ( '' !== $style_css ) {
100 + $new_block_classes['css'] = $style_css;
101 + $new_global_styles[] = $new_block_classes;
102 + }
103 + }
104 +
105 + $settings['styles'] = array_merge( $styles_without_existing_global_styles, $new_global_styles );
51 106 }
52 107
53 - $theme_json_path = locate_template( 'experimental-theme.json' );
54 - $theme_json_global_styles = null;
55 - if ( $theme_json_path ) {
56 - $theme_json_global_styles = json_decode(
57 - file_get_contents( $theme_json_path ),
58 - true
59 - );
108 + // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php.
109 + $settings['__experimentalFeatures'] = wp_get_global_settings();
110 +
111 + if ( isset( $settings['__experimentalFeatures']['color']['palette'] ) ) {
112 + $colors_by_origin = $settings['__experimentalFeatures']['color']['palette'];
113 + $settings['colors'] = isset( $colors_by_origin['custom'] ) ?
114 + $colors_by_origin['custom'] : (
115 + isset( $colors_by_origin['theme'] ) ?
116 + $colors_by_origin['theme'] :
117 + $colors_by_origin['default']
118 + );
60 119 }
61 120
62 - // To-do: Load user customizations from a CPT.
63 - $css_vars = array();
64 - foreach (
65 - array(
66 - $default_global_styles,
67 - $theme_json_global_styles,
68 - ) as $global_styles_definition
69 - ) {
70 - if ( ! $global_styles_definition ) {
71 - continue;
72 - }
73 - if ( isset( $global_styles_definition['global'] ) ) {
74 - $css_vars = array_merge(
75 - $css_vars,
76 - gutenberg_get_css_vars( $global_styles_definition['global'], '--wp-' )
121 + if ( isset( $settings['__experimentalFeatures']['color']['gradients'] ) ) {
122 + $gradients_by_origin = $settings['__experimentalFeatures']['color']['gradients'];
123 + $settings['gradients'] = isset( $gradients_by_origin['custom'] ) ?
124 + $gradients_by_origin['custom'] : (
125 + isset( $gradients_by_origin['theme'] ) ?
126 + $gradients_by_origin['theme'] :
127 + $gradients_by_origin['default']
77 128 );
78 - }
79 - if ( isset( $global_styles_definition['blocks'] ) ) {
80 - $css_vars = array_merge(
81 - $css_vars,
82 - gutenberg_get_css_vars( $global_styles_definition['blocks'], '--wp-block-' )
129 + }
130 +
131 + if ( isset( $settings['__experimentalFeatures']['typography']['fontSizes'] ) ) {
132 + $font_sizes_by_origin = $settings['__experimentalFeatures']['typography']['fontSizes'];
133 + $settings['fontSizes'] = isset( $font_sizes_by_origin['custom'] ) ?
134 + $font_sizes_by_origin['user'] : (
135 + isset( $font_sizes_by_origin['theme'] ) ?
136 + $font_sizes_by_origin['theme'] :
137 + $font_sizes_by_origin['default']
83 138 );
84 - }
85 139 }
86 140
87 - if ( empty( $css_vars ) ) {
88 - return;
141 + if ( isset( $settings['__experimentalFeatures']['color']['custom'] ) ) {
142 + $settings['disableCustomColors'] = ! $settings['__experimentalFeatures']['color']['custom'];
143 + unset( $settings['__experimentalFeatures']['color']['custom'] );
89 144 }
145 + if ( isset( $settings['__experimentalFeatures']['color']['customGradient'] ) ) {
146 + $settings['disableCustomGradients'] = ! $settings['__experimentalFeatures']['color']['customGradient'];
147 + unset( $settings['__experimentalFeatures']['color']['customGradient'] );
148 + }
149 + if ( isset( $settings['__experimentalFeatures']['typography']['customFontSize'] ) ) {
150 + $settings['disableCustomFontSizes'] = ! $settings['__experimentalFeatures']['typography']['customFontSize'];
151 + unset( $settings['__experimentalFeatures']['typography']['customFontSize'] );
152 + }
153 + if ( isset( $settings['__experimentalFeatures']['typography']['lineHeight'] ) ) {
154 + $settings['enableCustomLineHeight'] = $settings['__experimentalFeatures']['typography']['lineHeight'];
155 + unset( $settings['__experimentalFeatures']['typography']['lineHeight'] );
156 + }
157 + if ( isset( $settings['__experimentalFeatures']['spacing']['units'] ) ) {
158 + $settings['enableCustomUnits'] = $settings['__experimentalFeatures']['spacing']['units'];
159 + }
160 + if ( isset( $settings['__experimentalFeatures']['spacing']['padding'] ) ) {
161 + $settings['enableCustomSpacing'] = $settings['__experimentalFeatures']['spacing']['padding'];
162 + unset( $settings['__experimentalFeatures']['spacing']['padding'] );
163 + }
90 164
91 - $inline_style = ":root {\n";
92 - foreach ( $css_vars as $var => $value ) {
93 - $inline_style .= "\t" . $var . ': ' . $value . ";\n";
165 + return $settings;
166 +}
167 +
168 +/**
169 + * Whether or not the Site Editor is available.
170 + *
171 + * @return boolean
172 + */
173 +function gutenberg_experimental_is_site_editor_available() {
174 + return gutenberg_is_fse_theme();
175 +}
176 +
177 +/**
178 + * Register CPT to store/access user data.
179 + *
180 + * @return void
181 + */
182 +function gutenberg_experimental_global_styles_register_user_cpt() {
183 + if ( gutenberg_experimental_is_site_editor_available() ) {
184 + register_global_styles_custom_post_type();
94 185 }
95 - $inline_style .= '}';
186 +}
96 187
97 - wp_register_style( 'global-styles', false, array(), true, true );
98 - wp_add_inline_style( 'global-styles', $inline_style );
99 - wp_enqueue_style( 'global-styles' );
188 +/**
189 + * Sanitizes global styles user content removing unsafe rules.
190 + *
191 + * @param string $content Post content to filter.
192 + * @return string Filtered post content with unsafe rules removed.
193 + */
194 +function gutenberg_global_styles_filter_post( $content ) {
195 + $decoded_data = json_decode( wp_unslash( $content ), true );
196 + $json_decoding_error = json_last_error();
197 + if (
198 + JSON_ERROR_NONE === $json_decoding_error &&
199 + is_array( $decoded_data ) &&
200 + isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
201 + $decoded_data['isGlobalStylesUserThemeJSON']
202 + ) {
203 + unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
204 +
205 + $data_to_encode = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $decoded_data );
206 +
207 + $data_to_encode['isGlobalStylesUserThemeJSON'] = true;
208 + return wp_slash( wp_json_encode( $data_to_encode ) );
209 + }
210 + return $content;
100 211 }
101 -add_action( 'enqueue_block_assets', 'gutenberg_enqueue_global_styles_assets' );
102 212
103 213 /**
104 - * Adds class wp-gs to the frontend body class if the theme defines a experimental-theme.json.
214 + * Adds the filters to filter global styles user theme.json.
215 + */
216 +function gutenberg_global_styles_kses_init_filters() {
217 + add_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
218 +}
219 +
220 +/**
221 + * Removes the filters to filter global styles user theme.json.
222 + */
223 +function gutenberg_global_styles_kses_remove_filters() {
224 + remove_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
225 +}
226 +
227 +/**
228 + * Register global styles kses filters if the user does not have unfiltered_html capability.
105 229 *
106 - * @param array $classes Existing body classes.
107 - * @return array The filtered array of body classes.
230 + * @uses render_block_core_navigation()
231 + * @throws WP_Error An WP_Error exception parsing the block definition.
108 232 */
109 -function gutenberg_add_wp_gs_class_front_end( $classes ) {
110 - if ( locate_template( 'experimental-theme.json' ) ) {
111 - return array_merge( $classes, array( 'wp-gs' ) );
233 +function gutenberg_global_styles_kses_init() {
234 + gutenberg_global_styles_kses_remove_filters();
235 + if ( ! current_user_can( 'unfiltered_html' ) ) {
236 + gutenberg_global_styles_kses_init_filters();
112 237 }
113 - return $classes;
114 238 }
115 -add_filter( 'body_class', 'gutenberg_add_wp_gs_class_front_end' );
116 239
240 +/**
241 + * This filter is the last being executed on force_filtered_html_on_import.
242 + * If the input of the filter is true it means we are in an import situation and should
243 + * enable kses, independently of the user capabilities.
244 + * So in that case we call gutenberg_global_styles_kses_init_filters;
245 + *
246 + * @param string $arg Input argument of the filter.
247 + * @return string Exactly what was passed as argument.
248 + */
249 +function gutenberg_global_styles_force_filtered_html_on_import_filter( $arg ) {
250 + // force_filtered_html_on_import is true we need to init the global styles kses filters.
251 + if ( $arg ) {
252 + gutenberg_global_styles_kses_init_filters();
253 + }
254 + return $arg;
255 +}
117 256
257 +// TODO: Remove this filter when minimum supported version is WP 5.8
258 +// As all this code is not needed anymore now core supports all variables.
118 259 /**
119 - * Adds class wp-gs to the block-editor body class if the theme defines a experimental-theme.json.
260 + * This filter is the last being executed on force_filtered_html_on_import.
261 + * If the input of the filter is true it means we are in an import situation and should
262 + * enable kses, independently of the user capabilities.
263 + * So in that case we call gutenberg_global_styles_kses_init_filters;
120 264 *
121 - * @param string $classes Existing body classes separated by space.
122 - * @return string The filtered string of body classes.
265 + * @param bool $allow_css Whether the CSS in the test string is considered safe.
266 + * @param bool $css_test_string The CSS string to test..
267 + * @return bool If $allow_css is true it returns true.
268 + * If $allow_css is false and the CSS rule is referencing a WordPress css variable it returns true.
269 + * Otherwise the function return false.
123 270 */
124 -function gutenberg_add_wp_gs_class_editor( $classes ) {
125 - global $current_screen;
126 - if ( $current_screen->is_block_editor() && locate_template( 'experimental-theme.json' ) ) {
127 - return $classes . ' wp-gs';
271 +function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $css_test_string ) {
272 + if ( $allow_css ) {
273 + return $allow_css;
128 274 }
129 - return $classes;
275 + $allowed_preset_attributes = array(
276 + 'background',
277 + 'background-color',
278 + 'border-color',
279 + 'color',
280 + 'font-family',
281 + 'font-size',
282 + );
283 + $parts = explode( ':', $css_test_string, 2 );
284 +
285 + if ( ! in_array( trim( $parts[0] ), $allowed_preset_attributes, true ) ) {
286 + return $allow_css;
287 + }
288 + return ! ! preg_match( '/^var\(--wp-[a-zA-Z0-9\-]+\)$/', trim( $parts[1] ) );
130 289 }
131 -add_filter( 'admin_body_class', 'gutenberg_add_wp_gs_class_editor' );
290 +
291 +// The else clause can be removed when plugin support requires WordPress 5.8.0+.
292 +if ( function_exists( 'get_block_editor_settings' ) ) {
293 + add_filter( 'block_editor_settings_all', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
294 +} else {
295 + add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
296 +}
297 +
298 +add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' );
299 +add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
300 +
301 +// kses actions&filters.
302 +add_action( 'init', 'gutenberg_global_styles_kses_init' );
303 +add_action( 'set_current_user', 'gutenberg_global_styles_kses_init' );
304 +add_filter( 'force_filtered_html_on_import', 'gutenberg_global_styles_force_filtered_html_on_import_filter', 999 );
305 +add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_global_styles_include_support_for_wp_variables', 10, 2 );
306 +// This filter needs to be executed last.