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

283 lines 9.9 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 $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 ( ( 'all' === $type || 'block_styles' === $type ) && WP_Theme_JSON_Resolver::theme_has_support() ) {
38 // To support all themes, we added in the block-library stylesheet
39 // a style rule such as .has-link-color a { color: var(--wp--style--color--link, #00e); }
40 // so that existing link colors themes used didn't break.
41 // We add this here to make it work for themes that opt-in to theme.json
42 // In the future, we may do this differently.
43 $stylesheet .= 'a{color:var(--wp--style--color--link, #00e);}';
44 }
45
46 if ( $can_use_cached ) {
47 // Cache for a minute.
48 // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites.
49 set_transient( 'global_styles', $stylesheet, MINUTE_IN_SECONDS );
50 }
51
52 return $stylesheet;
53 }
54
55 /**
56 * Fetches the preferences for each origin (core, theme, user)
57 * and enqueues the resulting stylesheet.
58 */
59 function gutenberg_experimental_global_styles_enqueue_assets() {
60 if (
61 ! get_theme_support( 'experimental-link-color' ) && // link color support needs the presets CSS variables regardless of the presence of theme.json file.
62 ! WP_Theme_JSON_Resolver::theme_has_support() ) {
63 return;
64 }
65
66 $settings = gutenberg_get_default_block_editor_settings();
67 $theme_support_data = WP_Theme_JSON::get_from_editor_settings( $settings );
68
69 $all = WP_Theme_JSON_Resolver::get_merged_data( $theme_support_data );
70
71 $stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $all );
72 if ( empty( $stylesheet ) ) {
73 return;
74 }
75
76 wp_register_style( 'global-styles', false, array(), true, true );
77 wp_add_inline_style( 'global-styles', $stylesheet );
78 wp_enqueue_style( 'global-styles' );
79 }
80
81 /**
82 * Adds the necessary data for the Global Styles client UI to the block settings.
83 *
84 * @param array $settings Existing block editor settings.
85 * @return array New block editor settings
86 */
87 function gutenberg_experimental_global_styles_settings( $settings ) {
88 $theme_support_data = WP_Theme_JSON::get_from_editor_settings( $settings );
89 unset( $settings['colors'] );
90 unset( $settings['disableCustomColors'] );
91 unset( $settings['disableCustomFontSizes'] );
92 unset( $settings['disableCustomGradients'] );
93 unset( $settings['enableCustomLineHeight'] );
94 unset( $settings['enableCustomUnits'] );
95 unset( $settings['enableCustomSpacing'] );
96 unset( $settings['fontSizes'] );
97 unset( $settings['gradients'] );
98
99 $origin = 'theme';
100 if (
101 WP_Theme_JSON_Resolver::theme_has_support() &&
102 gutenberg_supports_block_templates()
103 ) {
104 // Only lookup for the user data if we need it.
105 $origin = 'user';
106 }
107 $tree = WP_Theme_JSON_Resolver::get_merged_data( $theme_support_data, $origin );
108
109 // STEP 1: ADD FEATURES
110 //
111 // These need to be always added to the editor settings,
112 // even for themes that don't support theme.json.
113 // An example of this is that the presets are configured
114 // from the theme support data.
115 $settings['__experimentalFeatures'] = $tree->get_settings();
116
117 // STEP 2 - IF EDIT-SITE, ADD DATA REQUIRED FOR GLOBAL STYLES SIDEBAR
118 //
119 // In the site editor, the user can change styles, so the client
120 // needs the ability to create them. Hence, we pass it some data
121 // for this: base styles (core+theme) and the ID of the user CPT.
122 $screen = get_current_screen();
123 if (
124 ! empty( $screen ) &&
125 function_exists( 'gutenberg_is_edit_site_page' ) &&
126 gutenberg_is_edit_site_page( $screen->id ) &&
127 WP_Theme_JSON_Resolver::theme_has_support() &&
128 gutenberg_supports_block_templates()
129 ) {
130 $user_cpt_id = WP_Theme_JSON_Resolver::get_user_custom_post_type_id();
131 $base_styles = WP_Theme_JSON_Resolver::get_merged_data( $theme_support_data, 'theme' )->get_raw_data();
132
133 $settings['__experimentalGlobalStylesUserEntityId'] = $user_cpt_id;
134 $settings['__experimentalGlobalStylesBaseStyles'] = $base_styles;
135 } elseif (
136 WP_Theme_JSON_Resolver::theme_has_support() ||
137 get_theme_support( 'experimental-link-color' ) // link color support needs the presets CSS variables regardless of the presence of theme.json file.
138 ) {
139 // STEP 3 - ADD STYLES IF THEME HAS SUPPORT
140 //
141 // If we are in a block editor context, but not in edit-site,
142 // we add the styles via the settings, so the editor knows that
143 // some of these should be added the wrapper class,
144 // as if they were added via add_editor_styles.
145 $settings['styles'][] = array(
146 'css' => gutenberg_experimental_global_styles_get_stylesheet( $tree, 'css_variables' ),
147 '__experimentalNoWrapper' => true,
148 );
149 $settings['styles'][] = array(
150 'css' => gutenberg_experimental_global_styles_get_stylesheet( $tree, 'block_styles' ),
151 );
152 }
153
154 return $settings;
155 }
156
157 /**
158 * Register CPT to store/access user data.
159 *
160 * @return array|undefined
161 */
162 function gutenberg_experimental_global_styles_register_user_cpt() {
163 if ( ! WP_Theme_JSON_Resolver::theme_has_support() ) {
164 return;
165 }
166
167 WP_Theme_JSON_Resolver::register_user_custom_post_type();
168 }
169
170 add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' );
171 add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
172 add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
173
174
175 /**
176 * Sanitizes global styles user content removing unsafe rules.
177 *
178 * @param string $content Post content to filter.
179 * @return string Filtered post content with unsafe rules removed.
180 */
181 function gutenberg_global_styles_filter_post( $content ) {
182 $decoded_data = json_decode( stripslashes( $content ), true );
183 $json_decoding_error = json_last_error();
184 if (
185 JSON_ERROR_NONE === $json_decoding_error &&
186 is_array( $decoded_data ) &&
187 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
188 $decoded_data['isGlobalStylesUserThemeJSON']
189 ) {
190 unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
191 $theme_json = new WP_Theme_JSON( $decoded_data );
192 $theme_json->remove_insecure_properties();
193 $data_to_encode = $theme_json->get_raw_data();
194 $data_to_encode['isGlobalStylesUserThemeJSON'] = true;
195 return wp_json_encode( $data_to_encode );
196 }
197 return $content;
198 }
199
200 /**
201 * Adds the filters to filter global styles user theme.json.
202 */
203 function gutenberg_global_styles_kses_init_filters() {
204 add_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
205 }
206
207 /**
208 * Removes the filters to filter global styles user theme.json.
209 */
210 function gutenberg_global_styles_kses_remove_filters() {
211 remove_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' );
212 }
213
214 /**
215 * Register global styles kses filters if the user does not have unfiltered_html capability.
216 *
217 * @uses render_block_core_navigation()
218 * @throws WP_Error An WP_Error exception parsing the block definition.
219 */
220 function gutenberg_global_styles_kses_init() {
221 gutenberg_global_styles_kses_remove_filters();
222 if ( ! current_user_can( 'unfiltered_html' ) ) {
223 gutenberg_global_styles_kses_init_filters();
224 }
225 }
226
227 /**
228 * This filter is the last being executed on force_filtered_html_on_import.
229 * If the input of the filter is true it means we are in an import situation and should
230 * enable kses, independently of the user capabilities.
231 * So in that case we call gutenberg_global_styles_kses_init_filters;
232 *
233 * @param string $arg Input argument of the filter.
234 * @return string Exactly what was passed as argument.
235 */
236 function gutenberg_global_styles_force_filtered_html_on_import_filter( $arg ) {
237 // force_filtered_html_on_import is true we need to init the global styles kses filters.
238 if ( $arg ) {
239 gutenberg_global_styles_kses_init_filters();
240 }
241 return $arg;
242 }
243
244 /**
245 * This filter is the last being executed on force_filtered_html_on_import.
246 * If the input of the filter is true it means we are in an import situation and should
247 * enable kses, independently of the user capabilities.
248 * So in that case we call gutenberg_global_styles_kses_init_filters;
249 *
250 * @param bool $allow_css Whether the CSS in the test string is considered safe.
251 * @param bool $css_test_string The CSS string to test..
252 * @return bool If $allow_css is true it returns true.
253 * If $allow_css is false and the CSS rule is referencing a WordPress css variable it returns true.
254 * Otherwise the function return false.
255 */
256 function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $css_test_string ) {
257 if ( $allow_css ) {
258 return $allow_css;
259 }
260 $allowed_preset_attributes = array(
261 'background',
262 'background-color',
263 'border-color',
264 'color',
265 'font-family',
266 'font-size',
267 );
268 $parts = explode( ':', $css_test_string, 2 );
269
270 if ( ! in_array( trim( $parts[0] ), $allowed_preset_attributes, true ) ) {
271 return $allow_css;
272 }
273 return ! ! preg_match( '/^var\(--wp-[a-zA-Z0-9\-]+\)$/', trim( $parts[1] ) );
274 }
275
276
277 add_action( 'init', 'gutenberg_global_styles_kses_init' );
278 add_action( 'set_current_user', 'gutenberg_global_styles_kses_init' );
279 add_filter( 'force_filtered_html_on_import', 'gutenberg_global_styles_force_filtered_html_on_import_filter', 999 );
280 add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_global_styles_include_support_for_wp_variables', 10, 2 );
281 // This filter needs to be executed last.
282
283