PluginProbe
Gutenberg / 10.9.1
Gutenberg v10.9.1
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 10.9.1, at lib/global-styles.php

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