PluginProbe
Gutenberg / 11.6.0
Gutenberg v11.6.0
24.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 All 403 releases
gutenberg / lib / global-styles.php

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

364 lines 13.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 * Takes a tree adhering to the theme.json schema and generates
10 * the corresponding stylesheet.
11 *
12 * @param WP_Theme_JSON_Gutenberg $tree Input tree.
13 * @param string $type Type of stylesheet. It accepts 'all', 'block_styles', 'css_variables', 'presets'.
14 *
15 * @return string Stylesheet.
16 */
17 function gutenberg_experimental_global_styles_get_stylesheet( $tree, $type = null ) {
18 // Check if we can use cached.
19 $can_use_cached = (
20 ( 'all' === $type ) &&
21 ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) &&
22 ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) &&
23 ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) &&
24 ! is_admin()
25 );
26
27 $transient_name = 'gutenberg_global_styles_' . get_stylesheet();
28 if ( $can_use_cached ) {
29 // Check if we have the styles already cached.
30 // It's cached by theme to make sure that theme switching
31 // is inmediately reflected.
32 $cached = get_transient( $transient_name );
33 if ( $cached ) {
34 return $cached;
35 }
36 }
37
38 $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support();
39 $supports_link_color = get_theme_support( 'experimental-link-color' );
40
41 // Only modify the $type if the consumer hasn't provided any.
42 if ( null === $type && ! $supports_theme_json ) {
43 $type = 'presets';
44 } elseif ( null === $type ) {
45 $type = 'all';
46 }
47
48 $origins = array( 'core', 'theme', 'user' );
49 if ( ! $supports_theme_json && ! $supports_link_color ) {
50 // In this case we only enqueue the core presets (CSS Custom Properties + the classes).
51 $origins = array( 'core' );
52 } elseif ( ! $supports_theme_json && $supports_link_color ) {
53 // For the legacy link color feauter to work, the CSS Custom Properties
54 // should be in scope (either the core or the theme ones).
55 $origins = array( 'core', 'theme' );
56 }
57
58 $stylesheet = $tree->get_stylesheet( $type, $origins );
59
60 if ( $can_use_cached ) {
61 // Cache for a minute.
62 // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites.
63 set_transient( $transient_name, $stylesheet, MINUTE_IN_SECONDS );
64 }
65
66 return $stylesheet;
67 }
68
69 /**
70 * Fetches the preferences for each origin (core, theme, user)
71 * and enqueues the resulting stylesheet.
72 */
73 function gutenberg_experimental_global_styles_enqueue_assets() {
74 $settings = gutenberg_get_default_block_editor_settings();
75 $all = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings );
76
77 $stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $all );
78 if ( empty( $stylesheet ) ) {
79 return;
80 }
81
82 if ( isset( wp_styles()->registered['global-styles'] ) ) {
83 wp_styles()->registered['global-styles']->extra['after'][0] = $stylesheet;
84 } else {
85 wp_register_style( 'global-styles', false, array(), true, true );
86 wp_add_inline_style( 'global-styles', $stylesheet );
87 wp_enqueue_style( 'global-styles' );
88 }
89 }
90
91 /**
92 * Adds the necessary settings for the Global Styles client UI.
93 *
94 * @param array $settings Existing block editor settings.
95 *
96 * @return array New block editor settings.
97 */
98 function gutenberg_experimental_global_styles_settings( $settings ) {
99 // Set what is the context for this data request.
100 $context = 'all';
101 if (
102 is_callable( 'get_current_screen' ) &&
103 function_exists( 'gutenberg_is_edit_site_page' ) &&
104 gutenberg_is_edit_site_page( get_current_screen()->id ) &&
105 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() &&
106 gutenberg_supports_block_templates()
107 ) {
108 $context = 'site-editor';
109 }
110
111 if (
112 defined( 'REST_REQUEST' ) &&
113 REST_REQUEST &&
114 isset( $_GET['context'] ) &&
115 'mobile' === $_GET['context'] &&
116 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support()
117 ) {
118 $context = 'mobile';
119 }
120
121 $origin = 'theme';
122 if (
123 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() &&
124 gutenberg_supports_block_templates()
125 ) {
126 // Only lookup for the user data if we need it.
127 $origin = 'user';
128 }
129 $consolidated = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings, $origin );
130
131 if ( 'mobile' === $context ) {
132 $settings['__experimentalStyles'] = $consolidated->get_raw_data()['styles'];
133 }
134
135 if ( 'site-editor' === $context ) {
136 $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings, 'theme' );
137 $user_cpt_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id();
138
139 $settings['__experimentalGlobalStylesUserEntityId'] = $user_cpt_id;
140 $settings['__experimentalGlobalStylesBaseStyles'] = $theme->get_raw_data();
141 }
142
143 if (
144 'site-editor' !== $context &&
145 'mobile' !== $context &&
146 ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() || get_theme_support( 'experimental-link-color' ) )
147 ) {
148 $block_styles = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $consolidated, 'block_styles' ) );
149 $css_variables = array(
150 'css' => gutenberg_experimental_global_styles_get_stylesheet( $consolidated, 'css_variables' ),
151 '__experimentalNoWrapper' => true,
152 );
153
154 // Make sure the styles array exists.
155 // In some contexts, like the navigation editor, it doesn't.
156 if ( ! isset( $settings['styles'] ) ) {
157 $settings['styles'] = array();
158 }
159
160 // Reset existing global styles.
161 $styles_without_existing_global_styles = array();
162 foreach ( $settings['styles'] as $style ) {
163 if ( ! isset( $style['__unstableType'] ) || 'globalStyles' !== $style['__unstableType'] ) {
164 $styles_without_existing_global_styles[] = $style;
165 }
166 }
167
168 // Add the new ones.
169 $styles_without_existing_global_styles[] = $css_variables;
170 $styles_without_existing_global_styles[] = $block_styles;
171 $settings['styles'] = $styles_without_existing_global_styles;
172 }
173
174 // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php.
175 $settings['__experimentalFeatures'] = $consolidated->get_settings();
176
177 if ( isset( $settings['__experimentalFeatures']['color']['palette'] ) ) {
178 $colors_by_origin = $settings['__experimentalFeatures']['color']['palette'];
179 $settings['colors'] = isset( $colors_by_origin['user'] ) ?
180 $colors_by_origin['user'] : (
181 isset( $colors_by_origin['theme'] ) ?
182 $colors_by_origin['theme'] :
183 $colors_by_origin['core']
184 );
185 }
186
187 if ( isset( $settings['__experimentalFeatures']['color']['gradients'] ) ) {
188 $gradients_by_origin = $settings['__experimentalFeatures']['color']['gradients'];
189 $settings['gradients'] = isset( $gradients_by_origin['user'] ) ?
190 $gradients_by_origin['user'] : (
191 isset( $gradients_by_origin['theme'] ) ?
192 $gradients_by_origin['theme'] :
193 $gradients_by_origin['core']
194 );
195 }
196
197 if ( isset( $settings['__experimentalFeatures']['typography']['fontSizes'] ) ) {
198 $font_sizes_by_origin = $settings['__experimentalFeatures']['typography']['fontSizes'];
199 $settings['fontSizes'] = isset( $font_sizes_by_origin['user'] ) ?
200 $font_sizes_by_origin['user'] : (
201 isset( $font_sizes_by_origin['theme'] ) ?
202 $font_sizes_by_origin['theme'] :
203 $font_sizes_by_origin['core']
204 );
205 }
206
207 if ( isset( $settings['__experimentalFeatures']['color']['custom'] ) ) {
208 $settings['disableCustomColors'] = ! $settings['__experimentalFeatures']['color']['custom'];
209 unset( $settings['__experimentalFeatures']['color']['custom'] );
210 }
211 if ( isset( $settings['__experimentalFeatures']['color']['customGradient'] ) ) {
212 $settings['disableCustomGradients'] = ! $settings['__experimentalFeatures']['color']['customGradient'];
213 unset( $settings['__experimentalFeatures']['color']['customGradient'] );
214 }
215 if ( isset( $settings['__experimentalFeatures']['typography']['customFontSize'] ) ) {
216 $settings['disableCustomFontSizes'] = ! $settings['__experimentalFeatures']['typography']['customFontSize'];
217 unset( $settings['__experimentalFeatures']['typography']['customFontSize'] );
218 }
219 if ( isset( $settings['__experimentalFeatures']['typography']['customLineHeight'] ) ) {
220 $settings['enableCustomLineHeight'] = $settings['__experimentalFeatures']['typography']['customLineHeight'];
221 unset( $settings['__experimentalFeatures']['typography']['customLineHeight'] );
222 }
223 if ( isset( $settings['__experimentalFeatures']['spacing']['units'] ) ) {
224 $settings['enableCustomUnits'] = $settings['__experimentalFeatures']['spacing']['units'];
225 }
226 if ( isset( $settings['__experimentalFeatures']['spacing']['customPadding'] ) ) {
227 $settings['enableCustomSpacing'] = $settings['__experimentalFeatures']['spacing']['customPadding'];
228 unset( $settings['__experimentalFeatures']['spacing']['customPadding'] );
229 }
230
231 return $settings;
232 }
233
234 /**
235 * Register CPT to store/access user data.
236 *
237 * @return array|undefined
238 */
239 function gutenberg_experimental_global_styles_register_user_cpt() {
240 if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
241 return;
242 }
243
244 WP_Theme_JSON_Resolver_Gutenberg::register_user_custom_post_type();
245 }
246
247 /**
248 * Sanitizes global styles user content removing unsafe rules.
249 *
250 * @param string $content Post content to filter.
251 * @return string Filtered post content with unsafe rules removed.
252 */
253 function gutenberg_global_styles_filter_post( $content ) {
254 $decoded_data = json_decode( wp_unslash( $content ), true );
255 $json_decoding_error = json_last_error();
256 if (
257 JSON_ERROR_NONE === $json_decoding_error &&
258 is_array( $decoded_data ) &&
259 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
260 $decoded_data['isGlobalStylesUserThemeJSON']
261 ) {
262 unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
263
264 $data_to_encode = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $decoded_data );
265
266 $data_to_encode['isGlobalStylesUserThemeJSON'] = true;
267 return wp_slash( wp_json_encode( $data_to_encode ) );
268 }
269 return $content;
270 }
271
272 /**
273 * Adds the filters to filter global styles user theme.json.
274 */
275 function gutenberg_global_styles_kses_init_filters() {
276 add_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
277 }
278
279 /**
280 * Removes the filters to filter global styles user theme.json.
281 */
282 function gutenberg_global_styles_kses_remove_filters() {
283 remove_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
284 }
285
286 /**
287 * Register global styles kses filters if the user does not have unfiltered_html capability.
288 *
289 * @uses render_block_core_navigation()
290 * @throws WP_Error An WP_Error exception parsing the block definition.
291 */
292 function gutenberg_global_styles_kses_init() {
293 gutenberg_global_styles_kses_remove_filters();
294 if ( ! current_user_can( 'unfiltered_html' ) ) {
295 gutenberg_global_styles_kses_init_filters();
296 }
297 }
298
299 /**
300 * This filter is the last being executed on force_filtered_html_on_import.
301 * If the input of the filter is true it means we are in an import situation and should
302 * enable kses, independently of the user capabilities.
303 * So in that case we call gutenberg_global_styles_kses_init_filters;
304 *
305 * @param string $arg Input argument of the filter.
306 * @return string Exactly what was passed as argument.
307 */
308 function gutenberg_global_styles_force_filtered_html_on_import_filter( $arg ) {
309 // force_filtered_html_on_import is true we need to init the global styles kses filters.
310 if ( $arg ) {
311 gutenberg_global_styles_kses_init_filters();
312 }
313 return $arg;
314 }
315
316 /**
317 * This filter is the last being executed on force_filtered_html_on_import.
318 * If the input of the filter is true it means we are in an import situation and should
319 * enable kses, independently of the user capabilities.
320 * So in that case we call gutenberg_global_styles_kses_init_filters;
321 *
322 * @param bool $allow_css Whether the CSS in the test string is considered safe.
323 * @param bool $css_test_string The CSS string to test..
324 * @return bool If $allow_css is true it returns true.
325 * If $allow_css is false and the CSS rule is referencing a WordPress css variable it returns true.
326 * Otherwise the function return false.
327 */
328 function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $css_test_string ) {
329 if ( $allow_css ) {
330 return $allow_css;
331 }
332 $allowed_preset_attributes = array(
333 'background',
334 'background-color',
335 'border-color',
336 'color',
337 'font-family',
338 'font-size',
339 );
340 $parts = explode( ':', $css_test_string, 2 );
341
342 if ( ! in_array( trim( $parts[0] ), $allowed_preset_attributes, true ) ) {
343 return $allow_css;
344 }
345 return ! ! preg_match( '/^var\(--wp-[a-zA-Z0-9\-]+\)$/', trim( $parts[1] ) );
346 }
347
348 // The else clause can be removed when plugin support requires WordPress 5.8.0+.
349 if ( function_exists( 'get_block_editor_settings' ) ) {
350 add_filter( 'block_editor_settings_all', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
351 } else {
352 add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
353 }
354
355 add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' );
356 add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
357
358 // kses actions&filters.
359 add_action( 'init', 'gutenberg_global_styles_kses_init' );
360 add_action( 'set_current_user', 'gutenberg_global_styles_kses_init' );
361 add_filter( 'force_filtered_html_on_import', 'gutenberg_global_styles_force_filtered_html_on_import_filter', 999 );
362 add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_global_styles_include_support_for_wp_variables', 10, 2 );
363 // This filter needs to be executed last.
364