PluginProbe
Gutenberg / 10.6.0
Gutenberg v10.6.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
gutenberg / lib / global-styles.php

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

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