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

313 lines 11.4 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 wp_register_style( 'global-styles', false, array(), true, true );
66 wp_add_inline_style( 'global-styles', $stylesheet );
67 wp_enqueue_style( 'global-styles' );
68 }
69
70 /**
71 * Adds the necessary settings for the Global Styles client UI.
72 *
73 * @param array $settings Existing block editor settings.
74 *
75 * @return array New block editor settings.
76 */
77 function gutenberg_experimental_global_styles_settings( $settings ) {
78 // Set what is the context for this data request.
79 $context = 'all';
80 if (
81 is_callable( 'get_current_screen' ) &&
82 function_exists( 'gutenberg_is_edit_site_page' ) &&
83 gutenberg_is_edit_site_page( get_current_screen()->id ) &&
84 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() &&
85 gutenberg_supports_block_templates()
86 ) {
87 $context = 'site-editor';
88 }
89
90 if (
91 defined( 'REST_REQUEST' ) &&
92 REST_REQUEST &&
93 isset( $_GET['context'] ) &&
94 'mobile' === $_GET['context'] &&
95 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support()
96 ) {
97 $context = 'mobile';
98 }
99
100 $origin = 'theme';
101 if (
102 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() &&
103 gutenberg_supports_block_templates()
104 ) {
105 // Only lookup for the user data if we need it.
106 $origin = 'user';
107 }
108 $consolidated = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings, $origin );
109
110 if ( 'mobile' === $context ) {
111 $settings['__experimentalStyles'] = $consolidated->get_raw_data()['styles'];
112 }
113
114 if ( 'site-editor' === $context ) {
115 $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings, 'theme' );
116 $user_cpt_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id();
117
118 $settings['__experimentalGlobalStylesUserEntityId'] = $user_cpt_id;
119 $settings['__experimentalGlobalStylesBaseStyles'] = $theme->get_raw_data();
120 }
121
122 if (
123 'site-editor' !== $context &&
124 'mobile' !== $context &&
125 ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() || get_theme_support( 'experimental-link-color' ) )
126 ) {
127 $block_styles = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $consolidated, 'block_styles' ) );
128 $css_variables = array(
129 'css' => gutenberg_experimental_global_styles_get_stylesheet( $consolidated, 'css_variables' ),
130 '__experimentalNoWrapper' => true,
131 );
132
133 $settings['styles'][] = $css_variables;
134 $settings['styles'][] = $block_styles;
135 }
136
137 // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php.
138 $settings['__experimentalFeatures'] = $consolidated->get_settings();
139 if ( isset( $settings['__experimentalFeatures']['color']['palette'] ) ) {
140 $settings['colors'] = $settings['__experimentalFeatures']['color']['palette'];
141 unset( $settings['__experimentalFeatures']['color']['palette'] );
142 }
143 if ( isset( $settings['__experimentalFeatures']['color']['gradients'] ) ) {
144 $settings['gradients'] = $settings['__experimentalFeatures']['color']['gradients'];
145 unset( $settings['__experimentalFeatures']['color']['gradients'] );
146 }
147 if ( isset( $settings['__experimentalFeatures']['color']['custom'] ) ) {
148 $settings['disableCustomColors'] = ! $settings['__experimentalFeatures']['color']['custom'];
149 unset( $settings['__experimentalFeatures']['color']['custom'] );
150 }
151 if ( isset( $settings['__experimentalFeatures']['color']['customGradient'] ) ) {
152 $settings['disableCustomGradients'] = ! $settings['__experimentalFeatures']['color']['customGradient'];
153 unset( $settings['__experimentalFeatures']['color']['customGradient'] );
154 }
155 if ( isset( $settings['__experimentalFeatures']['typography']['fontSizes'] ) ) {
156 $settings['fontSizes'] = $settings['__experimentalFeatures']['typography']['fontSizes'];
157 unset( $settings['__experimentalFeatures']['typography']['fontSizes'] );
158 }
159 if ( isset( $settings['__experimentalFeatures']['typography']['customFontSize'] ) ) {
160 $settings['disableCustomFontSizes'] = ! $settings['__experimentalFeatures']['typography']['customFontSize'];
161 unset( $settings['__experimentalFeatures']['typography']['customFontSize'] );
162 }
163 if ( isset( $settings['__experimentalFeatures']['typography']['customLineHeight'] ) ) {
164 $settings['enableCustomLineHeight'] = $settings['__experimentalFeatures']['typography']['customLineHeight'];
165 unset( $settings['__experimentalFeatures']['typography']['customLineHeight'] );
166 }
167 if ( isset( $settings['__experimentalFeatures']['spacing']['units'] ) ) {
168 if ( ! is_array( $settings['__experimentalFeatures']['spacing']['units'] ) ) {
169 $settings['enableCustomUnits'] = false;
170 } else {
171 $settings['enableCustomUnits'] = count( $settings['__experimentalFeatures']['spacing']['units'] ) > 0;
172 }
173 unset( $settings['__experimentalFeatures']['spacing']['units'] );
174 }
175 if ( isset( $settings['__experimentalFeatures']['spacing']['customPadding'] ) ) {
176 $settings['enableCustomSpacing'] = $settings['__experimentalFeatures']['spacing']['customPadding'];
177 unset( $settings['__experimentalFeatures']['spacing']['customPadding'] );
178 }
179
180 return $settings;
181 }
182
183 /**
184 * Register CPT to store/access user data.
185 *
186 * @return array|undefined
187 */
188 function gutenberg_experimental_global_styles_register_user_cpt() {
189 if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
190 return;
191 }
192
193 WP_Theme_JSON_Resolver_Gutenberg::register_user_custom_post_type();
194 }
195
196 /**
197 * Sanitizes global styles user content removing unsafe rules.
198 *
199 * @param string $content Post content to filter.
200 * @return string Filtered post content with unsafe rules removed.
201 */
202 function gutenberg_global_styles_filter_post( $content ) {
203 $decoded_data = json_decode( stripslashes( $content ), true );
204 $json_decoding_error = json_last_error();
205 if (
206 JSON_ERROR_NONE === $json_decoding_error &&
207 is_array( $decoded_data ) &&
208 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
209 $decoded_data['isGlobalStylesUserThemeJSON']
210 ) {
211 unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
212 $theme_json = new WP_Theme_JSON_Gutenberg( $decoded_data );
213 $theme_json->remove_insecure_properties();
214 $data_to_encode = $theme_json->get_raw_data();
215 $data_to_encode['isGlobalStylesUserThemeJSON'] = true;
216 return wp_json_encode( $data_to_encode );
217 }
218 return $content;
219 }
220
221 /**
222 * Adds the filters to filter global styles user theme.json.
223 */
224 function gutenberg_global_styles_kses_init_filters() {
225 add_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
226 }
227
228 /**
229 * Removes the filters to filter global styles user theme.json.
230 */
231 function gutenberg_global_styles_kses_remove_filters() {
232 remove_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
233 }
234
235 /**
236 * Register global styles kses filters if the user does not have unfiltered_html capability.
237 *
238 * @uses render_block_core_navigation()
239 * @throws WP_Error An WP_Error exception parsing the block definition.
240 */
241 function gutenberg_global_styles_kses_init() {
242 gutenberg_global_styles_kses_remove_filters();
243 if ( ! current_user_can( 'unfiltered_html' ) ) {
244 gutenberg_global_styles_kses_init_filters();
245 }
246 }
247
248 /**
249 * This filter is the last being executed on force_filtered_html_on_import.
250 * If the input of the filter is true it means we are in an import situation and should
251 * enable kses, independently of the user capabilities.
252 * So in that case we call gutenberg_global_styles_kses_init_filters;
253 *
254 * @param string $arg Input argument of the filter.
255 * @return string Exactly what was passed as argument.
256 */
257 function gutenberg_global_styles_force_filtered_html_on_import_filter( $arg ) {
258 // force_filtered_html_on_import is true we need to init the global styles kses filters.
259 if ( $arg ) {
260 gutenberg_global_styles_kses_init_filters();
261 }
262 return $arg;
263 }
264
265 /**
266 * This filter is the last being executed on force_filtered_html_on_import.
267 * If the input of the filter is true it means we are in an import situation and should
268 * enable kses, independently of the user capabilities.
269 * So in that case we call gutenberg_global_styles_kses_init_filters;
270 *
271 * @param bool $allow_css Whether the CSS in the test string is considered safe.
272 * @param bool $css_test_string The CSS string to test..
273 * @return bool If $allow_css is true it returns true.
274 * If $allow_css is false and the CSS rule is referencing a WordPress css variable it returns true.
275 * Otherwise the function return false.
276 */
277 function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $css_test_string ) {
278 if ( $allow_css ) {
279 return $allow_css;
280 }
281 $allowed_preset_attributes = array(
282 'background',
283 'background-color',
284 'border-color',
285 'color',
286 'font-family',
287 'font-size',
288 );
289 $parts = explode( ':', $css_test_string, 2 );
290
291 if ( ! in_array( trim( $parts[0] ), $allowed_preset_attributes, true ) ) {
292 return $allow_css;
293 }
294 return ! ! preg_match( '/^var\(--wp-[a-zA-Z0-9\-]+\)$/', trim( $parts[1] ) );
295 }
296
297 // The else clause can be removed when plugin support requires WordPress 5.8.0+.
298 if ( function_exists( 'get_block_editor_settings' ) ) {
299 add_filter( 'block_editor_settings_all', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
300 } else {
301 add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
302 }
303
304 add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' );
305 add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
306
307 // kses actions&filters.
308 add_action( 'init', 'gutenberg_global_styles_kses_init' );
309 add_action( 'set_current_user', 'gutenberg_global_styles_kses_init' );
310 add_filter( 'force_filtered_html_on_import', 'gutenberg_global_styles_force_filtered_html_on_import_filter', 999 );
311 add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_global_styles_include_support_for_wp_variables', 10, 2 );
312 // This filter needs to be executed last.
313