2021-05-24 04:37:55 -04:00
< ? php
/**
2021-05-24 09:25:56 -04:00
* WP_Theme_JSON class
2021-05-24 04:37:55 -04:00
*
* @ package WordPress
2021-05-24 09:25:56 -04:00
* @ subpackage Theme
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
*/
/**
2021-05-24 09:25:56 -04:00
* Class that encapsulates the processing of structures that adhere to the theme . json spec .
2021-05-24 04:37:55 -04:00
*
2021-11-08 14:19:58 -05:00
* This class is for internal core usage and is not supposed to be used by extenders ( plugins and / or themes ) .
* This is a low - level API that may need to do breaking changes . Please ,
* use get_global_settings , get_global_styles , and get_global_stylesheet instead .
*
2021-05-24 04:37:55 -04:00
* @ access private
*/
class WP_Theme_JSON {
/**
* Container of data in theme . json format .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
* @ var array
*/
2022-02-17 04:04:05 -05:00
protected $theme_json = null ;
2021-05-24 04:37:55 -04:00
/**
2021-05-24 13:39:57 -04:00
* Holds block metadata extracted from block . json
* to be shared among all instances so we don ' t
* process it twice .
2021-05-24 04:37:55 -04:00
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
* @ var array
*/
2022-02-17 04:04:05 -05:00
protected static $blocks_metadata = null ;
2021-05-24 04:37:55 -04:00
2021-05-24 13:39:57 -04:00
/**
* The CSS selector for the top - level styles .
*
* @ since 5.8 . 0
* @ var string
*/
const ROOT_BLOCK_SELECTOR = 'body' ;
2021-06-15 07:25:08 -04:00
/**
* The sources of data this object can represent .
*
* @ since 5.8 . 0
2021-07-01 17:02:57 -04:00
* @ var string []
2021-06-15 07:25:08 -04:00
*/
2021-06-15 04:52:30 -04:00
const VALID_ORIGINS = array (
2021-11-23 00:40:38 -05:00
'default' ,
2021-06-15 04:52:30 -04:00
'theme' ,
2021-11-29 19:24:27 -05:00
'custom' ,
2021-06-15 04:52:30 -04:00
);
2021-05-24 13:39:57 -04:00
/**
* Presets are a set of values that serve
* to bootstrap some styles : colors , font sizes , etc .
*
* They are a unkeyed array of values such as :
*
* `` ` php
* array (
* array (
* 'slug' => 'unique-name-within-the-set' ,
* 'name' => 'Name for the UI' ,
* < value_key > => 'value'
* ),
* )
* `` `
*
* This contains the necessary metadata to process them :
*
2022-04-11 06:38:00 -04:00
* - path => Where to find the preset within the settings section .
* - prevent_override => Disables override of default presets by theme presets .
* The relationship between whether to override the defaults
* and whether the defaults are enabled is inverse :
* - If defaults are enabled => theme presets should not be overriden
* - If defaults are disabled => theme presets should be overriden
* For example , a theme sets defaultPalette to false ,
* making the default palette hidden from the user .
* In that case , we want all the theme presets to be present ,
* so they should override the defaults by setting this false .
2021-12-21 01:02:06 -05:00
* - use_default_names => whether to use the default names
2022-04-11 06:38:00 -04:00
* - value_key => the key that represents the value
* - value_func => optionally , instead of value_key , a function to generate
* the value that takes a preset as an argument
* ( either value_key or value_func should be present )
* - css_vars => template string to use in generating the CSS Custom Property .
* Example output : " --wp--preset--duotone--blue: <value> " will generate as many CSS Custom Properties as presets defined
* substituting the $slug for the slug ' s value for each preset value .
* - classes => array containing a structure with the classes to
* generate for the presets , where for each array item
* the key is the class name and the value the property name .
* The " $slug " substring will be replaced by the slug of each preset .
* For example :
* 'classes' => array (
* '.has-$slug-color' => 'color' ,
* '.has-$slug-background-color' => 'background-color' ,
* '.has-$slug-border-color' => 'border-color' ,
* )
* - properties => array of CSS properties to be used by kses to
* validate the content of each preset
* by means of the remove_insecure_properties method .
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
2021-12-21 05:20:04 -05:00
* @ since 5.9 . 0 Added the `color.duotone` and `typography.fontFamilies` presets ,
* `use_default_names` preset key , and simplified the metadata structure .
2022-04-11 06:38:00 -04:00
* @ since 6.0 . 0 Replaced `override` with `prevent_override` and updated the
* `prevent_overried` value for `color.duotone` to use `color.defaultDuotone` .
2021-05-24 13:39:57 -04:00
* @ var array
*/
const PRESETS_METADATA = array (
array (
2021-12-21 01:02:06 -05:00
'path' => array ( 'color' , 'palette' ),
2022-04-11 06:38:00 -04:00
'prevent_override' => array ( 'color' , 'defaultPalette' ),
2021-12-21 01:02:06 -05:00
'use_default_names' => false ,
'value_key' => 'color' ,
'css_vars' => '--wp--preset--color--$slug' ,
'classes' => array (
2021-11-08 14:19:58 -05:00
'.has-$slug-color' => 'color' ,
'.has-$slug-background-color' => 'background-color' ,
'.has-$slug-border-color' => 'border-color' ,
2021-05-24 13:39:57 -04:00
),
2021-12-21 01:02:06 -05:00
'properties' => array ( 'color' , 'background-color' , 'border-color' ),
2021-05-24 13:39:57 -04:00
),
array (
2021-12-21 01:02:06 -05:00
'path' => array ( 'color' , 'gradients' ),
2022-04-11 06:38:00 -04:00
'prevent_override' => array ( 'color' , 'defaultGradients' ),
2021-12-21 01:02:06 -05:00
'use_default_names' => false ,
'value_key' => 'gradient' ,
'css_vars' => '--wp--preset--gradient--$slug' ,
'classes' => array ( '.has-$slug-gradient-background' => 'background' ),
'properties' => array ( 'background' ),
2021-05-24 13:39:57 -04:00
),
array (
2021-12-21 01:02:06 -05:00
'path' => array ( 'color' , 'duotone' ),
2022-04-11 06:38:00 -04:00
'prevent_override' => array ( 'color' , 'defaultDuotone' ),
2021-12-21 01:02:06 -05:00
'use_default_names' => false ,
2022-02-17 11:18:03 -05:00
'value_func' => 'wp_get_duotone_filter_property' ,
2021-12-21 01:02:06 -05:00
'css_vars' => '--wp--preset--duotone--$slug' ,
'classes' => array (),
'properties' => array ( 'filter' ),
2021-11-08 14:19:58 -05:00
),
array (
2021-12-21 01:02:06 -05:00
'path' => array ( 'typography' , 'fontSizes' ),
2022-04-11 06:38:00 -04:00
'prevent_override' => false ,
2021-12-21 01:02:06 -05:00
'use_default_names' => true ,
'value_key' => 'size' ,
'css_vars' => '--wp--preset--font-size--$slug' ,
'classes' => array ( '.has-$slug-font-size' => 'font-size' ),
'properties' => array ( 'font-size' ),
2021-11-08 14:19:58 -05:00
),
array (
2021-12-21 01:02:06 -05:00
'path' => array ( 'typography' , 'fontFamilies' ),
2022-04-11 06:38:00 -04:00
'prevent_override' => false ,
2021-12-21 01:02:06 -05:00
'use_default_names' => false ,
'value_key' => 'fontFamily' ,
'css_vars' => '--wp--preset--font-family--$slug' ,
'classes' => array ( '.has-$slug-font-family' => 'font-family' ),
'properties' => array ( 'font-family' ),
2021-05-24 13:39:57 -04:00
),
);
/**
* Metadata for style properties .
*
2021-11-08 14:19:58 -05:00
* Each element is a direct mapping from the CSS property name to the
* path to the value in theme . json & block attributes .
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `border-*` , `font-family` , `font-style` , `font-weight` ,
* `letter-spacing` , `margin-*` , `padding-*` , `--wp--style--block-gap` ,
* `text-decoration` , `text-transform` , and `filter` properties ,
* simplified the metadata structure .
2021-05-24 13:39:57 -04:00
* @ var array
*/
const PROPERTIES_METADATA = array (
2021-11-08 14:19:58 -05:00
'background' => array ( 'color' , 'gradient' ),
'background-color' => array ( 'color' , 'background' ),
'border-radius' => array ( 'border' , 'radius' ),
'border-top-left-radius' => array ( 'border' , 'radius' , 'topLeft' ),
'border-top-right-radius' => array ( 'border' , 'radius' , 'topRight' ),
'border-bottom-left-radius' => array ( 'border' , 'radius' , 'bottomLeft' ),
'border-bottom-right-radius' => array ( 'border' , 'radius' , 'bottomRight' ),
'border-color' => array ( 'border' , 'color' ),
'border-width' => array ( 'border' , 'width' ),
'border-style' => array ( 'border' , 'style' ),
'color' => array ( 'color' , 'text' ),
'font-family' => array ( 'typography' , 'fontFamily' ),
'font-size' => array ( 'typography' , 'fontSize' ),
'font-style' => array ( 'typography' , 'fontStyle' ),
'font-weight' => array ( 'typography' , 'fontWeight' ),
'letter-spacing' => array ( 'typography' , 'letterSpacing' ),
'line-height' => array ( 'typography' , 'lineHeight' ),
'margin' => array ( 'spacing' , 'margin' ),
'margin-top' => array ( 'spacing' , 'margin' , 'top' ),
'margin-right' => array ( 'spacing' , 'margin' , 'right' ),
'margin-bottom' => array ( 'spacing' , 'margin' , 'bottom' ),
'margin-left' => array ( 'spacing' , 'margin' , 'left' ),
'padding' => array ( 'spacing' , 'padding' ),
'padding-top' => array ( 'spacing' , 'padding' , 'top' ),
'padding-right' => array ( 'spacing' , 'padding' , 'right' ),
'padding-bottom' => array ( 'spacing' , 'padding' , 'bottom' ),
'padding-left' => array ( 'spacing' , 'padding' , 'left' ),
'--wp--style--block-gap' => array ( 'spacing' , 'blockGap' ),
'text-decoration' => array ( 'typography' , 'textDecoration' ),
'text-transform' => array ( 'typography' , 'textTransform' ),
'filter' => array ( 'filter' , 'duotone' ),
2021-05-24 13:39:57 -04:00
);
/**
2021-11-08 14:19:58 -05:00
* Protected style properties .
*
* These style properties are only rendered if a setting enables it
* via a value other than `null` .
*
* Each element maps the style property to the corresponding theme . json
* setting key .
*
* @ since 5.9 . 0
*/
const PROTECTED_PROPERTIES = array (
'spacing.blockGap' => array ( 'spacing' , 'blockGap' ),
);
/**
* The top - level keys a theme . json can have .
*
2021-12-04 10:57:01 -05:00
* @ since 5.8 . 0 As `ALLOWED_TOP_LEVEL_KEYS` .
* @ since 5.9 . 0 Renamed from `ALLOWED_TOP_LEVEL_KEYS` to `VALID_TOP_LEVEL_KEYS` ,
2021-12-04 07:58:01 -05:00
* added the `customTemplates` and `templateParts` values .
2021-07-01 17:02:57 -04:00
* @ var string []
2021-05-24 13:39:57 -04:00
*/
2021-11-08 14:19:58 -05:00
const VALID_TOP_LEVEL_KEYS = array (
'customTemplates' ,
2022-04-11 06:38:00 -04:00
'patterns' ,
2021-05-24 04:37:55 -04:00
'settings' ,
2021-05-24 13:39:57 -04:00
'styles' ,
2021-11-08 14:19:58 -05:00
'templateParts' ,
2021-05-24 13:39:57 -04:00
'version' ,
2022-04-11 06:38:00 -04:00
'title' ,
2021-05-24 04:37:55 -04:00
);
2021-05-24 13:39:57 -04:00
/**
2021-11-08 14:19:58 -05:00
* The valid properties under the settings key .
*
2021-12-04 10:57:01 -05:00
* @ since 5.8 . 0 As `ALLOWED_SETTINGS` .
* @ since 5.9 . 0 Renamed from `ALLOWED_SETTINGS` to `VALID_SETTINGS` ,
* added new properties for `border` , `color` , `spacing` ,
* and `typography` , and renamed others according to the new schema .
2022-04-11 06:38:00 -04:00
* @ since 6.0 . 0 Added `color.defaultDuotone` .
2021-05-24 13:39:57 -04:00
* @ var array
*/
2021-11-08 14:19:58 -05:00
const VALID_SETTINGS = array (
2021-11-29 19:24:27 -05:00
'appearanceTools' => null ,
'border' => array (
2021-11-08 14:19:58 -05:00
'color' => null ,
'radius' => null ,
'style' => null ,
'width' => null ,
2021-08-03 14:14:58 -04:00
),
2021-11-29 19:24:27 -05:00
'color' => array (
2021-11-23 00:40:38 -05:00
'background' => null ,
'custom' => null ,
'customDuotone' => null ,
'customGradient' => null ,
2022-04-11 06:38:00 -04:00
'defaultDuotone' => null ,
2021-11-23 00:40:38 -05:00
'defaultGradients' => null ,
'defaultPalette' => null ,
'duotone' => null ,
'gradients' => null ,
'link' => null ,
'palette' => null ,
'text' => null ,
2021-05-24 04:37:55 -04:00
),
2021-11-29 19:24:27 -05:00
'custom' => null ,
'layout' => array (
2021-07-13 12:41:28 -04:00
'contentSize' => null ,
'wideSize' => null ,
),
2021-11-29 19:24:27 -05:00
'spacing' => array (
2021-11-08 14:19:58 -05:00
'blockGap' => null ,
'margin' => null ,
'padding' => null ,
'units' => null ,
2021-05-24 04:37:55 -04:00
),
2021-11-29 19:24:27 -05:00
'typography' => array (
2021-11-08 14:19:58 -05:00
'customFontSize' => null ,
'dropCap' => null ,
'fontFamilies' => null ,
'fontSizes' => null ,
'fontStyle' => null ,
'fontWeight' => null ,
'letterSpacing' => null ,
'lineHeight' => null ,
'textDecoration' => null ,
'textTransform' => null ,
2021-05-24 04:37:55 -04:00
),
);
2021-05-24 13:39:57 -04:00
/**
2021-11-08 14:19:58 -05:00
* The valid properties under the styles key .
*
2021-12-04 10:57:01 -05:00
* @ since 5.8 . 0 As `ALLOWED_STYLES` .
* @ since 5.9 . 0 Renamed from `ALLOWED_STYLES` to `VALID_STYLES` ,
* added new properties for `border` , `filter` , `spacing` ,
* and `typography` .
2021-05-24 13:39:57 -04:00
* @ var array
*/
2021-11-08 14:19:58 -05:00
const VALID_STYLES = array (
2021-08-03 14:14:58 -04:00
'border' => array (
2021-11-08 14:19:58 -05:00
'color' => null ,
2021-08-03 14:14:58 -04:00
'radius' => null ,
2021-11-08 14:19:58 -05:00
'style' => null ,
'width' => null ,
2021-08-03 14:14:58 -04:00
),
2021-05-24 13:39:57 -04:00
'color' => array (
'background' => null ,
'gradient' => null ,
'text' => null ,
),
2021-11-08 14:19:58 -05:00
'filter' => array (
'duotone' => null ,
),
2021-05-24 13:39:57 -04:00
'spacing' => array (
2021-11-08 14:19:58 -05:00
'margin' => null ,
'padding' => null ,
2022-01-04 00:39:28 -05:00
'blockGap' => 'top' ,
2021-05-24 13:39:57 -04:00
),
'typography' => array (
2021-11-08 14:19:58 -05:00
'fontFamily' => null ,
'fontSize' => null ,
'fontStyle' => null ,
'fontWeight' => null ,
'letterSpacing' => null ,
'lineHeight' => null ,
'textDecoration' => null ,
'textTransform' => null ,
2021-05-24 13:39:57 -04:00
),
);
2022-09-10 08:38:12 -04:00
/**
* Defines which pseudo selectors are enabled for which elements .
*
* Note : this will affect both top - level and block - level elements .
*
* @ since 6.1 . 0
*/
const VALID_ELEMENT_PSEUDO_SELECTORS = array (
'link' => array ( ':hover' , ':focus' , ':active' , ':visited' ),
'button' => array ( ':hover' , ':focus' , ':active' , ':visited' ),
);
2021-05-24 13:39:57 -04:00
/**
2021-11-29 19:24:27 -05:00
* The valid elements that can be found under styles .
*
2021-05-24 13:39:57 -04:00
* @ since 5.8 . 0
2022-09-10 08:38:12 -04:00
* @ since 6.1 . 0 Added `heading` , `button` . and `caption` elements .
2021-07-01 17:02:57 -04:00
* @ var string []
2021-05-24 13:39:57 -04:00
*/
const ELEMENTS = array (
2022-09-10 08:38:12 -04:00
'link' => 'a:where(:not(.wp-element-button))' , // The `where` is needed to lower the specificity.
Global Styles: Add support for `heading`, `button`, and `caption` elements.
This enables themes to:
* Set style rules for all heading elements together rather than having to do it individually.
* Style captions in `theme.json` by adding this into your `theme.json` file:
{{{
{
"styles": {
"elements": {
"caption": {
"color": {
"background": "red",
"text": "yellow"
}
}
}
}
}
}}}
This commit backports the original PRs from Gutenberg repository:
* [https://github.com/WordPress/gutenberg/pull/41981 #41981: Global Styles: Add support for heading elements]
* [https://github.com/WordPress/gutenberg/pull/41140 #41140: Global Styles: Add support for caption elements]
Follow-up to [50973].
Props cbravobernal, scruffian, madhudollu, mikachan, zieladam, bph, poena, andraganescu, ndiego, bgardner.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54105
git-svn-id: http://core.svn.wordpress.org/trunk@53664 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2022-09-08 12:44:11 -04:00
'heading' => 'h1, h2, h3, h4, h5, h6' ,
'h1' => 'h1' ,
'h2' => 'h2' ,
'h3' => 'h3' ,
'h4' => 'h4' ,
'h5' => 'h5' ,
'h6' => 'h6' ,
// We have the .wp-block-button__link class so that this will target older buttons that have been serialized.
'button' => '.wp-element-button, .wp-block-button__link' ,
// The block classes are necessary to target older content that won't use the new class names.
'caption' => '.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption' ,
2021-05-24 13:39:57 -04:00
);
2022-09-10 08:38:12 -04:00
const __EXPERIMENTAL_ELEMENT_CLASS_NAMES = array (
'button' => 'wp-element-button' ,
'caption' => 'wp-element-caption' ,
);
/**
* Returns a class name by an element name .
*
* @ since 6.1 . 0
*
* @ param string $element The name of the element .
* @ return string The name of the class .
*/
public static function get_element_class_name ( $element ) {
$class_name = '' ;
if ( array_key_exists ( $element , static :: __EXPERIMENTAL_ELEMENT_CLASS_NAMES ) ) {
$class_name = static :: __EXPERIMENTAL_ELEMENT_CLASS_NAMES [ $element ];
}
return $class_name ;
}
2022-04-11 06:38:00 -04:00
/**
* Options that settings . appearanceTools enables .
*
* @ since 6.0 . 0
* @ var array
*/
const APPEARANCE_TOOLS_OPT_INS = array (
array ( 'border' , 'color' ),
array ( 'border' , 'radius' ),
array ( 'border' , 'style' ),
array ( 'border' , 'width' ),
array ( 'color' , 'link' ),
array ( 'spacing' , 'blockGap' ),
array ( 'spacing' , 'margin' ),
array ( 'spacing' , 'padding' ),
array ( 'typography' , 'lineHeight' ),
);
2021-05-24 13:39:57 -04:00
/**
2021-11-08 14:19:58 -05:00
* The latest version of the schema in use .
*
2021-05-24 13:39:57 -04:00
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Changed value from 1 to 2.
2021-05-24 13:39:57 -04:00
* @ var int
*/
2021-11-08 14:19:58 -05:00
const LATEST_SCHEMA = 2 ;
2021-05-24 04:37:55 -04:00
/**
* Constructor .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
*
2021-12-06 17:42:00 -05:00
* @ param array $theme_json A structure that follows the theme . json schema .
* @ param string $origin Optional . What source of data this object represents .
* One of 'default' , 'theme' , or 'custom' . Default 'theme' .
2021-05-24 04:37:55 -04:00
*/
2021-06-15 04:52:30 -04:00
public function __construct ( $theme_json = array (), $origin = 'theme' ) {
2022-02-17 04:04:05 -05:00
if ( ! in_array ( $origin , static :: VALID_ORIGINS , true ) ) {
2021-06-15 04:52:30 -04:00
$origin = 'theme' ;
}
2021-11-08 14:19:58 -05:00
$this -> theme_json = WP_Theme_JSON_Schema :: migrate ( $theme_json );
2022-02-17 04:04:05 -05:00
$valid_block_names = array_keys ( static :: get_blocks_metadata () );
$valid_element_names = array_keys ( static :: ELEMENTS );
$theme_json = static :: sanitize ( $this -> theme_json , $valid_block_names , $valid_element_names );
$this -> theme_json = static :: maybe_opt_in_into_settings ( $theme_json );
2021-06-15 04:52:30 -04:00
// Internally, presets are keyed by origin.
2022-02-17 04:04:05 -05:00
$nodes = static :: get_setting_nodes ( $this -> theme_json );
2021-06-15 04:52:30 -04:00
foreach ( $nodes as $node ) {
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $preset_metadata ) {
2021-11-08 14:19:58 -05:00
$path = array_merge ( $node [ 'path' ], $preset_metadata [ 'path' ] );
2021-06-15 04:52:30 -04:00
$preset = _wp_array_get ( $this -> theme_json , $path , null );
if ( null !== $preset ) {
2021-11-29 19:24:27 -05:00
// If the preset is not already keyed by origin.
if ( isset ( $preset [ 0 ] ) || empty ( $preset ) ) {
2021-11-23 00:40:38 -05:00
_wp_array_set ( $this -> theme_json , $path , array ( $origin => $preset ) );
}
2021-06-15 04:52:30 -04:00
}
}
}
2021-05-24 04:37:55 -04:00
}
2021-11-29 19:24:27 -05:00
/**
* Enables some opt - in settings if theme declared support .
*
* @ since 5.9 . 0
*
* @ param array $theme_json A theme . json structure to modify .
* @ return array The modified theme . json structure .
*/
2022-02-17 04:04:05 -05:00
protected static function maybe_opt_in_into_settings ( $theme_json ) {
2021-11-29 19:24:27 -05:00
$new_theme_json = $theme_json ;
2021-12-13 20:57:26 -05:00
if (
isset ( $new_theme_json [ 'settings' ][ 'appearanceTools' ] ) &&
true === $new_theme_json [ 'settings' ][ 'appearanceTools' ]
) {
2022-02-17 04:04:05 -05:00
static :: do_opt_in_into_settings ( $new_theme_json [ 'settings' ] );
2021-11-29 19:24:27 -05:00
}
if ( isset ( $new_theme_json [ 'settings' ][ 'blocks' ] ) && is_array ( $new_theme_json [ 'settings' ][ 'blocks' ] ) ) {
foreach ( $new_theme_json [ 'settings' ][ 'blocks' ] as & $block ) {
2021-12-13 20:57:26 -05:00
if ( isset ( $block [ 'appearanceTools' ] ) && ( true === $block [ 'appearanceTools' ] ) ) {
2022-02-17 04:04:05 -05:00
static :: do_opt_in_into_settings ( $block );
2021-11-29 19:24:27 -05:00
}
}
}
return $new_theme_json ;
}
/**
* Enables some settings .
*
* @ since 5.9 . 0
*
* @ param array $context The context to which the settings belong .
*/
2022-02-17 04:04:05 -05:00
protected static function do_opt_in_into_settings ( & $context ) {
2022-04-11 06:38:00 -04:00
foreach ( static :: APPEARANCE_TOOLS_OPT_INS as $path ) {
2021-12-13 20:57:26 -05:00
// Use "unset prop" as a marker instead of "null" because
// "null" can be a valid value for some props (e.g. blockGap).
if ( 'unset prop' === _wp_array_get ( $context , $path , 'unset prop' ) ) {
2021-11-29 19:24:27 -05:00
_wp_array_set ( $context , $path , true );
}
}
unset ( $context [ 'appearanceTools' ] );
}
2021-05-24 04:37:55 -04:00
/**
* Sanitizes the input according to the schemas .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `$valid_block_names` and `$valid_element_name` parameters .
2021-05-24 04:37:55 -04:00
*
2021-12-04 07:58:01 -05:00
* @ param array $input Structure to sanitize .
* @ param array $valid_block_names List of valid block names .
2021-11-08 14:19:58 -05:00
* @ param array $valid_element_names List of valid element names .
2021-05-24 04:37:55 -04:00
* @ return array The sanitized output .
*/
2022-02-17 04:04:05 -05:00
protected static function sanitize ( $input , $valid_block_names , $valid_element_names ) {
2022-09-10 08:38:12 -04:00
2021-05-24 04:37:55 -04:00
$output = array ();
if ( ! is_array ( $input ) ) {
return $output ;
}
2022-09-10 08:38:12 -04:00
// Preserve only the top most level keys.
2022-02-17 04:04:05 -05:00
$output = array_intersect_key ( $input , array_flip ( static :: VALID_TOP_LEVEL_KEYS ) );
2021-05-24 04:37:55 -04:00
2022-09-10 08:38:12 -04:00
/*
* Remove any rules that are annotated as " top " in VALID_STYLES constant .
* Some styles are only meant to be available at the top - level ( e . g .: blockGap ),
* hence , the schema for blocks & elements should not have them .
*/
2022-02-17 04:04:05 -05:00
$styles_non_top_level = static :: VALID_STYLES ;
2022-01-04 00:39:28 -05:00
foreach ( array_keys ( $styles_non_top_level ) as $section ) {
foreach ( array_keys ( $styles_non_top_level [ $section ] ) as $prop ) {
if ( 'top' === $styles_non_top_level [ $section ][ $prop ] ) {
unset ( $styles_non_top_level [ $section ][ $prop ] );
}
}
}
2021-11-08 14:19:58 -05:00
// Build the schema based on valid block & element names.
2021-05-24 04:37:55 -04:00
$schema = array ();
2021-05-24 13:39:57 -04:00
$schema_styles_elements = array ();
2022-09-10 08:38:12 -04:00
/*
* Set allowed element pseudo selectors based on per element allow list .
* Target data structure in schema :
* e . g .
* - top level elements : `$schema['styles']['elements']['link'][':hover']` .
* - block level elements : `$schema['styles']['blocks']['core/button']['elements']['link'][':hover']` .
*/
2021-11-08 14:19:58 -05:00
foreach ( $valid_element_names as $element ) {
2022-01-04 00:39:28 -05:00
$schema_styles_elements [ $element ] = $styles_non_top_level ;
2022-09-10 08:38:12 -04:00
if ( array_key_exists ( $element , static :: VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $element ] as $pseudo_selector ) {
$schema_styles_elements [ $element ][ $pseudo_selector ] = $styles_non_top_level ;
}
}
2021-05-24 13:39:57 -04:00
}
2022-09-10 08:38:12 -04:00
2021-05-24 13:39:57 -04:00
$schema_styles_blocks = array ();
2021-05-24 04:37:55 -04:00
$schema_settings_blocks = array ();
2021-11-08 14:19:58 -05:00
foreach ( $valid_block_names as $block ) {
2022-02-17 04:04:05 -05:00
$schema_settings_blocks [ $block ] = static :: VALID_SETTINGS ;
2022-01-04 00:39:28 -05:00
$schema_styles_blocks [ $block ] = $styles_non_top_level ;
2021-05-24 13:39:57 -04:00
$schema_styles_blocks [ $block ][ 'elements' ] = $schema_styles_elements ;
2021-05-24 04:37:55 -04:00
}
2022-09-10 08:38:12 -04:00
2022-02-17 04:04:05 -05:00
$schema [ 'styles' ] = static :: VALID_STYLES ;
2021-05-24 13:39:57 -04:00
$schema [ 'styles' ][ 'blocks' ] = $schema_styles_blocks ;
$schema [ 'styles' ][ 'elements' ] = $schema_styles_elements ;
2022-02-17 04:04:05 -05:00
$schema [ 'settings' ] = static :: VALID_SETTINGS ;
2021-05-24 04:37:55 -04:00
$schema [ 'settings' ][ 'blocks' ] = $schema_settings_blocks ;
// Remove anything that's not present in the schema.
2021-05-24 13:39:57 -04:00
foreach ( array ( 'styles' , 'settings' ) as $subtree ) {
2021-05-24 04:37:55 -04:00
if ( ! isset ( $input [ $subtree ] ) ) {
continue ;
}
if ( ! is_array ( $input [ $subtree ] ) ) {
unset ( $output [ $subtree ] );
continue ;
}
2022-02-17 04:04:05 -05:00
$result = static :: remove_keys_not_in_schema ( $input [ $subtree ], $schema [ $subtree ] );
2021-05-24 04:37:55 -04:00
if ( empty ( $result ) ) {
unset ( $output [ $subtree ] );
} else {
$output [ $subtree ] = $result ;
}
}
return $output ;
}
2021-11-29 19:24:27 -05:00
2022-09-10 08:38:12 -04:00
/**
* Appends a sub - selector to an existing one .
*
* Given the compounded $selector " h1, h2, h3 "
* and the $to_append selector " .some-class " the result will be
* " h1.some-class, h2.some-class, h3.some-class " .
*
* @ since 5.8 . 0
* @ since 6.1 . 0 Added append position .
*
* @ param string $selector Original selector .
* @ param string $to_append Selector to append .
* @ param string $position A position sub - selector should be appended . Default 'right' .
* @ return string
*/
protected static function append_to_selector ( $selector , $to_append , $position = 'right' ) {
$new_selectors = array ();
$selectors = explode ( ',' , $selector );
foreach ( $selectors as $sel ) {
$new_selectors [] = 'right' === $position ? $sel . $to_append : $to_append . $sel ;
}
return implode ( ',' , $new_selectors );
}
2021-05-24 13:39:57 -04:00
/**
* Returns the metadata for each block .
*
* Example :
*
2021-06-23 15:05:57 -04:00
* {
* 'core/paragraph' : {
* 'selector' : 'p' ,
* 'elements' : {
* 'link' => 'link selector' ,
* 'etc' => 'element selector'
* }
* },
* 'core/heading' : {
* 'selector' : 'h1' ,
* 'elements' : {}
2021-11-08 14:19:58 -05:00
* },
* 'core/image' : {
* 'selector' : '.wp-block-image' ,
* 'duotone' : 'img' ,
2021-06-23 15:05:57 -04:00
* 'elements' : {}
* }
2021-05-24 13:39:57 -04:00
* }
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added `duotone` key with CSS selector .
2021-05-24 13:39:57 -04:00
*
* @ return array Block metadata .
*/
2022-02-17 04:04:05 -05:00
protected static function get_blocks_metadata () {
if ( null !== static :: $blocks_metadata ) {
return static :: $blocks_metadata ;
2021-05-24 13:39:57 -04:00
}
2022-02-17 04:04:05 -05:00
static :: $blocks_metadata = array ();
2021-05-24 13:39:57 -04:00
$registry = WP_Block_Type_Registry :: get_instance ();
$blocks = $registry -> get_all_registered ();
foreach ( $blocks as $block_name => $block_type ) {
if (
isset ( $block_type -> supports [ '__experimentalSelector' ] ) &&
is_string ( $block_type -> supports [ '__experimentalSelector' ] )
) {
2022-02-17 04:04:05 -05:00
static :: $blocks_metadata [ $block_name ][ 'selector' ] = $block_type -> supports [ '__experimentalSelector' ];
2021-05-24 13:39:57 -04:00
} else {
2022-02-17 04:04:05 -05:00
static :: $blocks_metadata [ $block_name ][ 'selector' ] = '.wp-block-' . str_replace ( '/' , '-' , str_replace ( 'core/' , '' , $block_name ) );
2021-05-24 13:39:57 -04:00
}
2021-11-08 14:19:58 -05:00
if (
isset ( $block_type -> supports [ 'color' ][ '__experimentalDuotone' ] ) &&
is_string ( $block_type -> supports [ 'color' ][ '__experimentalDuotone' ] )
) {
2022-02-17 04:04:05 -05:00
static :: $blocks_metadata [ $block_name ][ 'duotone' ] = $block_type -> supports [ 'color' ][ '__experimentalDuotone' ];
2021-11-08 14:19:58 -05:00
}
// Assign defaults, then overwrite those that the block sets by itself.
// If the block selector is compounded, will append the element to each
// individual block selector.
2022-02-17 04:04:05 -05:00
$block_selectors = explode ( ',' , static :: $blocks_metadata [ $block_name ][ 'selector' ] );
foreach ( static :: ELEMENTS as $el_name => $el_selector ) {
2021-05-24 13:39:57 -04:00
$element_selector = array ();
foreach ( $block_selectors as $selector ) {
2022-09-10 08:38:12 -04:00
if ( $selector === $el_selector ) {
$element_selector = array ( $el_selector );
break ;
}
$element_selector [] = static :: append_to_selector ( $el_selector , $selector . ' ' , 'left' );
2021-05-24 13:39:57 -04:00
}
2022-02-17 04:04:05 -05:00
static :: $blocks_metadata [ $block_name ][ 'elements' ][ $el_name ] = implode ( ',' , $element_selector );
2021-05-24 13:39:57 -04:00
}
}
2022-02-17 04:04:05 -05:00
return static :: $blocks_metadata ;
2021-05-24 13:39:57 -04:00
}
2021-05-24 04:37:55 -04:00
/**
* Given a tree , removes the keys that are not present in the schema .
*
* It is recursive and modifies the input in - place .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
*
2021-06-30 15:00:58 -04:00
* @ param array $tree Input to process .
2021-05-24 04:37:55 -04:00
* @ param array $schema Schema to adhere to .
* @ return array Returns the modified $tree .
*/
2022-02-17 04:04:05 -05:00
protected static function remove_keys_not_in_schema ( $tree , $schema ) {
2021-05-24 04:37:55 -04:00
$tree = array_intersect_key ( $tree , $schema );
foreach ( $schema as $key => $data ) {
if ( ! isset ( $tree [ $key ] ) ) {
continue ;
}
if ( is_array ( $schema [ $key ] ) && is_array ( $tree [ $key ] ) ) {
2022-02-17 04:04:05 -05:00
$tree [ $key ] = static :: remove_keys_not_in_schema ( $tree [ $key ], $schema [ $key ] );
2021-05-24 04:37:55 -04:00
if ( empty ( $tree [ $key ] ) ) {
unset ( $tree [ $key ] );
}
} elseif ( is_array ( $schema [ $key ] ) && ! is_array ( $tree [ $key ] ) ) {
unset ( $tree [ $key ] );
}
}
return $tree ;
}
/**
* Returns the existing settings for each block .
*
* Example :
*
2021-05-24 09:25:56 -04:00
* {
* 'root' : {
* 'color' : {
* 'custom' : true
* }
* },
* 'core/paragraph' : {
* 'spacing' : {
* 'customPadding' : true
* }
* }
2021-05-24 04:37:55 -04:00
* }
2021-05-24 09:25:56 -04:00
*
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
*
* @ return array Settings per block .
*/
public function get_settings () {
if ( ! isset ( $this -> theme_json [ 'settings' ] ) ) {
return array ();
} else {
return $this -> theme_json [ 'settings' ];
}
}
2021-05-24 13:39:57 -04:00
/**
* Returns the stylesheet that results of processing
* the theme . json structure this object represents .
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Removed the `$type` parameter `, added the ` $types ` and ` $origins ` parameters .
2021-05-24 13:39:57 -04:00
*
2021-12-04 07:58:01 -05:00
* @ param array $types Types of styles to load . Will load all by default . It accepts :
* - `variables` : only the CSS Custom Properties for presets & custom ones .
* - `styles` : only the styles section in theme . json .
* - `presets` : only the classes for the presets .
2022-02-17 04:04:05 -05:00
* @ param array $origins A list of origins to include . By default it includes VALID_ORIGINS .
2021-05-24 13:39:57 -04:00
* @ return string Stylesheet .
*/
2022-02-17 04:04:05 -05:00
public function get_stylesheet ( $types = array ( 'variables' , 'styles' , 'presets' ), $origins = null ) {
if ( null === $origins ) {
$origins = static :: VALID_ORIGINS ;
}
2021-11-08 14:19:58 -05:00
if ( is_string ( $types ) ) {
// Dispatch error and map old arguments to new ones.
2021-12-04 07:58:01 -05:00
_deprecated_argument ( __FUNCTION__ , '5.9.0' );
2021-11-08 14:19:58 -05:00
if ( 'block_styles' === $types ) {
$types = array ( 'styles' , 'presets' );
} elseif ( 'css_variables' === $types ) {
$types = array ( 'variables' );
} else {
$types = array ( 'variables' , 'styles' , 'presets' );
}
}
2022-02-17 04:04:05 -05:00
$blocks_metadata = static :: get_blocks_metadata ();
$style_nodes = static :: get_style_nodes ( $this -> theme_json , $blocks_metadata );
$setting_nodes = static :: get_setting_nodes ( $this -> theme_json , $blocks_metadata );
2021-05-24 13:39:57 -04:00
2021-11-08 14:19:58 -05:00
$stylesheet = '' ;
if ( in_array ( 'variables' , $types , true ) ) {
$stylesheet .= $this -> get_css_variables ( $setting_nodes , $origins );
}
if ( in_array ( 'styles' , $types , true ) ) {
$stylesheet .= $this -> get_block_classes ( $style_nodes );
2021-05-24 13:39:57 -04:00
}
2021-11-08 14:19:58 -05:00
if ( in_array ( 'presets' , $types , true ) ) {
$stylesheet .= $this -> get_preset_classes ( $setting_nodes , $origins );
}
return $stylesheet ;
}
/**
2022-01-20 18:53:05 -05:00
* Returns the page templates of the active theme .
2021-11-08 14:19:58 -05:00
*
* @ since 5.9 . 0
*
* @ return array
*/
public function get_custom_templates () {
$custom_templates = array ();
2021-11-08 18:10:59 -05:00
if ( ! isset ( $this -> theme_json [ 'customTemplates' ] ) || ! is_array ( $this -> theme_json [ 'customTemplates' ] ) ) {
2021-11-08 14:19:58 -05:00
return $custom_templates ;
}
foreach ( $this -> theme_json [ 'customTemplates' ] as $item ) {
if ( isset ( $item [ 'name' ] ) ) {
$custom_templates [ $item [ 'name' ] ] = array (
'title' => isset ( $item [ 'title' ] ) ? $item [ 'title' ] : '' ,
'postTypes' => isset ( $item [ 'postTypes' ] ) ? $item [ 'postTypes' ] : array ( 'page' ),
);
}
}
return $custom_templates ;
}
/**
2022-01-20 18:53:05 -05:00
* Returns the template part data of active theme .
2021-11-08 14:19:58 -05:00
*
* @ since 5.9 . 0
*
* @ return array
*/
public function get_template_parts () {
$template_parts = array ();
2021-11-08 18:10:59 -05:00
if ( ! isset ( $this -> theme_json [ 'templateParts' ] ) || ! is_array ( $this -> theme_json [ 'templateParts' ] ) ) {
2021-11-08 14:19:58 -05:00
return $template_parts ;
}
foreach ( $this -> theme_json [ 'templateParts' ] as $item ) {
if ( isset ( $item [ 'name' ] ) ) {
$template_parts [ $item [ 'name' ] ] = array (
'title' => isset ( $item [ 'title' ] ) ? $item [ 'title' ] : '' ,
'area' => isset ( $item [ 'area' ] ) ? $item [ 'area' ] : '' ,
);
}
}
return $template_parts ;
2021-05-24 13:39:57 -04:00
}
/**
* Converts each style section into a list of rulesets
* containing the block styles to be appended to the stylesheet .
*
* See glossary at https :// developer . mozilla . org / en - US / docs / Web / CSS / Syntax
*
* For each section this creates a new ruleset such as :
*
* block - selector {
* style - property - one : value ;
* }
*
2021-12-04 10:57:01 -05:00
* @ since 5.8 . 0 As `get_block_styles()` .
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Renamed from `get_block_styles()` to `get_block_classes()`
* and no longer returns preset classes .
2021-12-04 10:57:01 -05:00
* Removed the `$setting_nodes` parameter .
2021-05-24 13:39:57 -04:00
*
2021-11-08 14:19:58 -05:00
* @ param array $style_nodes Nodes with styles .
2021-05-24 13:39:57 -04:00
* @ return string The new stylesheet .
*/
2022-02-17 04:04:05 -05:00
protected function get_block_classes ( $style_nodes ) {
2021-05-24 13:39:57 -04:00
$block_rules = '' ;
2021-11-08 14:19:58 -05:00
2021-05-24 13:39:57 -04:00
foreach ( $style_nodes as $metadata ) {
if ( null === $metadata [ 'selector' ] ) {
continue ;
}
2022-09-10 08:38:12 -04:00
$block_rules .= static :: get_styles_for_block ( $metadata );
2021-05-24 13:39:57 -04:00
}
2021-11-08 14:19:58 -05:00
return $block_rules ;
}
/**
* Creates new rulesets as classes for each preset value such as :
*
* . has - value - color {
* color : value ;
* }
*
* . has - value - background - color {
* background - color : value ;
* }
*
* . has - value - font - size {
* font - size : value ;
* }
*
* . has - value - gradient - background {
* background : value ;
* }
*
* p . has - value - gradient - background {
* background : value ;
* }
*
* @ since 5.9 . 0
*
* @ param array $setting_nodes Nodes with settings .
* @ param array $origins List of origins to process presets from .
* @ return string The new stylesheet .
*/
2022-02-17 04:04:05 -05:00
protected function get_preset_classes ( $setting_nodes , $origins ) {
2021-05-24 13:39:57 -04:00
$preset_rules = '' ;
2021-11-08 14:19:58 -05:00
2021-05-24 13:39:57 -04:00
foreach ( $setting_nodes as $metadata ) {
if ( null === $metadata [ 'selector' ] ) {
continue ;
}
$selector = $metadata [ 'selector' ];
$node = _wp_array_get ( $this -> theme_json , $metadata [ 'path' ], array () );
2022-02-17 04:04:05 -05:00
$preset_rules .= static :: compute_preset_classes ( $node , $selector , $origins );
2021-05-24 13:39:57 -04:00
}
2021-11-08 14:19:58 -05:00
return $preset_rules ;
2021-05-24 13:39:57 -04:00
}
/**
* Converts each styles section into a list of rulesets
* to be appended to the stylesheet .
* These rulesets contain all the css variables ( custom variables and preset variables ) .
*
* See glossary at https :// developer . mozilla . org / en - US / docs / Web / CSS / Syntax
*
* For each section this creates a new ruleset such as :
*
2021-06-23 15:05:57 -04:00
* block - selector {
* -- wp -- preset -- category -- slug : value ;
* -- wp -- custom -- variable : value ;
* }
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `$origins` parameter .
2021-05-24 13:39:57 -04:00
*
2021-12-04 07:58:01 -05:00
* @ param array $nodes Nodes with settings .
2021-11-08 14:19:58 -05:00
* @ param array $origins List of origins to process .
2021-05-24 13:39:57 -04:00
* @ return string The new stylesheet .
*/
2022-02-17 04:04:05 -05:00
protected function get_css_variables ( $nodes , $origins ) {
2021-05-24 13:39:57 -04:00
$stylesheet = '' ;
foreach ( $nodes as $metadata ) {
if ( null === $metadata [ 'selector' ] ) {
continue ;
}
$selector = $metadata [ 'selector' ];
$node = _wp_array_get ( $this -> theme_json , $metadata [ 'path' ], array () );
2022-02-17 04:04:05 -05:00
$declarations = array_merge ( static :: compute_preset_vars ( $node , $origins ), static :: compute_theme_vars ( $node ) );
2021-05-24 13:39:57 -04:00
2022-02-17 04:04:05 -05:00
$stylesheet .= static :: to_ruleset ( $selector , $declarations );
2021-05-24 13:39:57 -04:00
}
return $stylesheet ;
}
/**
* Given a selector and a declaration list ,
* creates the corresponding ruleset .
*
* @ since 5.8 . 0
*
2021-06-30 15:00:58 -04:00
* @ param string $selector CSS selector .
2021-05-24 13:39:57 -04:00
* @ param array $declarations List of declarations .
* @ return string CSS ruleset .
*/
2022-02-17 04:04:05 -05:00
protected static function to_ruleset ( $selector , $declarations ) {
2021-05-24 13:39:57 -04:00
if ( empty ( $declarations ) ) {
return '' ;
}
$declaration_block = array_reduce (
$declarations ,
2021-08-26 08:59:02 -04:00
static function ( $carry , $element ) {
2021-05-24 13:39:57 -04:00
return $carry .= $element [ 'name' ] . ': ' . $element [ 'value' ] . ';' ; },
''
);
return $selector . '{' . $declaration_block . '}' ;
}
/**
* Given a settings array , it returns the generated rulesets
* for the preset classes .
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `$origins` parameter .
2021-05-24 13:39:57 -04:00
*
* @ param array $settings Settings to process .
* @ param string $selector Selector wrapping the classes .
2021-11-08 14:19:58 -05:00
* @ param array $origins List of origins to process .
2021-05-24 13:39:57 -04:00
* @ return string The result of processing the presets .
*/
2022-02-17 04:04:05 -05:00
protected static function compute_preset_classes ( $settings , $selector , $origins ) {
if ( static :: ROOT_BLOCK_SELECTOR === $selector ) {
2021-05-24 13:39:57 -04:00
// Classes at the global level do not need any CSS prefixed,
// and we don't want to increase its specificity.
$selector = '' ;
}
$stylesheet = '' ;
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $preset_metadata ) {
$slugs = static :: get_settings_slugs ( $settings , $preset_metadata , $origins );
2021-11-08 14:19:58 -05:00
foreach ( $preset_metadata [ 'classes' ] as $class => $property ) {
foreach ( $slugs as $slug ) {
2022-02-17 04:04:05 -05:00
$css_var = static :: replace_slug_in_string ( $preset_metadata [ 'css_vars' ], $slug );
$class_name = static :: replace_slug_in_string ( $class , $slug );
$stylesheet .= static :: to_ruleset (
static :: append_to_selector ( $selector , $class_name ),
2021-05-24 13:39:57 -04:00
array (
array (
2021-11-08 14:19:58 -05:00
'name' => $property ,
'value' => 'var(' . $css_var . ') !important' ,
2021-05-24 13:39:57 -04:00
),
)
);
}
}
}
return $stylesheet ;
}
2021-11-08 14:19:58 -05:00
/**
* Function that scopes a selector with another one . This works a bit like
* SCSS nesting except the `&` operator isn ' t supported .
*
* < code >
* $scope = '.a, .b .c' ;
* $selector = '> .x, .y' ;
* $merged = scope_selector ( $scope , $selector );
* // $merged is '.a > .x, .a .y, .b .c > .x, .b .c .y'
* </ code >
*
* @ since 5.9 . 0
*
* @ param string $scope Selector to scope to .
* @ param string $selector Original selector .
* @ return string Scoped selector .
*/
2022-02-17 04:04:05 -05:00
protected static function scope_selector ( $scope , $selector ) {
2021-11-08 14:19:58 -05:00
$scopes = explode ( ',' , $scope );
$selectors = explode ( ',' , $selector );
$selectors_scoped = array ();
foreach ( $scopes as $outer ) {
foreach ( $selectors as $inner ) {
$selectors_scoped [] = trim ( $outer ) . ' ' . trim ( $inner );
}
}
return implode ( ', ' , $selectors_scoped );
}
/**
* Gets preset values keyed by slugs based on settings and metadata .
*
* < code >
* $settings = array (
* 'typography' => array (
* 'fontFamilies' => array (
* array (
* 'slug' => 'sansSerif' ,
* 'fontFamily' => '"Helvetica Neue", sans-serif' ,
* ),
* array (
* 'slug' => 'serif' ,
* 'colors' => 'Georgia, serif' ,
* )
* ),
* ),
* );
* $meta = array (
* 'path' => array ( 'typography' , 'fontFamilies' ),
* 'value_key' => 'fontFamily' ,
* );
* $values_by_slug = get_settings_values_by_slug ();
* // $values_by_slug === array(
* // 'sans-serif' => '"Helvetica Neue", sans-serif',
* // 'serif' => 'Georgia, serif',
* // );
* </ code >
*
* @ since 5.9 . 0
*
2021-12-04 07:58:01 -05:00
* @ param array $settings Settings to process .
2021-11-08 14:19:58 -05:00
* @ param array $preset_metadata One of the PRESETS_METADATA values .
2021-12-04 07:58:01 -05:00
* @ param array $origins List of origins to process .
2021-11-08 14:19:58 -05:00
* @ return array Array of presets where each key is a slug and each value is the preset value .
*/
2022-02-17 04:04:05 -05:00
protected static function get_settings_values_by_slug ( $settings , $preset_metadata , $origins ) {
2021-11-08 14:19:58 -05:00
$preset_per_origin = _wp_array_get ( $settings , $preset_metadata [ 'path' ], array () );
$result = array ();
foreach ( $origins as $origin ) {
if ( ! isset ( $preset_per_origin [ $origin ] ) ) {
continue ;
}
foreach ( $preset_per_origin [ $origin ] as $preset ) {
$slug = _wp_to_kebab_case ( $preset [ 'slug' ] );
$value = '' ;
2022-02-17 13:47:02 -05:00
if ( isset ( $preset_metadata [ 'value_key' ], $preset [ $preset_metadata [ 'value_key' ] ] ) ) {
2021-11-08 14:19:58 -05:00
$value_key = $preset_metadata [ 'value_key' ];
$value = $preset [ $value_key ];
} elseif (
isset ( $preset_metadata [ 'value_func' ] ) &&
is_callable ( $preset_metadata [ 'value_func' ] )
) {
$value_func = $preset_metadata [ 'value_func' ];
$value = call_user_func ( $value_func , $preset );
} else {
// If we don't have a value, then don't add it to the result.
continue ;
}
$result [ $slug ] = $value ;
}
}
return $result ;
}
/**
* Similar to get_settings_values_by_slug , but doesn ' t compute the value .
*
* @ since 5.9 . 0
*
2021-12-04 07:58:01 -05:00
* @ param array $settings Settings to process .
2021-11-08 14:19:58 -05:00
* @ param array $preset_metadata One of the PRESETS_METADATA values .
2021-12-04 07:58:01 -05:00
* @ param array $origins List of origins to process .
2021-11-08 14:19:58 -05:00
* @ return array Array of presets where the key and value are both the slug .
*/
2022-02-17 04:04:05 -05:00
protected static function get_settings_slugs ( $settings , $preset_metadata , $origins = null ) {
if ( null === $origins ) {
$origins = static :: VALID_ORIGINS ;
}
2021-11-08 14:19:58 -05:00
$preset_per_origin = _wp_array_get ( $settings , $preset_metadata [ 'path' ], array () );
$result = array ();
foreach ( $origins as $origin ) {
if ( ! isset ( $preset_per_origin [ $origin ] ) ) {
continue ;
}
foreach ( $preset_per_origin [ $origin ] as $preset ) {
$slug = _wp_to_kebab_case ( $preset [ 'slug' ] );
// Use the array as a set so we don't get duplicates.
$result [ $slug ] = $slug ;
}
}
return $result ;
}
/**
* Transform a slug into a CSS Custom Property .
*
* @ since 5.9 . 0
*
* @ param string $input String to replace .
2021-12-04 07:58:01 -05:00
* @ param string $slug The slug value to use to generate the custom property .
* @ return string The CSS Custom Property . Something along the lines of `--wp--preset--color--black` .
2021-11-08 14:19:58 -05:00
*/
2022-02-17 04:04:05 -05:00
protected static function replace_slug_in_string ( $input , $slug ) {
2021-11-08 14:19:58 -05:00
return strtr ( $input , array ( '$slug' => $slug ) );
}
2021-05-24 13:39:57 -04:00
/**
* Given the block settings , it extracts the CSS Custom Properties
* for the presets and adds them to the $declarations array
* following the format :
*
2021-06-23 15:05:57 -04:00
* array (
* 'name' => 'property_name' ,
* 'value' => ' property_value ,
* )
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `$origins` parameter .
2021-05-24 13:39:57 -04:00
*
* @ param array $settings Settings to process .
2021-11-08 14:19:58 -05:00
* @ param array $origins List of origins to process .
2021-05-24 13:39:57 -04:00
* @ return array Returns the modified $declarations .
*/
2022-02-17 04:04:05 -05:00
protected static function compute_preset_vars ( $settings , $origins ) {
2021-05-24 13:39:57 -04:00
$declarations = array ();
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $preset_metadata ) {
$values_by_slug = static :: get_settings_values_by_slug ( $settings , $preset_metadata , $origins );
2021-11-08 14:19:58 -05:00
foreach ( $values_by_slug as $slug => $value ) {
2021-05-24 13:39:57 -04:00
$declarations [] = array (
2022-02-17 04:04:05 -05:00
'name' => static :: replace_slug_in_string ( $preset_metadata [ 'css_vars' ], $slug ),
2021-06-15 04:52:30 -04:00
'value' => $value ,
2021-05-24 13:39:57 -04:00
);
}
}
return $declarations ;
}
/**
* Given an array of settings , it extracts the CSS Custom Properties
* for the custom values and adds them to the $declarations
* array following the format :
*
2021-06-23 15:05:57 -04:00
* array (
* 'name' => 'property_name' ,
* 'value' => ' property_value ,
* )
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
*
* @ param array $settings Settings to process .
* @ return array Returns the modified $declarations .
*/
2022-02-17 04:04:05 -05:00
protected static function compute_theme_vars ( $settings ) {
2021-05-24 13:39:57 -04:00
$declarations = array ();
$custom_values = _wp_array_get ( $settings , array ( 'custom' ), array () );
2022-02-17 04:04:05 -05:00
$css_vars = static :: flatten_tree ( $custom_values );
2021-05-24 13:39:57 -04:00
foreach ( $css_vars as $key => $value ) {
$declarations [] = array (
'name' => '--wp--custom--' . $key ,
'value' => $value ,
);
}
return $declarations ;
}
/**
* Given a tree , it creates a flattened one
* by merging the keys and binding the leaf values
* to the new keys .
*
* It also transforms camelCase names into kebab - case
* and substitutes '/' by '-' .
*
* This is thought to be useful to generate
* CSS Custom Properties from a tree ,
* although there ' s nothing in the implementation
* of this function that requires that format .
*
* For example , assuming the given prefix is '--wp'
* and the token is '--' , for this input tree :
*
2021-06-23 15:05:57 -04:00
* {
* 'some/property' : 'value' ,
* 'nestedProperty' : {
* 'sub-property' : 'value'
* }
* }
2021-05-24 13:39:57 -04:00
*
* it ' ll return this output :
*
2021-06-23 15:05:57 -04:00
* {
* '--wp--some-property' : 'value' ,
* '--wp--nested-property--sub-property' : 'value'
* }
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
*
2021-06-30 15:00:58 -04:00
* @ param array $tree Input tree to process .
* @ param string $prefix Optional . Prefix to prepend to each variable . Default empty string .
* @ param string $token Optional . Token to use between levels . Default '--' .
2021-05-24 13:39:57 -04:00
* @ return array The flattened tree .
*/
2022-02-17 04:04:05 -05:00
protected static function flatten_tree ( $tree , $prefix = '' , $token = '--' ) {
2021-05-24 13:39:57 -04:00
$result = array ();
foreach ( $tree as $property => $value ) {
$new_key = $prefix . str_replace (
'/' ,
'-' ,
2021-12-21 02:02:34 -05:00
strtolower ( _wp_to_kebab_case ( $property ) )
2021-05-24 13:39:57 -04:00
);
if ( is_array ( $value ) ) {
$new_prefix = $new_key . $token ;
$result = array_merge (
$result ,
2022-02-17 04:04:05 -05:00
static :: flatten_tree ( $value , $new_prefix , $token )
2021-05-24 13:39:57 -04:00
);
} else {
$result [ $new_key ] = $value ;
}
}
return $result ;
}
/**
* Given a styles array , it extracts the style properties
* and adds them to the $declarations array following the format :
*
2021-06-23 15:05:57 -04:00
* array (
* 'name' => 'property_name' ,
* 'value' => ' property_value ,
* )
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added the `$settings` and `$properties` parameters .
2022-09-10 08:38:12 -04:00
* @ since 6.1 . 0 Added the `$theme_json` parameter .
2021-05-24 13:39:57 -04:00
*
2021-12-04 07:58:01 -05:00
* @ param array $styles Styles to process .
* @ param array $settings Theme settings .
2021-11-08 14:19:58 -05:00
* @ param array $properties Properties metadata .
2022-09-10 08:38:12 -04:00
* @ param array $theme_json Theme JSON array .
2021-05-24 13:39:57 -04:00
* @ return array Returns the modified $declarations .
*/
2022-09-10 08:38:12 -04:00
protected static function compute_style_properties ( $styles , $settings = array (), $properties = null , $theme_json = null ) {
2022-02-17 04:04:05 -05:00
if ( null === $properties ) {
$properties = static :: PROPERTIES_METADATA ;
}
2021-05-24 13:39:57 -04:00
$declarations = array ();
if ( empty ( $styles ) ) {
return $declarations ;
}
2021-11-08 14:19:58 -05:00
foreach ( $properties as $css_property => $value_path ) {
2022-09-10 08:38:12 -04:00
$value = static :: get_property_value ( $styles , $value_path , $theme_json );
2021-11-08 14:19:58 -05:00
// Look up protected properties, keyed by value path.
// Skip protected properties that are explicitly set to `null`.
if ( is_array ( $value_path ) ) {
$path_string = implode ( '.' , $value_path );
if (
2022-02-17 04:04:05 -05:00
array_key_exists ( $path_string , static :: PROTECTED_PROPERTIES ) &&
_wp_array_get ( $settings , static :: PROTECTED_PROPERTIES [ $path_string ], null ) === null
2021-11-08 14:19:58 -05:00
) {
continue ;
2021-05-24 13:39:57 -04:00
}
}
2021-11-08 14:19:58 -05:00
// Skip if empty and not "0" or value represents array of longhand values.
$has_missing_value = empty ( $value ) && ! is_numeric ( $value );
if ( $has_missing_value || is_array ( $value ) ) {
2021-05-24 13:39:57 -04:00
continue ;
}
$declarations [] = array (
2021-11-08 14:19:58 -05:00
'name' => $css_property ,
2021-05-24 13:39:57 -04:00
'value' => $value ,
);
}
return $declarations ;
}
/**
* Returns the style property for the given path .
*
* It also converts CSS Custom Property stored as
* " var:preset|color|secondary " to the form
* " --wp--preset--color--secondary " .
*
2022-09-10 08:38:12 -04:00
* It also converts references to a path to the value
* stored at that location , e . g .
* { " ref " : " style.color.background " } => " #fff " .
*
2021-05-24 13:39:57 -04:00
* @ since 5.8 . 0
2021-12-04 07:58:01 -05:00
* @ since 5.9 . 0 Added support for values of array type , which are returned as is .
2022-09-10 08:38:12 -04:00
* @ since 6.1 . 0 Added the `$theme_json` parameter .
2021-05-24 13:39:57 -04:00
*
* @ param array $styles Styles subtree .
2021-06-30 15:00:58 -04:00
* @ param array $path Which property to process .
2022-09-10 08:38:12 -04:00
* @ param array $theme_json Theme JSON array .
2021-12-04 07:58:01 -05:00
* @ return string | array Style property value .
2021-05-24 13:39:57 -04:00
*/
2022-09-10 08:38:12 -04:00
protected static function get_property_value ( $styles , $path , $theme_json = null ) {
2021-05-24 13:39:57 -04:00
$value = _wp_array_get ( $styles , $path , '' );
2022-09-10 08:38:12 -04:00
/*
* This converts references to a path to the value at that path
* where the values is an array with a " ref " key , pointing to a path .
* For example : { " ref " : " style.color.background " } => " #fff " .
*/
if ( is_array ( $value ) && array_key_exists ( 'ref' , $value ) ) {
$value_path = explode ( '.' , $value [ 'ref' ] );
$ref_value = _wp_array_get ( $theme_json , $value_path );
// Only use the ref value if we find anything.
if ( ! empty ( $ref_value ) && is_string ( $ref_value ) ) {
$value = $ref_value ;
}
if ( is_array ( $ref_value ) && array_key_exists ( 'ref' , $ref_value ) ) {
$path_string = json_encode ( $path );
$ref_value_string = json_encode ( $ref_value );
_doing_it_wrong (
'get_property_value' ,
sprintf (
/* translators: 1: theme.json, 2: Value name, 3: Value path, 4: Another value name. */
__ ( 'Your %1$s file uses a dynamic value (%2$s) for the path at %3$s. However, the value at %3$s is also a dynamic value (pointing to %4$s) and pointing to another dynamic value is not supported. Please update %3$s to point directly to %4$s.' ),
'theme.json' ,
$ref_value_string ,
$path_string ,
$ref_value [ 'ref' ]
),
'6.1.0'
);
}
}
2021-11-08 14:19:58 -05:00
if ( '' === $value || is_array ( $value ) ) {
2021-05-24 13:39:57 -04:00
return $value ;
}
2022-09-10 08:38:12 -04:00
// Convert custom CSS properties.
2021-05-24 13:39:57 -04:00
$prefix = 'var:' ;
$prefix_len = strlen ( $prefix );
$token_in = '|' ;
$token_out = '--' ;
if ( 0 === strncmp ( $value , $prefix , $prefix_len ) ) {
$unwrapped_name = str_replace (
$token_in ,
$token_out ,
substr ( $value , $prefix_len )
);
$value = " var(--wp-- $unwrapped_name ) " ;
}
return $value ;
}
2021-05-24 04:37:55 -04:00
/**
* Builds metadata for the setting nodes , which returns in the form of :
*
2021-06-23 15:05:57 -04:00
* [
* [
* 'path' => [ 'path' , 'to' , 'some' , 'node' ],
* 'selector' => 'CSS selector for some node'
* ],
* [
* 'path' => [ 'path' , 'to' , 'other' , 'node' ],
* 'selector' => 'CSS selector for other node'
* ],
* ]
2021-05-24 04:37:55 -04:00
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
*
2021-05-24 09:25:56 -04:00
* @ param array $theme_json The tree to extract setting nodes from .
2021-06-30 15:00:58 -04:00
* @ param array $selectors List of selectors per block .
2021-05-24 04:37:55 -04:00
* @ return array
*/
2022-02-17 04:04:05 -05:00
protected static function get_setting_nodes ( $theme_json , $selectors = array () ) {
2021-05-24 04:37:55 -04:00
$nodes = array ();
if ( ! isset ( $theme_json [ 'settings' ] ) ) {
return $nodes ;
}
// Top-level.
$nodes [] = array (
2021-05-24 13:39:57 -04:00
'path' => array ( 'settings' ),
2022-02-17 04:04:05 -05:00
'selector' => static :: ROOT_BLOCK_SELECTOR ,
2021-05-24 04:37:55 -04:00
);
// Calculate paths for blocks.
if ( ! isset ( $theme_json [ 'settings' ][ 'blocks' ] ) ) {
return $nodes ;
}
foreach ( $theme_json [ 'settings' ][ 'blocks' ] as $name => $node ) {
2021-05-24 13:39:57 -04:00
$selector = null ;
if ( isset ( $selectors [ $name ][ 'selector' ] ) ) {
$selector = $selectors [ $name ][ 'selector' ];
}
2021-05-24 04:37:55 -04:00
$nodes [] = array (
2021-05-24 13:39:57 -04:00
'path' => array ( 'settings' , 'blocks' , $name ),
'selector' => $selector ,
2021-05-24 04:37:55 -04:00
);
}
return $nodes ;
}
2021-05-24 13:39:57 -04:00
/**
* Builds metadata for the style nodes , which returns in the form of :
*
2021-06-23 15:05:57 -04:00
* [
* [
* 'path' => [ 'path' , 'to' , 'some' , 'node' ],
2021-11-08 14:19:58 -05:00
* 'selector' => 'CSS selector for some node' ,
* 'duotone' => 'CSS selector for duotone for some node'
2021-06-23 15:05:57 -04:00
* ],
* [
* 'path' => [ 'path' , 'to' , 'other' , 'node' ],
2021-11-08 14:19:58 -05:00
* 'selector' => 'CSS selector for other node' ,
* 'duotone' => null
2021-06-23 15:05:57 -04:00
* ],
* ]
2021-05-24 13:39:57 -04:00
*
* @ since 5.8 . 0
*
* @ param array $theme_json The tree to extract style nodes from .
2021-06-30 15:00:58 -04:00
* @ param array $selectors List of selectors per block .
2021-05-24 13:39:57 -04:00
* @ return array
*/
2022-02-17 04:04:05 -05:00
protected static function get_style_nodes ( $theme_json , $selectors = array () ) {
2021-05-24 13:39:57 -04:00
$nodes = array ();
if ( ! isset ( $theme_json [ 'styles' ] ) ) {
return $nodes ;
}
// Top-level.
$nodes [] = array (
'path' => array ( 'styles' ),
2022-02-17 04:04:05 -05:00
'selector' => static :: ROOT_BLOCK_SELECTOR ,
2021-05-24 13:39:57 -04:00
);
if ( isset ( $theme_json [ 'styles' ][ 'elements' ] ) ) {
Global Styles: Add support for `heading`, `button`, and `caption` elements.
This enables themes to:
* Set style rules for all heading elements together rather than having to do it individually.
* Style captions in `theme.json` by adding this into your `theme.json` file:
{{{
{
"styles": {
"elements": {
"caption": {
"color": {
"background": "red",
"text": "yellow"
}
}
}
}
}
}}}
This commit backports the original PRs from Gutenberg repository:
* [https://github.com/WordPress/gutenberg/pull/41981 #41981: Global Styles: Add support for heading elements]
* [https://github.com/WordPress/gutenberg/pull/41140 #41140: Global Styles: Add support for caption elements]
Follow-up to [50973].
Props cbravobernal, scruffian, madhudollu, mikachan, zieladam, bph, poena, andraganescu, ndiego, bgardner.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54105
git-svn-id: http://core.svn.wordpress.org/trunk@53664 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2022-09-08 12:44:11 -04:00
foreach ( self :: ELEMENTS as $element => $selector ) {
if ( ! isset ( $theme_json [ 'styles' ][ 'elements' ][ $element ] ) ) {
continue ;
}
2021-05-24 13:39:57 -04:00
$nodes [] = array (
'path' => array ( 'styles' , 'elements' , $element ),
2022-02-17 04:04:05 -05:00
'selector' => static :: ELEMENTS [ $element ],
2021-05-24 13:39:57 -04:00
);
2022-09-10 08:38:12 -04:00
// Handle any pseudo selectors for the element.
if ( array_key_exists ( $element , static :: VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $element ] as $pseudo_selector ) {
if ( isset ( $theme_json [ 'styles' ][ 'elements' ][ $element ][ $pseudo_selector ] ) ) {
$nodes [] = array (
'path' => array ( 'styles' , 'elements' , $element ),
'selector' => static :: append_to_selector ( static :: ELEMENTS [ $element ], $pseudo_selector ),
);
}
}
}
2021-05-24 13:39:57 -04:00
}
}
// Blocks.
if ( ! isset ( $theme_json [ 'styles' ][ 'blocks' ] ) ) {
return $nodes ;
}
2022-09-10 08:38:12 -04:00
$nodes = array_merge ( $nodes , static :: get_block_nodes ( $theme_json ) );
/**
* Filters the list of style nodes with metadata .
*
* This allows for things like loading block CSS independently .
*
* @ since 6.1 . 0
*
* @ param array $nodes Style nodes with metadata .
*/
return apply_filters ( 'get_style_nodes' , $nodes );
}
/**
* A public helper to get the block nodes from a theme . json file .
*
* @ since 6.1 . 0
*
* @ return array The block nodes in theme . json .
*/
public function get_styles_block_nodes () {
return static :: get_block_nodes ( $this -> theme_json );
}
/**
* An internal method to get the block nodes from a theme . json file .
*
* @ since 6.1 . 0
*
* @ param array $theme_json The theme . json converted to an array .
* @ return array The block nodes in theme . json .
*/
private static function get_block_nodes ( $theme_json ) {
$selectors = static :: get_blocks_metadata ();
$nodes = array ();
if ( ! isset ( $theme_json [ 'styles' ] ) ) {
return $nodes ;
}
// Blocks.
if ( ! isset ( $theme_json [ 'styles' ][ 'blocks' ] ) ) {
return $nodes ;
}
2021-05-24 13:39:57 -04:00
foreach ( $theme_json [ 'styles' ][ 'blocks' ] as $name => $node ) {
$selector = null ;
if ( isset ( $selectors [ $name ][ 'selector' ] ) ) {
$selector = $selectors [ $name ][ 'selector' ];
}
2021-11-08 14:19:58 -05:00
$duotone_selector = null ;
if ( isset ( $selectors [ $name ][ 'duotone' ] ) ) {
$duotone_selector = $selectors [ $name ][ 'duotone' ];
}
2021-05-24 13:39:57 -04:00
$nodes [] = array (
2022-09-10 08:38:12 -04:00
'name' => $name ,
2021-05-24 13:39:57 -04:00
'path' => array ( 'styles' , 'blocks' , $name ),
'selector' => $selector ,
2021-11-08 14:19:58 -05:00
'duotone' => $duotone_selector ,
2021-05-24 13:39:57 -04:00
);
if ( isset ( $theme_json [ 'styles' ][ 'blocks' ][ $name ][ 'elements' ] ) ) {
foreach ( $theme_json [ 'styles' ][ 'blocks' ][ $name ][ 'elements' ] as $element => $node ) {
$nodes [] = array (
'path' => array ( 'styles' , 'blocks' , $name , 'elements' , $element ),
'selector' => $selectors [ $name ][ 'elements' ][ $element ],
);
2022-09-10 08:38:12 -04:00
// Handle any pseudo selectors for the element.
if ( array_key_exists ( $element , static :: VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $element ] as $pseudo_selector ) {
if ( isset ( $theme_json [ 'styles' ][ 'blocks' ][ $name ][ 'elements' ][ $element ][ $pseudo_selector ] ) ) {
$nodes [] = array (
'path' => array ( 'styles' , 'blocks' , $name , 'elements' , $element ),
'selector' => static :: append_to_selector ( $selectors [ $name ][ 'elements' ][ $element ], $pseudo_selector ),
);
}
}
}
2021-05-24 13:39:57 -04:00
}
}
}
return $nodes ;
}
2022-09-10 08:38:12 -04:00
/**
* Gets the CSS rules for a particular block from theme . json .
*
* @ since 6.1 . 0
*
* @ param array $block_metadata Meta data about the block to get styles for .
* @ return array Styles for the block .
*/
public function get_styles_for_block ( $block_metadata ) {
$node = _wp_array_get ( $this -> theme_json , $block_metadata [ 'path' ], array () );
$selector = $block_metadata [ 'selector' ];
$settings = _wp_array_get ( $this -> theme_json , array ( 'settings' ) );
/*
* Get a reference to element name from path .
* $block_metadata [ 'path' ] = array ( 'styles' , 'elements' , 'link' );
* Make sure that $block_metadata [ 'path' ] describes an element node , like [ 'styles' , 'element' , 'link' ] .
* Skip non - element paths like just [ 'styles' ] .
*/
$is_processing_element = in_array ( 'elements' , $block_metadata [ 'path' ], true );
$current_element = $is_processing_element ? $block_metadata [ 'path' ][ count ( $block_metadata [ 'path' ] ) - 1 ] : null ;
$element_pseudo_allowed = array ();
if ( array_key_exists ( $current_element , static :: VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
$element_pseudo_allowed = static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $current_element ];
}
/*
* Check for allowed pseudo classes ( e . g . " :hover " ) from the $selector ( " a:hover " ) .
* This also resets the array keys .
*/
$pseudo_matches = array_values (
array_filter (
$element_pseudo_allowed ,
function ( $pseudo_selector ) use ( $selector ) {
return str_contains ( $selector , $pseudo_selector );
}
)
);
$pseudo_selector = isset ( $pseudo_matches [ 0 ] ) ? $pseudo_matches [ 0 ] : null ;
/*
* If the current selector is a pseudo selector that ' s defined in the allow list for the current
* element then compute the style properties for it .
* Otherwise just compute the styles for the default selector as normal .
*/
if ( $pseudo_selector && isset ( $node [ $pseudo_selector ] ) &&
array_key_exists ( $current_element , static :: VALID_ELEMENT_PSEUDO_SELECTORS )
&& in_array ( $pseudo_selector , static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $current_element ], true )
) {
$declarations = static :: compute_style_properties ( $node [ $pseudo_selector ], $settings , null , $this -> theme_json );
} else {
$declarations = static :: compute_style_properties ( $node , $settings , null , $this -> theme_json );
}
$block_rules = '' ;
/*
* 1. Separate the declarations that use the general selector
* from the ones using the duotone selector .
*/
$declarations_duotone = array ();
foreach ( $declarations as $index => $declaration ) {
if ( 'filter' === $declaration [ 'name' ] ) {
unset ( $declarations [ $index ] );
$declarations_duotone [] = $declaration ;
}
}
/*
* Reset default browser margin on the root body element .
* This is set on the root selector ** before ** generating the ruleset
* from the `theme.json` . This is to ensure that if the `theme.json` declares
* `margin` in its `spacing` declaration for the `body` element then these
* user - generated values take precedence in the CSS cascade .
* @ link https :// github . com / WordPress / gutenberg / issues / 36147.
*/
if ( static :: ROOT_BLOCK_SELECTOR === $selector ) {
$block_rules .= 'body { margin: 0; }' ;
}
// 2. Generate and append the rules that use the general selector.
$block_rules .= static :: to_ruleset ( $selector , $declarations );
// 3. Generate and append the rules that use the duotone selector.
if ( isset ( $block_metadata [ 'duotone' ] ) && ! empty ( $declarations_duotone ) ) {
$selector_duotone = static :: scope_selector ( $block_metadata [ 'selector' ], $block_metadata [ 'duotone' ] );
$block_rules .= static :: to_ruleset ( $selector_duotone , $declarations_duotone );
}
if ( static :: ROOT_BLOCK_SELECTOR === $selector ) {
$block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }' ;
$block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }' ;
$block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }' ;
$has_block_gap_support = _wp_array_get ( $this -> theme_json , array ( 'settings' , 'spacing' , 'blockGap' ) ) !== null ;
if ( $has_block_gap_support ) {
$block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }' ;
$block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }' ;
}
}
return $block_rules ;
}
2022-04-11 06:38:00 -04:00
/**
* For metadata values that can either be booleans or paths to booleans , gets the value .
*
* `` ` php
* $data = array (
* 'color' => array (
* 'defaultPalette' => true
* )
* );
*
* static :: get_metadata_boolean ( $data , false );
* // => false
*
* static :: get_metadata_boolean ( $data , array ( 'color' , 'defaultPalette' ) );
* // => true
* `` `
*
* @ since 6.0 . 0
*
2022-04-28 05:59:13 -04:00
* @ param array $data The data to inspect .
* @ param bool | array $path Boolean or path to a boolean .
2022-04-11 06:38:00 -04:00
* @ param bool $default Default value if the referenced path is missing .
2022-04-28 05:59:13 -04:00
* Default false .
* @ return bool Value of boolean metadata .
2022-04-11 06:38:00 -04:00
*/
protected static function get_metadata_boolean ( $data , $path , $default = false ) {
if ( is_bool ( $path ) ) {
return $path ;
}
if ( is_array ( $path ) ) {
$value = _wp_array_get ( $data , $path );
if ( null !== $value ) {
return $value ;
}
}
return $default ;
}
2021-05-24 04:37:55 -04:00
/**
* Merge new incoming data .
*
2021-06-30 12:23:57 -04:00
* @ since 5.8 . 0
2021-11-08 14:19:58 -05:00
* @ since 5.9 . 0 Duotone preset also has origins .
2021-06-30 12:23:57 -04:00
*
2021-05-24 04:37:55 -04:00
* @ param WP_Theme_JSON $incoming Data to merge .
*/
2021-06-15 04:52:30 -04:00
public function merge ( $incoming ) {
$incoming_data = $incoming -> get_raw_data ();
$this -> theme_json = array_replace_recursive ( $this -> theme_json , $incoming_data );
2021-05-24 14:57:55 -04:00
2021-06-16 05:42:56 -04:00
/*
2021-11-29 19:24:27 -05:00
* The array_replace_recursive algorithm merges at the leaf level ,
* but we don ' t want leaf arrays to be merged , so we overwrite it .
*
* For leaf values that are sequential arrays it will use the numeric indexes for replacement .
* We rather replace the existing with the incoming value , if it exists .
* This is the case of spacing . units .
*
* For leaf values that are associative arrays it will merge them as expected .
* This is also not the behavior we want for the current associative arrays ( presets ) .
* We rather replace the existing with the incoming value , if it exists .
* This happens , for example , when we merge data from theme . json upon existing
* theme supports or when we merge anything coming from the same source twice .
* This is the case of color . palette , color . gradients , color . duotone ,
* typography . fontSizes , or typography . fontFamilies .
*
* Additionally , for some preset types , we also want to make sure the
* values they introduce don ' t conflict with default values . We do so
* by checking the incoming slugs for theme presets and compare them
2022-01-18 15:29:06 -05:00
* with the equivalent default presets : if a slug is present as a default
2021-11-29 19:24:27 -05:00
* we remove it from the theme presets .
2021-06-16 05:42:56 -04:00
*/
2022-02-17 04:04:05 -05:00
$nodes = static :: get_setting_nodes ( $incoming_data );
$slugs_global = static :: get_default_slugs ( $this -> theme_json , array ( 'settings' ) );
2021-11-29 19:24:27 -05:00
foreach ( $nodes as $node ) {
2022-02-17 04:04:05 -05:00
$slugs_node = static :: get_default_slugs ( $this -> theme_json , $node [ 'path' ] );
2021-11-29 19:24:27 -05:00
$slugs = array_merge_recursive ( $slugs_global , $slugs_node );
// Replace the spacing.units.
$path = array_merge ( $node [ 'path' ], array ( 'spacing' , 'units' ) );
$content = _wp_array_get ( $incoming_data , $path , null );
if ( isset ( $content ) ) {
_wp_array_set ( $this -> theme_json , $path , $content );
}
2021-05-24 04:37:55 -04:00
2021-11-29 19:24:27 -05:00
// Replace the presets.
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $preset ) {
2022-04-11 06:38:00 -04:00
$override_preset = ! static :: get_metadata_boolean ( $this -> theme_json [ 'settings' ], $preset [ 'prevent_override' ], true );
2021-12-13 20:57:26 -05:00
2022-02-17 04:04:05 -05:00
foreach ( static :: VALID_ORIGINS as $origin ) {
2021-12-21 01:02:06 -05:00
$base_path = array_merge ( $node [ 'path' ], $preset [ 'path' ] );
$path = array_merge ( $base_path , array ( $origin ) );
$content = _wp_array_get ( $incoming_data , $path , null );
2021-11-29 19:24:27 -05:00
if ( ! isset ( $content ) ) {
continue ;
}
2021-12-21 01:02:06 -05:00
if ( 'theme' === $origin && $preset [ 'use_default_names' ] ) {
foreach ( $content as & $item ) {
if ( ! array_key_exists ( 'name' , $item ) ) {
2022-02-17 04:04:05 -05:00
$name = static :: get_name_from_defaults ( $item [ 'slug' ], $base_path );
2021-12-21 01:02:06 -05:00
if ( null !== $name ) {
$item [ 'name' ] = $name ;
}
}
}
}
2021-11-29 19:24:27 -05:00
if (
( 'theme' !== $origin ) ||
2021-12-13 20:57:26 -05:00
( 'theme' === $origin && $override_preset )
2021-11-29 19:24:27 -05:00
) {
_wp_array_set ( $this -> theme_json , $path , $content );
2021-12-13 20:57:26 -05:00
} else {
$slugs_for_preset = _wp_array_get ( $slugs , $preset [ 'path' ], array () );
2022-02-17 04:04:05 -05:00
$content = static :: filter_slugs ( $content , $slugs_for_preset );
2021-11-29 19:24:27 -05:00
_wp_array_set ( $this -> theme_json , $path , $content );
}
2021-05-24 04:37:55 -04:00
}
}
}
2021-11-29 19:24:27 -05:00
}
2022-02-17 11:18:03 -05:00
/**
* Converts all filter ( duotone ) presets into SVGs .
*
* @ since 5.9 . 1
*
* @ param array $origins List of origins to process .
* @ return string SVG filters .
*/
public function get_svg_filters ( $origins ) {
$blocks_metadata = static :: get_blocks_metadata ();
$setting_nodes = static :: get_setting_nodes ( $this -> theme_json , $blocks_metadata );
2022-02-23 18:17:01 -05:00
$filters = '' ;
2022-02-17 11:18:03 -05:00
foreach ( $setting_nodes as $metadata ) {
$node = _wp_array_get ( $this -> theme_json , $metadata [ 'path' ], array () );
if ( empty ( $node [ 'color' ][ 'duotone' ] ) ) {
continue ;
}
$duotone_presets = $node [ 'color' ][ 'duotone' ];
foreach ( $origins as $origin ) {
if ( ! isset ( $duotone_presets [ $origin ] ) ) {
continue ;
}
foreach ( $duotone_presets [ $origin ] as $duotone_preset ) {
$filters .= wp_get_duotone_filter_svg ( $duotone_preset );
}
}
}
return $filters ;
}
2021-11-29 19:24:27 -05:00
/**
2022-02-08 11:11:03 -05:00
* Returns whether a presets should be overridden or not .
2021-12-13 20:57:26 -05:00
*
* @ since 5.9 . 0
2022-04-11 06:38:00 -04:00
* @ deprecated 6.0 . 0 Use { @ see 'get_metadata_boolean' } instead .
2021-12-13 20:57:26 -05:00
*
* @ param array $theme_json The theme . json like structure to inspect .
2022-04-11 06:38:00 -04:00
* @ param array $path Path to inspect .
* @ param bool | array $override Data to compute whether to override the preset .
2021-12-13 20:57:26 -05:00
* @ return boolean
*/
2022-02-17 04:04:05 -05:00
protected static function should_override_preset ( $theme_json , $path , $override ) {
2022-04-11 06:38:00 -04:00
_deprecated_function ( __METHOD__ , '6.0.0' , 'get_metadata_boolean' );
2021-12-13 20:57:26 -05:00
if ( is_bool ( $override ) ) {
return $override ;
}
/*
* The relationship between whether to override the defaults
* and whether the defaults are enabled is inverse :
*
2022-02-08 11:11:03 -05:00
* - If defaults are enabled => theme presets should not be overridden
* - If defaults are disabled => theme presets should be overridden
2021-12-13 20:57:26 -05:00
*
* For example , a theme sets defaultPalette to false ,
* making the default palette hidden from the user .
* In that case , we want all the theme presets to be present ,
* so they should override the defaults .
*/
if ( is_array ( $override ) ) {
$value = _wp_array_get ( $theme_json , array_merge ( $path , $override ) );
if ( isset ( $value ) ) {
return ! $value ;
}
// Search the top-level key if none was found for this node.
$value = _wp_array_get ( $theme_json , array_merge ( array ( 'settings' ), $override ) );
if ( isset ( $value ) ) {
return ! $value ;
}
return true ;
}
}
/**
* Returns the default slugs for all the presets in an associative array
2021-11-29 19:24:27 -05:00
* whose keys are the preset paths and the leafs is the list of slugs .
*
* For example :
*
2021-12-13 20:57:26 -05:00
* array (
2021-11-29 19:24:27 -05:00
* 'color' => array (
* 'palette' => array ( 'slug-1' , 'slug-2' ),
* 'gradients' => array ( 'slug-3' , 'slug-4' ),
* ),
* )
*
* @ since 5.9 . 0
*
2021-12-13 20:57:26 -05:00
* @ param array $data A theme . json like structure .
* @ param array $node_path The path to inspect . It 's ' settings ' by default .
* @ return array
2021-11-29 19:24:27 -05:00
*/
2022-02-17 04:04:05 -05:00
protected static function get_default_slugs ( $data , $node_path ) {
2021-11-29 19:24:27 -05:00
$slugs = array ();
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $metadata ) {
2021-12-13 20:57:26 -05:00
$path = array_merge ( $node_path , $metadata [ 'path' ], array ( 'default' ) );
$preset = _wp_array_get ( $data , $path , null );
2021-11-29 19:24:27 -05:00
if ( ! isset ( $preset ) ) {
continue ;
}
2021-12-13 20:57:26 -05:00
$slugs_for_preset = array ();
2021-11-29 19:24:27 -05:00
$slugs_for_preset = array_map (
2021-12-13 20:57:26 -05:00
static function ( $value ) {
2021-11-29 19:24:27 -05:00
return isset ( $value [ 'slug' ] ) ? $value [ 'slug' ] : null ;
},
$preset
);
_wp_array_set ( $slugs , $metadata [ 'path' ], $slugs_for_preset );
}
return $slugs ;
}
2021-12-21 01:02:06 -05:00
/**
* Get a `default` ' s preset name by a provided slug .
*
* @ since 5.9 . 0
*
* @ param string $slug The slug we want to find a match from default presets .
* @ param array $base_path The path to inspect . It 's ' settings ' by default .
* @ return string | null
*/
2022-02-17 04:04:05 -05:00
protected function get_name_from_defaults ( $slug , $base_path ) {
2021-12-21 01:02:06 -05:00
$path = array_merge ( $base_path , array ( 'default' ) );
$default_content = _wp_array_get ( $this -> theme_json , $path , null );
if ( ! $default_content ) {
return null ;
}
foreach ( $default_content as $item ) {
if ( $slug === $item [ 'slug' ] ) {
return $item [ 'name' ];
}
}
return null ;
}
2021-11-29 19:24:27 -05:00
/**
* Removes the preset values whose slug is equal to any of given slugs .
*
* @ since 5.9 . 0
*
2021-12-04 07:58:01 -05:00
* @ param array $node The node with the presets to validate .
2022-02-08 11:11:03 -05:00
* @ param array $slugs The slugs that should not be overridden .
2021-12-04 07:58:01 -05:00
* @ return array The new node .
2021-11-29 19:24:27 -05:00
*/
2022-02-17 04:04:05 -05:00
protected static function filter_slugs ( $node , $slugs ) {
2021-12-13 20:57:26 -05:00
if ( empty ( $slugs ) ) {
2021-11-29 19:24:27 -05:00
return $node ;
}
$new_node = array ();
foreach ( $node as $value ) {
2021-12-13 20:57:26 -05:00
if ( isset ( $value [ 'slug' ] ) && ! in_array ( $value [ 'slug' ], $slugs , true ) ) {
2021-11-29 19:24:27 -05:00
$new_node [] = $value ;
}
}
2021-11-08 14:19:58 -05:00
2021-11-29 19:24:27 -05:00
return $new_node ;
2021-11-08 14:19:58 -05:00
}
/**
* Removes insecure data from theme . json .
*
* @ since 5.9 . 0
*
* @ param array $theme_json Structure to sanitize .
* @ return array Sanitized structure .
*/
public static function remove_insecure_properties ( $theme_json ) {
$sanitized = array ();
$theme_json = WP_Theme_JSON_Schema :: migrate ( $theme_json );
2022-02-17 04:04:05 -05:00
$valid_block_names = array_keys ( static :: get_blocks_metadata () );
$valid_element_names = array_keys ( static :: ELEMENTS );
2022-09-10 08:38:12 -04:00
$theme_json = static :: sanitize ( $theme_json , $valid_block_names , $valid_element_names );
2021-11-08 14:19:58 -05:00
2022-02-17 04:04:05 -05:00
$blocks_metadata = static :: get_blocks_metadata ();
$style_nodes = static :: get_style_nodes ( $theme_json , $blocks_metadata );
2022-09-10 08:38:12 -04:00
2021-11-08 14:19:58 -05:00
foreach ( $style_nodes as $metadata ) {
$input = _wp_array_get ( $theme_json , $metadata [ 'path' ], array () );
if ( empty ( $input ) ) {
continue ;
}
2022-02-17 04:04:05 -05:00
$output = static :: remove_insecure_styles ( $input );
2022-09-10 08:38:12 -04:00
/*
* Get a reference to element name from path .
* $metadata [ 'path' ] = array ( 'styles' , 'elements' , 'link' );
*/
$current_element = $metadata [ 'path' ][ count ( $metadata [ 'path' ] ) - 1 ];
/*
* $output is stripped of pseudo selectors . Re - add and process them
* or insecure styles here .
*/
if ( array_key_exists ( $current_element , static :: VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static :: VALID_ELEMENT_PSEUDO_SELECTORS [ $current_element ] as $pseudo_selector ) {
if ( isset ( $input [ $pseudo_selector ] ) ) {
$output [ $pseudo_selector ] = static :: remove_insecure_styles ( $input [ $pseudo_selector ] );
}
}
}
2021-11-08 14:19:58 -05:00
if ( ! empty ( $output ) ) {
_wp_array_set ( $sanitized , $metadata [ 'path' ], $output );
}
}
2022-02-17 04:04:05 -05:00
$setting_nodes = static :: get_setting_nodes ( $theme_json );
2021-11-08 14:19:58 -05:00
foreach ( $setting_nodes as $metadata ) {
$input = _wp_array_get ( $theme_json , $metadata [ 'path' ], array () );
if ( empty ( $input ) ) {
continue ;
}
2022-02-17 04:04:05 -05:00
$output = static :: remove_insecure_settings ( $input );
2021-11-08 14:19:58 -05:00
if ( ! empty ( $output ) ) {
_wp_array_set ( $sanitized , $metadata [ 'path' ], $output );
}
}
if ( empty ( $sanitized [ 'styles' ] ) ) {
unset ( $theme_json [ 'styles' ] );
} else {
$theme_json [ 'styles' ] = $sanitized [ 'styles' ];
}
if ( empty ( $sanitized [ 'settings' ] ) ) {
unset ( $theme_json [ 'settings' ] );
} else {
$theme_json [ 'settings' ] = $sanitized [ 'settings' ];
}
return $theme_json ;
}
/**
* Processes a setting node and returns the same node
* without the insecure settings .
*
* @ since 5.9 . 0
*
* @ param array $input Node to process .
* @ return array
*/
2022-02-17 04:04:05 -05:00
protected static function remove_insecure_settings ( $input ) {
2021-11-08 14:19:58 -05:00
$output = array ();
2022-02-17 04:04:05 -05:00
foreach ( static :: PRESETS_METADATA as $preset_metadata ) {
foreach ( static :: VALID_ORIGINS as $origin ) {
2021-11-23 00:40:38 -05:00
$path_with_origin = array_merge ( $preset_metadata [ 'path' ], array ( $origin ) );
$presets = _wp_array_get ( $input , $path_with_origin , null );
if ( null === $presets ) {
continue ;
}
2021-11-08 14:19:58 -05:00
2021-11-23 00:40:38 -05:00
$escaped_preset = array ();
foreach ( $presets as $preset ) {
if (
esc_attr ( esc_html ( $preset [ 'name' ] ) ) === $preset [ 'name' ] &&
sanitize_html_class ( $preset [ 'slug' ] ) === $preset [ 'slug' ]
2021-11-08 14:19:58 -05:00
) {
2021-11-23 00:40:38 -05:00
$value = null ;
2022-02-17 13:47:02 -05:00
if ( isset ( $preset_metadata [ 'value_key' ], $preset [ $preset_metadata [ 'value_key' ] ] ) ) {
2021-11-23 00:40:38 -05:00
$value = $preset [ $preset_metadata [ 'value_key' ] ];
} elseif (
isset ( $preset_metadata [ 'value_func' ] ) &&
is_callable ( $preset_metadata [ 'value_func' ] )
) {
$value = call_user_func ( $preset_metadata [ 'value_func' ], $preset );
}
2021-11-08 14:19:58 -05:00
2021-11-23 00:40:38 -05:00
$preset_is_valid = true ;
foreach ( $preset_metadata [ 'properties' ] as $property ) {
2022-02-17 04:04:05 -05:00
if ( ! static :: is_safe_css_declaration ( $property , $value ) ) {
2021-11-23 00:40:38 -05:00
$preset_is_valid = false ;
break ;
}
2021-11-08 14:19:58 -05:00
}
2021-11-23 00:40:38 -05:00
if ( $preset_is_valid ) {
$escaped_preset [] = $preset ;
}
2021-11-08 14:19:58 -05:00
}
}
2021-11-23 00:40:38 -05:00
if ( ! empty ( $escaped_preset ) ) {
_wp_array_set ( $output , $path_with_origin , $escaped_preset );
}
2021-11-08 14:19:58 -05:00
}
}
return $output ;
}
/**
* Processes a style node and returns the same node
* without the insecure styles .
*
* @ since 5.9 . 0
*
* @ param array $input Node to process .
* @ return array
*/
2022-02-17 04:04:05 -05:00
protected static function remove_insecure_styles ( $input ) {
2021-11-08 14:19:58 -05:00
$output = array ();
2022-02-17 04:04:05 -05:00
$declarations = static :: compute_style_properties ( $input );
2021-11-08 14:19:58 -05:00
foreach ( $declarations as $declaration ) {
2022-02-17 04:04:05 -05:00
if ( static :: is_safe_css_declaration ( $declaration [ 'name' ], $declaration [ 'value' ] ) ) {
$path = static :: PROPERTIES_METADATA [ $declaration [ 'name' ] ];
2021-11-08 14:19:58 -05:00
// Check the value isn't an array before adding so as to not
// double up shorthand and longhand styles.
$value = _wp_array_get ( $input , $path , array () );
if ( ! is_array ( $value ) ) {
_wp_array_set ( $output , $path , $value );
}
}
}
return $output ;
}
/**
* Checks that a declaration provided by the user is safe .
*
* @ since 5.9 . 0
*
2021-12-04 07:58:01 -05:00
* @ param string $property_name Property name in a CSS declaration , i . e . the `color` in `color: red` .
2021-11-08 14:19:58 -05:00
* @ param string $property_value Value in a CSS declaration , i . e . the `red` in `color: red` .
2021-12-04 07:58:01 -05:00
* @ return bool
2021-11-08 14:19:58 -05:00
*/
2022-02-17 04:04:05 -05:00
protected static function is_safe_css_declaration ( $property_name , $property_value ) {
2021-11-08 14:19:58 -05:00
$style_to_validate = $property_name . ': ' . $property_value ;
$filtered = esc_html ( safecss_filter_attr ( $style_to_validate ) );
return ! empty ( trim ( $filtered ) );
2021-05-24 04:37:55 -04:00
}
/**
* Returns the raw data .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
*
2021-05-24 04:37:55 -04:00
* @ return array Raw data .
*/
public function get_raw_data () {
return $this -> theme_json ;
}
/**
* Transforms the given editor settings according the
* add_theme_support format to the theme . json format .
*
2021-05-24 09:25:56 -04:00
* @ since 5.8 . 0
2021-05-24 04:37:55 -04:00
*
2021-05-24 09:25:56 -04:00
* @ param array $settings Existing editor settings .
2021-05-24 04:37:55 -04:00
* @ return array Config that adheres to the theme . json schema .
*/
public static function get_from_editor_settings ( $settings ) {
$theme_settings = array (
2022-02-17 04:04:05 -05:00
'version' => static :: LATEST_SCHEMA ,
2021-05-24 04:37:55 -04:00
'settings' => array (),
);
// Deprecated theme supports.
if ( isset ( $settings [ 'disableCustomColors' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'color' ] ) ) {
$theme_settings [ 'settings' ][ 'color' ] = array ();
}
$theme_settings [ 'settings' ][ 'color' ][ 'custom' ] = ! $settings [ 'disableCustomColors' ];
}
if ( isset ( $settings [ 'disableCustomGradients' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'color' ] ) ) {
$theme_settings [ 'settings' ][ 'color' ] = array ();
}
$theme_settings [ 'settings' ][ 'color' ][ 'customGradient' ] = ! $settings [ 'disableCustomGradients' ];
}
if ( isset ( $settings [ 'disableCustomFontSizes' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'typography' ] ) ) {
$theme_settings [ 'settings' ][ 'typography' ] = array ();
}
$theme_settings [ 'settings' ][ 'typography' ][ 'customFontSize' ] = ! $settings [ 'disableCustomFontSizes' ];
}
if ( isset ( $settings [ 'enableCustomLineHeight' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'typography' ] ) ) {
$theme_settings [ 'settings' ][ 'typography' ] = array ();
}
2021-11-08 14:19:58 -05:00
$theme_settings [ 'settings' ][ 'typography' ][ 'lineHeight' ] = $settings [ 'enableCustomLineHeight' ];
2021-05-24 04:37:55 -04:00
}
if ( isset ( $settings [ 'enableCustomUnits' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'spacing' ] ) ) {
$theme_settings [ 'settings' ][ 'spacing' ] = array ();
}
$theme_settings [ 'settings' ][ 'spacing' ][ 'units' ] = ( true === $settings [ 'enableCustomUnits' ] ) ?
2021-07-15 14:55:29 -04:00
array ( 'px' , 'em' , 'rem' , 'vh' , 'vw' , '%' ) :
2021-05-24 04:37:55 -04:00
$settings [ 'enableCustomUnits' ];
}
if ( isset ( $settings [ 'colors' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'color' ] ) ) {
$theme_settings [ 'settings' ][ 'color' ] = array ();
}
$theme_settings [ 'settings' ][ 'color' ][ 'palette' ] = $settings [ 'colors' ];
}
if ( isset ( $settings [ 'gradients' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'color' ] ) ) {
$theme_settings [ 'settings' ][ 'color' ] = array ();
}
$theme_settings [ 'settings' ][ 'color' ][ 'gradients' ] = $settings [ 'gradients' ];
}
if ( isset ( $settings [ 'fontSizes' ] ) ) {
$font_sizes = $settings [ 'fontSizes' ];
// Back-compatibility for presets without units.
foreach ( $font_sizes as $key => $font_size ) {
if ( is_numeric ( $font_size [ 'size' ] ) ) {
$font_sizes [ $key ][ 'size' ] = $font_size [ 'size' ] . 'px' ;
}
}
if ( ! isset ( $theme_settings [ 'settings' ][ 'typography' ] ) ) {
$theme_settings [ 'settings' ][ 'typography' ] = array ();
}
$theme_settings [ 'settings' ][ 'typography' ][ 'fontSizes' ] = $font_sizes ;
}
if ( isset ( $settings [ 'enableCustomSpacing' ] ) ) {
if ( ! isset ( $theme_settings [ 'settings' ][ 'spacing' ] ) ) {
$theme_settings [ 'settings' ][ 'spacing' ] = array ();
}
2021-11-08 14:19:58 -05:00
$theme_settings [ 'settings' ][ 'spacing' ][ 'padding' ] = $settings [ 'enableCustomSpacing' ];
2021-05-24 04:37:55 -04:00
}
return $theme_settings ;
}
2022-04-11 06:38:00 -04:00
/**
* Returns the current theme ' s wanted patterns ( slugs ) to be
* registered from Pattern Directory .
*
* @ since 6.0 . 0
*
* @ return string []
*/
public function get_patterns () {
if ( isset ( $this -> theme_json [ 'patterns' ] ) && is_array ( $this -> theme_json [ 'patterns' ] ) ) {
return $this -> theme_json [ 'patterns' ];
}
return array ();
}
/**
* Returns a valid theme . json as provided by a theme .
*
* Unlike get_raw_data () this returns the presets flattened , as provided by a theme .
* This also uses appearanceTools instead of their opt - ins if all of them are true .
*
* @ since 6.0 . 0
*
* @ return array
*/
public function get_data () {
$output = $this -> theme_json ;
$nodes = static :: get_setting_nodes ( $output );
/**
* Flatten the theme & custom origins into a single one .
*
* For example , the following :
*
* {
* " settings " : {
* " color " : {
* " palette " : {
* " theme " : [ {} ],
* " custom " : [ {} ]
* }
* }
* }
* }
*
* will be converted to :
*
* {
* " settings " : {
* " color " : {
* " palette " : [ {} ]
* }
* }
* }
*/
foreach ( $nodes as $node ) {
foreach ( static :: PRESETS_METADATA as $preset_metadata ) {
$path = array_merge ( $node [ 'path' ], $preset_metadata [ 'path' ] );
$preset = _wp_array_get ( $output , $path , null );
if ( null === $preset ) {
continue ;
}
$items = array ();
if ( isset ( $preset [ 'theme' ] ) ) {
foreach ( $preset [ 'theme' ] as $item ) {
$slug = $item [ 'slug' ];
unset ( $item [ 'slug' ] );
$items [ $slug ] = $item ;
}
}
if ( isset ( $preset [ 'custom' ] ) ) {
foreach ( $preset [ 'custom' ] as $item ) {
$slug = $item [ 'slug' ];
unset ( $item [ 'slug' ] );
$items [ $slug ] = $item ;
}
}
$flattened_preset = array ();
foreach ( $items as $slug => $value ) {
$flattened_preset [] = array_merge ( array ( 'slug' => $slug ), $value );
}
_wp_array_set ( $output , $path , $flattened_preset );
}
}
// If all of the static::APPEARANCE_TOOLS_OPT_INS are true,
// this code unsets them and sets 'appearanceTools' instead.
foreach ( $nodes as $node ) {
$all_opt_ins_are_set = true ;
foreach ( static :: APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) {
$full_path = array_merge ( $node [ 'path' ], $opt_in_path );
// Use "unset prop" as a marker instead of "null" because
// "null" can be a valid value for some props (e.g. blockGap).
$opt_in_value = _wp_array_get ( $output , $full_path , 'unset prop' );
if ( 'unset prop' === $opt_in_value ) {
$all_opt_ins_are_set = false ;
break ;
}
}
if ( $all_opt_ins_are_set ) {
_wp_array_set ( $output , array_merge ( $node [ 'path' ], array ( 'appearanceTools' ) ), true );
foreach ( static :: APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) {
$full_path = array_merge ( $node [ 'path' ], $opt_in_path );
// Use "unset prop" as a marker instead of "null" because
// "null" can be a valid value for some props (e.g. blockGap).
$opt_in_value = _wp_array_get ( $output , $full_path , 'unset prop' );
if ( true !== $opt_in_value ) {
continue ;
}
// The following could be improved to be path independent.
// At the moment it relies on a couple of assumptions:
//
// - all opt-ins having a path of size 2.
// - there's two sources of settings: the top-level and the block-level.
if (
( 1 === count ( $node [ 'path' ] ) ) &&
( 'settings' === $node [ 'path' ][ 0 ] )
) {
// Top-level settings.
unset ( $output [ 'settings' ][ $opt_in_path [ 0 ] ][ $opt_in_path [ 1 ] ] );
if ( empty ( $output [ 'settings' ][ $opt_in_path [ 0 ] ] ) ) {
unset ( $output [ 'settings' ][ $opt_in_path [ 0 ] ] );
}
} elseif (
( 3 === count ( $node [ 'path' ] ) ) &&
( 'settings' === $node [ 'path' ][ 0 ] ) &&
( 'blocks' === $node [ 'path' ][ 1 ] )
) {
// Block-level settings.
$block_name = $node [ 'path' ][ 2 ];
unset ( $output [ 'settings' ][ 'blocks' ][ $block_name ][ $opt_in_path [ 0 ] ][ $opt_in_path [ 1 ] ] );
if ( empty ( $output [ 'settings' ][ 'blocks' ][ $block_name ][ $opt_in_path [ 0 ] ] ) ) {
unset ( $output [ 'settings' ][ 'blocks' ][ $block_name ][ $opt_in_path [ 0 ] ] );
}
}
}
}
}
wp_recursive_ksort ( $output );
return $output ;
}
2021-05-24 04:37:55 -04:00
}