PluginProbe
Gutenberg / 7.7.2
Gutenberg v7.7.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 7.7.2, at lib/global-styles.php

343 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Bootstraping Global Styles.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Whether the current theme has support for Global Styles.
10 *
11 * @return boolean
12 */
13 function gutenberg_experimental_global_styles_has_theme_support() {
14 return is_readable( locate_template( 'experimental-theme.json' ) );
15 }
16
17 /**
18 * Given a Global Styles tree, it creates a flattened tree
19 * whose keys are the CSS custom properties
20 * and its values the CSS custom properties' values.
21 *
22 * @param array $global_styles Global Styles object to process.
23 * @param string $prefix Prefix to prepend to each variable.
24 * @param string $token Token to use between levels.
25 *
26 * @return array The flattened tree.
27 */
28 function gutenberg_experimental_global_styles_get_css_vars( $global_styles, $prefix = '', $token = '--' ) {
29 $result = array();
30 foreach ( $global_styles as $key => $value ) {
31 $new_key = $prefix . str_replace( '/', '-', $key );
32
33 if ( is_array( $value ) ) {
34 $new_prefix = $new_key . $token;
35 $result = array_merge(
36 $result,
37 gutenberg_experimental_global_styles_get_css_vars( $value, $new_prefix, $token )
38 );
39 } else {
40 $result[ $new_key ] = $value;
41 }
42 }
43 return $result;
44 }
45
46 /**
47 * Returns an array containing the Global Styles
48 * found in a file, or a void array if none found.
49 *
50 * @param string $global_styles_path Path to file.
51 * @return array Global Styles tree.
52 */
53 function gutenberg_experimental_global_styles_get_from_file( $global_styles_path ) {
54 $global_styles = array();
55 if ( file_exists( $global_styles_path ) ) {
56 $decoded_file = json_decode(
57 file_get_contents( $global_styles_path ),
58 true
59 );
60 if ( is_array( $decoded_file ) ) {
61 $global_styles = $decoded_file;
62 }
63 }
64 return $global_styles;
65 }
66
67 /**
68 * Returns an array containing the user's Global Styles
69 * or a void array if none.
70 *
71 * @return array Global Styles tree.
72 */
73 function gutenberg_experimental_global_styles_get_user() {
74 $global_styles = array();
75 $user_cpt = gutenberg_experimental_global_styles_get_user_cpt( array( 'publish' ) );
76 if ( array_key_exists( 'post_content', $user_cpt ) ) {
77 $decoded_data = json_decode( $user_cpt['post_content'], true );
78 if ( is_array( $decoded_data ) ) {
79 $global_styles = $decoded_data;
80 }
81 }
82
83 return $global_styles;
84 }
85
86 /**
87 * Returns the CPT that contains the user's Global Styles
88 * for the current theme or a void array if none found.
89 * It can also create and return a new draft CPT.
90 *
91 * @param array $post_status_filter Filter CPT by post status.
92 * By default, only fetches published posts.
93 * @param bool $should_create_draft Whether a new draft should be created
94 * if no CPT was found. False by default.
95 * @return array Custom Post Type for the user's Global Styles.
96 */
97 function gutenberg_experimental_global_styles_get_user_cpt( $post_status_filter = array( 'publish' ), $should_create_draft = false ) {
98 $user_cpt = array();
99 $post_type_filter = 'wp_global_styles';
100 $post_name_filter = 'wp-global-styles-' . strtolower( wp_get_theme()->get( 'TextDomain' ) );
101 $recent_posts = wp_get_recent_posts(
102 array(
103 'numberposts' => 1,
104 'orderby' => 'date',
105 'order' => 'desc',
106 'post_type' => $post_type_filter,
107 'post_status' => $post_status_filter,
108 'name' => $post_name_filter,
109 )
110 );
111
112 if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
113 $user_cpt = $recent_posts[0];
114 } elseif ( $should_create_draft ) {
115 $cpt_post_id = wp_insert_post(
116 array(
117 'post_content' => '{}',
118 'post_status' => 'draft',
119 'post_type' => $post_type_filter,
120 'post_name' => $post_name_filter,
121 ),
122 true
123 );
124 $user_cpt = get_post( $cpt_post_id, ARRAY_A );
125 }
126
127 return $user_cpt;
128 }
129
130 /**
131 * Returns the post ID of the CPT containing the user's Global Styles.
132 *
133 * @return integer Custom Post Type ID for the user's Global Styles.
134 */
135 function gutenberg_experimental_global_styles_get_user_cpt_id() {
136 $user_cpt_id = null;
137 $user_cpt = gutenberg_experimental_global_styles_get_user_cpt( array( 'publish', 'draft' ), true );
138 if ( array_key_exists( 'ID', $user_cpt ) ) {
139 $user_cpt_id = $user_cpt['ID'];
140 }
141 return $user_cpt_id;
142 }
143
144 /**
145 * Return core's Global Styles.
146 *
147 * @return array Global Styles tree.
148 */
149 function gutenberg_experimental_global_styles_get_core() {
150 return gutenberg_experimental_global_styles_get_from_file(
151 dirname( dirname( __FILE__ ) ) . '/experimental-default-global-styles.json'
152 );
153 }
154
155 /**
156 * Return theme's Global Styles.
157 *
158 * @return array Global Styles tree.
159 */
160 function gutenberg_experimental_global_styles_get_theme() {
161 return gutenberg_experimental_global_styles_get_from_file(
162 locate_template( 'experimental-theme.json' )
163 );
164 }
165
166 /**
167 * Takes a Global Styles tree and returns a CSS rule
168 * that contains the corresponding CSS custom properties.
169 *
170 * @param array $global_styles Global Styles tree.
171 * @return string CSS rule.
172 */
173 function gutenberg_experimental_global_styles_resolver( $global_styles ) {
174 $css_rule = '';
175
176 $token = '--';
177 $prefix = '--wp' . $token;
178 $css_vars = gutenberg_experimental_global_styles_get_css_vars( $global_styles, $prefix, $token );
179 if ( empty( $css_vars ) ) {
180 return $css_rule;
181 }
182
183 $css_rule = ":root {\n";
184 foreach ( $css_vars as $var => $value ) {
185 $css_rule .= "\t" . $var . ': ' . $value . ";\n";
186 }
187 $css_rule .= '}';
188
189 return $css_rule;
190 }
191
192 /**
193 * Fetches the Global Styles for each level (core, theme, user)
194 * and enqueues the resulting CSS custom properties for the editor.
195 */
196 function gutenberg_experimental_global_styles_enqueue_assets_editor() {
197 if ( gutenberg_experimental_global_styles_is_site_editor() ) {
198 gutenberg_experimental_global_styles_enqueue_assets();
199 }
200 }
201
202 /**
203 * Fetches the Global Styles for each level (core, theme, user)
204 * and enqueues the resulting CSS custom properties.
205 */
206 function gutenberg_experimental_global_styles_enqueue_assets() {
207 if ( ! gutenberg_experimental_global_styles_has_theme_support() ) {
208 return;
209 }
210
211 $global_styles = array_merge(
212 gutenberg_experimental_global_styles_get_core(),
213 gutenberg_experimental_global_styles_get_theme(),
214 gutenberg_experimental_global_styles_get_user()
215 );
216
217 $inline_style = gutenberg_experimental_global_styles_resolver( $global_styles );
218 if ( empty( $inline_style ) ) {
219 return;
220 }
221
222 wp_register_style( 'global-styles', false, array(), true, true );
223 wp_add_inline_style( 'global-styles', $inline_style );
224 wp_enqueue_style( 'global-styles' );
225 }
226
227 /**
228 * Adds class wp-gs to the frontend body class.
229 *
230 * @param array $classes Existing body classes.
231 * @return array The filtered array of body classes.
232 */
233 function gutenberg_experimental_global_styles_wp_gs_class_front_end( $classes ) {
234 if ( ! gutenberg_experimental_global_styles_has_theme_support() ) {
235 return $classes;
236 }
237
238 return array_merge( $classes, array( 'wp-gs' ) );
239 }
240
241 /**
242 * Adds class wp-gs to the block-editor body class.
243 *
244 * @param string $classes Existing body classes separated by space.
245 * @return string The filtered string of body classes.
246 */
247 function gutenberg_experimental_global_styles_wp_gs_class_editor( $classes ) {
248 if (
249 ! gutenberg_experimental_global_styles_has_theme_support() ||
250 ! gutenberg_experimental_global_styles_is_site_editor()
251 ) {
252 return $classes;
253 }
254
255 return $classes . ' wp-gs';
256 }
257
258 /**
259 * Whether the loaded page is the site editor.
260 *
261 * @return boolean Whether the loaded page is the site editor.
262 */
263 function gutenberg_experimental_global_styles_is_site_editor() {
264 if ( ! function_exists( 'get_current_screen' ) ) {
265 return false;
266 }
267
268 $screen = get_current_screen();
269 return ! empty( $screen ) &&
270 ( 'gutenberg_page_gutenberg-edit-site' === $screen->id );
271 }
272
273 /**
274 * Makes the base Global Styles (core, theme)
275 * and the ID of the CPT that stores the user's Global Styles
276 * available to the client, to be used for live rendering the styles.
277 *
278 * @param array $settings Existing block editor settings.
279 * @return array New block editor settings
280 */
281 function gutenberg_experimental_global_styles_settings( $settings ) {
282 if (
283 ! gutenberg_experimental_global_styles_has_theme_support() ||
284 ! gutenberg_experimental_global_styles_is_site_editor()
285 ) {
286 return $settings;
287 }
288
289 $settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id();
290
291 $global_styles = array_merge(
292 gutenberg_experimental_global_styles_get_core(),
293 gutenberg_experimental_global_styles_get_theme()
294 );
295
296 $settings['__experimentalGlobalStylesBase'] = $global_styles;
297
298 return $settings;
299 }
300
301 /**
302 * Registers a Custom Post Type to store the user's Global Styles.
303 */
304 function gutenberg_experimental_global_styles_register_cpt() {
305 if ( ! gutenberg_experimental_global_styles_has_theme_support() ) {
306 return;
307 }
308
309 $args = array(
310 'label' => __( 'Global Styles', 'gutenberg' ),
311 'description' => 'CPT to store user design tokens',
312 'public' => false,
313 'show_ui' => false,
314 'show_in_rest' => true,
315 'rest_base' => '__experimental/global-styles',
316 'capabilities' => array(
317 'read' => 'edit_theme_options',
318 'create_posts' => 'edit_theme_options',
319 'edit_posts' => 'edit_theme_options',
320 'edit_published_posts' => 'edit_theme_options',
321 'delete_published_posts' => 'edit_theme_options',
322 'edit_others_posts' => 'edit_theme_options',
323 'delete_others_posts' => 'edit_theme_options',
324 ),
325 'map_meta_cap' => true,
326 'supports' => array(
327 'editor',
328 'revisions',
329 ),
330 );
331 register_post_type( 'wp_global_styles', $args );
332 }
333
334 if ( gutenberg_is_experiment_enabled( 'gutenberg-full-site-editing' ) ) {
335 add_action( 'init', 'gutenberg_experimental_global_styles_register_cpt' );
336 add_filter( 'body_class', 'gutenberg_experimental_global_styles_wp_gs_class_front_end' );
337 add_filter( 'admin_body_class', 'gutenberg_experimental_global_styles_wp_gs_class_editor' );
338 add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings' );
339 // enqueue_block_assets is not fired in edit-site, so we use separate back/front hooks instead.
340 add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
341 add_action( 'admin_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets_editor' );
342 }
343