PluginProbe ʕ •ᴥ•ʔ
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets / 4.0.5
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets v4.0.5
4.2.5 4.2.4 trunk 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.2 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8 3.8.1 3.8.10 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.8.9.1 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.5.1 4.0.6 4.0.6.1 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2.0 4.2.1 4.2.2 4.2.3
widget-options / includes / widgets / gutenberg / lib / class-wp-theme-json-resolver-gutenberg.php
widget-options / includes / widgets / gutenberg / lib Last commit date
block-supports 2 years ago compat 2 years ago experimental 2 years ago README.md 2 years ago block-editor-settings.php 2 years ago blocks.php 2 years ago class-wp-duotone-gutenberg.php 2 years ago class-wp-theme-json-data-gutenberg.php 2 years ago class-wp-theme-json-gutenberg.php 2 years ago class-wp-theme-json-resolver-gutenberg.php 2 years ago client-assets.php 2 years ago demo.php 2 years ago experiments-page.php 2 years ago global-styles-and-settings.php 2 years ago init.php 2 years ago load.php 2 years ago script-loader.php 2 years ago theme-i18n.json 2 years ago theme.json 2 years ago upgrade.php 2 years ago
class-wp-theme-json-resolver-gutenberg.php
733 lines
1 <?php
2 /**
3 * WP_Theme_JSON_Resolver class
4 *
5 * @package gutenberg
6 * @since 5.8.0
7 */
8
9 /**
10 * Class that abstracts the processing of the different data sources
11 * for site-level config and offers an API to work with them.
12 *
13 * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
14 * This is a low-level API that may need to do breaking changes. Please,
15 * use gutenberg_get_global_settings, gutenberg_get_global_styles, and gutenberg_get_global_stylesheet instead.
16 *
17 * @access private
18 */
19 #[AllowDynamicProperties]
20 class WP_Theme_JSON_Resolver_Gutenberg {
21
22 /**
23 * Container for keep track of registered blocks.
24 *
25 * @since 6.1.0
26 * @var array
27 */
28 protected static $blocks_cache = array(
29 'core' => array(),
30 'blocks' => array(),
31 'theme' => array(),
32 'user' => array(),
33 );
34
35 /**
36 * Container for data coming from core.
37 *
38 * @since 5.8.0
39 * @var WP_Theme_JSON
40 */
41 protected static $core = null;
42
43 /**
44 * Container for data coming from the blocks.
45 *
46 * @since 6.1.0
47 * @var WP_Theme_JSON
48 */
49 protected static $blocks = null;
50
51 /**
52 * Container for data coming from the theme.
53 *
54 * @since 5.8.0
55 * @var WP_Theme_JSON
56 */
57 protected static $theme = null;
58
59 /**
60 * Container for data coming from the user.
61 *
62 * @since 5.9.0
63 * @var WP_Theme_JSON
64 */
65 protected static $user = null;
66
67 /**
68 * Stores the ID of the custom post type
69 * that holds the user data.
70 *
71 * @since 5.9.0
72 * @var int
73 */
74 protected static $user_custom_post_type_id = null;
75
76 /**
77 * Container to keep loaded i18n schema for `theme.json`.
78 *
79 * @since 5.8.0 As `$theme_json_i18n`.
80 * @since 5.9.0 Renamed from `$theme_json_i18n` to `$i18n_schema`.
81 * @var array
82 */
83 protected static $i18n_schema = null;
84
85 /**
86 * `theme.json` file cache.
87 *
88 * @since 6.1.0
89 * @var array
90 */
91 protected static $theme_json_file_cache = array();
92
93 /**
94 * Processes a file that adheres to the theme.json schema
95 * and returns an array with its contents, or a void array if none found.
96 *
97 * @since 5.8.0
98 * @since 6.1.0 Added caching.
99 *
100 * @param string $file_path Path to file. Empty if no file.
101 * @return array Contents that adhere to the theme.json schema.
102 */
103 protected static function read_json_file( $file_path ) {
104 if ( $file_path ) {
105 if ( array_key_exists( $file_path, static::$theme_json_file_cache ) ) {
106 return static::$theme_json_file_cache[ $file_path ];
107 }
108
109 $decoded_file = wp_json_file_decode( $file_path, array( 'associative' => true ) );
110 if ( is_array( $decoded_file ) ) {
111 static::$theme_json_file_cache[ $file_path ] = $decoded_file;
112 return static::$theme_json_file_cache[ $file_path ];
113 }
114 }
115
116 return array();
117 }
118
119 /**
120 * Returns a data structure used in theme.json translation.
121 *
122 * @since 5.8.0
123 * @deprecated 5.9.0
124 *
125 * @return array An array of theme.json fields that are translatable and the keys that are translatable.
126 */
127 public static function get_fields_to_translate() {
128 _deprecated_function( __METHOD__, '5.9.0' );
129 return array();
130 }
131
132 /**
133 * Given a theme.json structure modifies it in place to update certain values
134 * by its translated strings according to the language set by the user.
135 *
136 * @since 5.8.0
137 *
138 * @param array $theme_json The theme.json to translate.
139 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
140 * Default 'default'.
141 * @return array Returns the modified $theme_json_structure.
142 */
143 protected static function translate( $theme_json, $domain = 'default' ) {
144 if ( null === static::$i18n_schema ) {
145 $i18n_schema = wp_json_file_decode( __DIR__ . '/theme-i18n.json' );
146 static::$i18n_schema = null === $i18n_schema ? array() : $i18n_schema;
147 }
148
149 return translate_settings_using_i18n_schema( static::$i18n_schema, $theme_json, $domain );
150 }
151
152 /**
153 * Returns core's origin config.
154 *
155 * @since 5.8.0
156 *
157 * @return WP_Theme_JSON Entity that holds core data.
158 */
159 public static function get_core_data() {
160 if ( null !== static::$core && static::has_same_registered_blocks( 'core' ) ) {
161 return static::$core;
162 }
163
164 $config = static::read_json_file( __DIR__ . '/theme.json' );
165 $config = static::translate( $config );
166
167 /**
168 * Filters the default data provided by WordPress for global styles & settings.
169 *
170 * @since 6.1.0
171 *
172 * @param WP_Theme_JSON_Data Class to access and update the underlying data.
173 */
174 $theme_json = apply_filters( 'wp_theme_json_data_default', new WP_Theme_JSON_Data_Gutenberg( $config, 'default' ) );
175 $config = $theme_json->get_data();
176 static::$core = new WP_Theme_JSON_Gutenberg( $config, 'default' );
177
178 return static::$core;
179 }
180
181 /**
182 * Checks whether the registered blocks were already processed for this origin.
183 *
184 * @since 6.1.0
185 *
186 * @param string $origin Data source for which to cache the blocks.
187 * Valid values are 'core', 'blocks', 'theme', and 'user'.
188 * @return bool True on success, false otherwise.
189 */
190 protected static function has_same_registered_blocks( $origin ) {
191 // Bail out if the origin is invalid.
192 if ( ! isset( static::$blocks_cache[ $origin ] ) ) {
193 return false;
194 }
195
196 $registry = WP_Block_Type_Registry::get_instance();
197 $blocks = $registry->get_all_registered();
198
199 // Is there metadata for all currently registered blocks?
200 $block_diff = array_diff_key( $blocks, static::$blocks_cache[ $origin ] );
201 if ( empty( $block_diff ) ) {
202 return true;
203 }
204
205 foreach ( $blocks as $block_name => $block_type ) {
206 static::$blocks_cache[ $origin ][ $block_name ] = true;
207 }
208
209 return false;
210 }
211
212 /**
213 * Returns the theme's data.
214 *
215 * Data from theme.json will be backfilled from existing
216 * theme supports, if any. Note that if the same data
217 * is present in theme.json and in theme supports,
218 * the theme.json takes precedence.
219 *
220 * @since 5.8.0
221 * @since 5.9.0 Theme supports have been inlined and the `$theme_support_data` argument removed.
222 * @since 6.0.0 Added an `$options` parameter to allow the theme data to be returned without theme supports.
223 *
224 * @param array $deprecated Deprecated. Not used.
225 * @param array $options {
226 * Options arguments.
227 *
228 * @type bool $with_supports Whether to include theme supports in the data. Default true.
229 * }
230 * @return WP_Theme_JSON Entity that holds theme data.
231 */
232 public static function get_theme_data( $deprecated = array(), $options = array() ) {
233 if ( ! empty( $deprecated ) ) {
234 _deprecated_argument( __METHOD__, '5.9.0' );
235 }
236
237 $options = wp_parse_args( $options, array( 'with_supports' => true ) );
238
239 if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) {
240 $theme_json_file = static::get_file_path_from_theme( 'theme.json' );
241 $wp_theme = wp_get_theme();
242 if ( '' !== $theme_json_file ) {
243 $theme_json_data = static::read_json_file( $theme_json_file );
244 $theme_json_data = static::translate( $theme_json_data, $wp_theme->get( 'TextDomain' ) );
245 } else {
246 $theme_json_data = array();
247 }
248
249 /**
250 * Filters the data provided by the theme for global styles and settings.
251 *
252 * @since 6.1.0
253 *
254 * @param WP_Theme_JSON_Data Class to access and update the underlying data.
255 */
256 $theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data_Gutenberg( $theme_json_data, 'theme' ) );
257 $theme_json_data = $theme_json->get_data();
258 static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data );
259
260 if ( $wp_theme->parent() ) {
261 // Get parent theme.json.
262 $parent_theme_json_file = static::get_file_path_from_theme( 'theme.json', true );
263 if ( '' !== $parent_theme_json_file ) {
264 $parent_theme_json_data = static::read_json_file( $parent_theme_json_file );
265 $parent_theme_json_data = static::translate( $parent_theme_json_data, $wp_theme->parent()->get( 'TextDomain' ) );
266 $parent_theme = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data );
267
268 /*
269 * Merge the child theme.json into the parent theme.json.
270 * The child theme takes precedence over the parent.
271 */
272 $parent_theme->merge( static::$theme );
273 static::$theme = $parent_theme;
274 }
275 }
276
277 // BEGIN OF EXPERIMENTAL CODE. Not to backport to core.
278 static::$theme = gutenberg_add_registered_fonts_to_theme_json( static::$theme );
279 // END OF EXPERIMENTAL CODE.
280
281 }
282
283 if ( ! $options['with_supports'] ) {
284 return static::$theme;
285 }
286
287 /*
288 * We want the presets and settings declared in theme.json
289 * to override the ones declared via theme supports.
290 * So we take theme supports, transform it to theme.json shape
291 * and merge the static::$theme upon that.
292 */
293 $theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
294 if ( ! wp_theme_has_theme_json() ) {
295 if ( ! isset( $theme_support_data['settings']['color'] ) ) {
296 $theme_support_data['settings']['color'] = array();
297 }
298
299 $default_palette = false;
300 if ( current_theme_supports( 'default-color-palette' ) ) {
301 $default_palette = true;
302 }
303 if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) {
304 // If the theme does not have any palette, we still want to show the core one.
305 $default_palette = true;
306 }
307 $theme_support_data['settings']['color']['defaultPalette'] = $default_palette;
308
309 $default_gradients = false;
310 if ( current_theme_supports( 'default-gradient-presets' ) ) {
311 $default_gradients = true;
312 }
313 if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) {
314 // If the theme does not have any gradients, we still want to show the core ones.
315 $default_gradients = true;
316 }
317 $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients;
318
319 // Classic themes without a theme.json don't support global duotone.
320 $theme_support_data['settings']['color']['defaultDuotone'] = false;
321
322 // BEGIN EXPERIMENTAL.
323 // Allow themes to enable appearance tools via theme_support.
324 // This feature was backported for WordPress 6.2 as of https://core.trac.wordpress.org/ticket/56487
325 // and then reverted as of https://core.trac.wordpress.org/ticket/57649
326 // Not to backport until the issues are resolved.
327 if ( current_theme_supports( 'appearance-tools' ) ) {
328 $theme_support_data['settings']['appearanceTools'] = true;
329 }
330 // END EXPERIMENTAL.
331 }
332 $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data );
333 $with_theme_supports->merge( static::$theme );
334 return $with_theme_supports;
335 }
336
337 /**
338 * Gets the styles for blocks from the block.json file.
339 *
340 * @since 6.1.0
341 *
342 * @return WP_Theme_JSON
343 */
344 public static function get_block_data() {
345 $registry = WP_Block_Type_Registry::get_instance();
346 $blocks = $registry->get_all_registered();
347
348 if ( null !== static::$blocks && static::has_same_registered_blocks( 'blocks' ) ) {
349 return static::$blocks;
350 }
351
352 $config = array( 'version' => 2 );
353 foreach ( $blocks as $block_name => $block_type ) {
354 if ( isset( $block_type->supports['__experimentalStyle'] ) ) {
355 $config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] );
356 }
357
358 if (
359 isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) &&
360 null === _wp_array_get( $config, array( 'styles', 'blocks', $block_name, 'spacing', 'blockGap' ), null )
361 ) {
362 // Ensure an empty placeholder value exists for the block, if it provides a default blockGap value.
363 // The real blockGap value to be used will be determined when the styles are rendered for output.
364 $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null;
365 }
366 }
367
368 /**
369 * Filters the data provided by the blocks for global styles & settings.
370 *
371 * @since 6.1.0
372 *
373 * @param WP_Theme_JSON_Data Class to access and update the underlying data.
374 */
375 $theme_json = apply_filters( 'wp_theme_json_data_blocks', new WP_Theme_JSON_Data_Gutenberg( $config, 'blocks' ) );
376 $config = $theme_json->get_data();
377
378 static::$blocks = new WP_Theme_JSON_Gutenberg( $config, 'blocks' );
379 return static::$blocks;
380 }
381
382 /**
383 * When given an array, this will remove any keys with the name `//`.
384 *
385 * @param array $array The array to filter.
386 * @return array The filtered array.
387 */
388 private static function remove_json_comments( $array ) {
389 unset( $array['//'] );
390 foreach ( $array as $k => $v ) {
391 if ( is_array( $v ) ) {
392 $array[ $k ] = static::remove_json_comments( $v );
393 }
394 }
395
396 return $array;
397 }
398
399 /**
400 * Returns the custom post type that contains the user's origin config
401 * for the active theme or a void array if none are found.
402 *
403 * This can also create and return a new draft custom post type.
404 *
405 * @since 5.9.0
406 *
407 * @param WP_Theme $theme The theme object. If empty, it
408 * defaults to the active theme.
409 * @param bool $create_post Optional. Whether a new custom post
410 * type should be created if none are
411 * found. Default false.
412 * @param array $post_status_filter Optional. Filter custom post type by
413 * post status. Default `array( 'publish' )`,
414 * so it only fetches published posts.
415 * @return array Custom Post Type for the user's origin config.
416 */
417 public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) {
418 if ( ! $theme instanceof WP_Theme ) {
419 $theme = wp_get_theme();
420 }
421
422 /*
423 * Bail early if the theme does not support a theme.json.
424 *
425 * Since wp_theme_has_theme_json only supports the active
426 * theme, the extra condition for whether $theme is the active theme is
427 * present here.
428 */
429 if ( $theme->get_stylesheet() === get_stylesheet() && ! wp_theme_has_theme_json() ) {
430 return array();
431 }
432
433 $user_cpt = array();
434 $post_type_filter = 'wp_global_styles';
435 $stylesheet = $theme->get_stylesheet();
436 $args = array(
437 'posts_per_page' => 1,
438 'orderby' => 'date',
439 'order' => 'desc',
440 'post_type' => $post_type_filter,
441 'post_status' => $post_status_filter,
442 'ignore_sticky_posts' => true,
443 'no_found_rows' => true,
444 'update_post_meta_cache' => false,
445 'update_post_term_cache' => false,
446 'tax_query' => array(
447 array(
448 'taxonomy' => 'wp_theme',
449 'field' => 'name',
450 'terms' => $stylesheet,
451 ),
452 ),
453 );
454
455 $global_style_query = new WP_Query();
456 $recent_posts = $global_style_query->query( $args );
457 if ( count( $recent_posts ) === 1 ) {
458 $user_cpt = get_object_vars( $recent_posts[0] );
459 } elseif ( $create_post ) {
460 $cpt_post_id = wp_insert_post(
461 array(
462 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
463 'post_status' => 'publish',
464 'post_title' => 'Custom Styles', // Do not make string translatable, see https://core.trac.wordpress.org/ticket/54518.
465 'post_type' => $post_type_filter,
466 'post_name' => sprintf( 'wp-global-styles-%s', urlencode( $stylesheet ) ),
467 'tax_input' => array(
468 'wp_theme' => array( $stylesheet ),
469 ),
470 ),
471 true
472 );
473 if ( ! is_wp_error( $cpt_post_id ) ) {
474 $user_cpt = get_object_vars( get_post( $cpt_post_id ) );
475 }
476 }
477
478 return $user_cpt;
479 }
480
481 /**
482 * Returns the user's origin config.
483 *
484 * @since 5.9.0
485 *
486 * @return WP_Theme_JSON Entity that holds styles for user data.
487 */
488 public static function get_user_data() {
489 if ( null !== static::$user && static::has_same_registered_blocks( 'user' ) ) {
490 return static::$user;
491 }
492
493 $config = array();
494 $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
495
496 if ( array_key_exists( 'post_content', $user_cpt ) ) {
497 $decoded_data = json_decode( $user_cpt['post_content'], true );
498
499 $json_decoding_error = json_last_error();
500 if ( JSON_ERROR_NONE !== $json_decoding_error ) {
501 trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() );
502 /**
503 * Filters the data provided by the user for global styles & settings.
504 *
505 * @since 6.1.0
506 *
507 * @param WP_Theme_JSON_Data Class to access and update the underlying data.
508 */
509 $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) );
510 $config = $theme_json->get_data();
511 return new WP_Theme_JSON_Gutenberg( $config, 'custom' );
512 }
513
514 // Very important to verify that the flag isGlobalStylesUserThemeJSON is true.
515 // If it's not true then the content was not escaped and is not safe.
516 if (
517 is_array( $decoded_data ) &&
518 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
519 $decoded_data['isGlobalStylesUserThemeJSON']
520 ) {
521 unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
522 $config = $decoded_data;
523 }
524 }
525
526 /** This filter is documented in wp-includes/class-wp-theme-json-resolver.php */
527 $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) );
528 $config = $theme_json->get_data();
529 static::$user = new WP_Theme_JSON_Gutenberg( $config, 'custom' );
530
531 return static::$user;
532 }
533
534 /**
535 * Returns the data merged from multiple origins.
536 *
537 * There are four sources of data (origins) for a site:
538 *
539 * - default => WordPress
540 * - blocks => each one of the blocks provides data for itself
541 * - theme => the active theme
542 * - custom => data provided by the user
543 *
544 * The custom's has higher priority than the theme's, the theme's higher than blocks',
545 * and block's higher than default's.
546 *
547 * Unlike the getters
548 * {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_core_data/ get_core_data},
549 * {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_theme_data/ get_theme_data},
550 * and {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_user_data/ get_user_data},
551 * this method returns data after it has been merged with the previous origins.
552 * This means that if the same piece of data is declared in different origins
553 * (default, blocks, theme, custom), the last origin overrides the previous.
554 *
555 * For example, if the user has set a background color
556 * for the paragraph block, and the theme has done it as well,
557 * the user preference wins.
558 *
559 * @since 5.8.0
560 * @since 5.9.0 Added user data, removed the `$settings` parameter,
561 * added the `$origin` parameter.
562 * @since 6.1.0 Added block data and generation of spacingSizes array.
563 *
564 * @param string $origin Optional. To what level should we merge data:'default', 'blocks', 'theme' or 'custom'.
565 * 'custom' is used as default value as well as fallback value if the origin is unknown.
566 *
567 * @return WP_Theme_JSON
568 */
569 public static function get_merged_data( $origin = 'custom' ) {
570 if ( is_array( $origin ) ) {
571 _deprecated_argument( __FUNCTION__, '5.9.0' );
572 }
573
574 $result = new WP_Theme_JSON_Gutenberg();
575 $result->merge( static::get_core_data() );
576 if ( 'default' === $origin ) {
577 $result->set_spacing_sizes();
578 return $result;
579 }
580
581 $result->merge( static::get_block_data() );
582 if ( 'blocks' === $origin ) {
583 return $result;
584 }
585
586 $result->merge( static::get_theme_data() );
587 if ( 'theme' === $origin ) {
588 $result->set_spacing_sizes();
589 return $result;
590 }
591
592 $result->merge( static::get_user_data() );
593 $result->set_spacing_sizes();
594 return $result;
595 }
596
597 /**
598 * Returns the ID of the custom post type
599 * that stores user data.
600 *
601 * @since 5.9.0
602 *
603 * @return integer|null
604 */
605 public static function get_user_global_styles_post_id() {
606 if ( null !== static::$user_custom_post_type_id ) {
607 return static::$user_custom_post_type_id;
608 }
609
610 $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme(), true );
611
612 if ( array_key_exists( 'ID', $user_cpt ) ) {
613 static::$user_custom_post_type_id = $user_cpt['ID'];
614 }
615
616 return static::$user_custom_post_type_id;
617 }
618
619 /**
620 * Determines whether the active theme has a theme.json file.
621 *
622 * @since 5.8.0
623 * @since 5.9.0 Added a check in the parent theme.
624 * @deprecated 6.2.0 Use wp_theme_has_theme_json() instead.
625 *
626 * @return bool
627 */
628 public static function theme_has_support() {
629 _deprecated_function( __METHOD__, '6.2.0', 'wp_theme_has_theme_json()' );
630
631 return wp_theme_has_theme_json();
632 }
633
634 /**
635 * Builds the path to the given file and checks that it is readable.
636 *
637 * If it isn't, returns an empty string, otherwise returns the whole file path.
638 *
639 * @since 5.8.0
640 * @since 5.9.0 Adapted to work with child themes, added the `$template` argument.
641 *
642 * @param string $file_name Name of the file.
643 * @param bool $template Optional. Use template theme directory. Default false.
644 * @return string The whole file path or empty if the file doesn't exist.
645 */
646 protected static function get_file_path_from_theme( $file_name, $template = false ) {
647 $path = $template ? get_template_directory() : get_stylesheet_directory();
648 $candidate = $path . '/' . $file_name;
649
650 return is_readable( $candidate ) ? $candidate : '';
651 }
652
653 /**
654 * Cleans the cached data so it can be recalculated.
655 *
656 * @since 5.8.0
657 * @since 5.9.0 Added the `$user`, `$user_custom_post_type_id`,
658 * and `$i18n_schema` variables to reset.
659 * @since 6.1.0 Added the `$blocks` and `$blocks_cache` variables
660 * to reset.
661 */
662 public static function clean_cached_data() {
663 static::$core = null;
664 static::$blocks = null;
665 static::$blocks_cache = array(
666 'core' => array(),
667 'blocks' => array(),
668 'theme' => array(),
669 'user' => array(),
670 );
671 static::$theme = null;
672 static::$user = null;
673 static::$user_custom_post_type_id = null;
674 static::$i18n_schema = null;
675 }
676
677 /**
678 * Returns an array of all nested json files within a given directory.
679 *
680 * @since 6.2.0
681 *
682 * @param string $dir The directory to recursively iterate and list files of.
683 * @return array The merged array.
684 */
685 private static function recursively_iterate_json( $dir ) {
686 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) );
687 $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) );
688 return $nested_json_files;
689 }
690
691 /**
692 * Returns the style variations defined by the theme (parent and child).
693 *
694 * @since 6.2.0 Returns parent theme variations if theme is a child.
695 *
696 * @return array
697 */
698 public static function get_style_variations() {
699 $variation_files = array();
700 $variations = array();
701 $base_directory = get_stylesheet_directory() . '/styles';
702 $template_directory = get_template_directory() . '/styles';
703 if ( is_dir( $base_directory ) ) {
704 $variation_files = static::recursively_iterate_json( $base_directory );
705 }
706 if ( is_dir( $template_directory ) && $template_directory !== $base_directory ) {
707 $variation_files_parent = static::recursively_iterate_json( $template_directory );
708 // If the child and parent variation file basename are the same, only include the child theme's.
709 foreach ( $variation_files_parent as $parent_path => $parent ) {
710 foreach ( $variation_files as $child_path => $child ) {
711 if ( basename( $parent_path ) === basename( $child_path ) ) {
712 unset( $variation_files_parent[ $parent_path ] );
713 }
714 }
715 }
716 $variation_files = array_merge( $variation_files, $variation_files_parent );
717 }
718 ksort( $variation_files );
719 foreach ( $variation_files as $path => $file ) {
720 $decoded_file = wp_json_file_decode( $path, array( 'associative' => true ) );
721 if ( is_array( $decoded_file ) ) {
722 $translated = static::translate( $decoded_file, wp_get_theme()->get( 'TextDomain' ) );
723 $variation = ( new WP_Theme_JSON_Gutenberg( $translated ) )->get_raw_data();
724 if ( empty( $variation['title'] ) ) {
725 $variation['title'] = basename( $path, '.json' );
726 }
727 $variations[] = $variation;
728 }
729 }
730 return $variations;
731 }
732 }
733