Make WordPress Core


Ignore:
Timestamp:
11/08/2021 07:18:39 PM (3 years ago)
Author:
jorgefilipecosta
Message:

Update theme.json classes for WordPress 5.9.

This commit ports to core the changes to the classes that deal with theme.json code.

See #54336.
Props oandregal, spacedmonkey, noisysocks, hellofromtonya, youknowriad.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-theme-json.php

    r51657 r52049  
    1010/**
    1111 * Class that encapsulates the processing of structures that adhere to the theme.json spec.
     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 get_global_settings, get_global_styles, and get_global_stylesheet instead.
    1216 *
    1317 * @access private
     
    7175     * This contains the necessary metadata to process them:
    7276     *
    73      * - path          => where to find the preset within the settings section
    74      *
    75      * - value_key     => the key that represents the value
    76      *
    77      * - css_var_infix => infix to use in generating the CSS Custom Property. Example:
    78      *                   --wp--preset--<preset_infix>--<slug>: <preset_value>
    79      *
    80      * - classes      => array containing a structure with the classes to
    81      *                   generate for the presets. Each class should have
    82      *                   the class suffix and the property name. Example:
    83      *
    84      *                   .has-<slug>-<class_suffix> {
    85      *                       <property_name>: <preset_value>
    86      *                   }
    87      *
    88      * @since 5.8.0
     77     * - path       => where to find the preset within the settings section
     78     * - value_key  => the key that represents the value
     79     * - value_func => the callback to render the value (either value_key or value_func should be present)
     80     * - css_vars   => template string to use in generating the CSS Custom Property.
     81     *                 Example output: "--wp--preset--duotone--blue: <value>" will generate as many CSS Custom Properties as presets defined
     82     *                 substituting the $slug for the slug's value for each preset value.
     83     * - classes    => array containing a structure with the classes to generate for the presets.
     84     *                 Each key is a template string to resolve similarly to the css_vars and each value is the CSS property to use for that class.
     85     *                 Example output: ".has-blue-color { color: <value> }"
     86     * - properties  => a list of CSS properties to be used by kses to check the preset value is safe.
     87     *
     88     * @since 5.8.0
     89     * @since 5.9.0 Added new presets and simplified the metadata structure.
    8990     * @var array
    9091     */
    9192    const PRESETS_METADATA = array(
    9293        array(
    93             'path'          => array( 'color', 'palette' ),
    94             'value_key'     => 'color',
    95             'css_var_infix' => 'color',
    96             'classes'       => array(
    97                 array(
    98                     'class_suffix'  => 'color',
    99                     'property_name' => 'color',
    100                 ),
    101                 array(
    102                     'class_suffix'  => 'background-color',
    103                     'property_name' => 'background-color',
    104                 ),
     94            'path'       => array( 'color', 'palette' ),
     95            'value_key'  => 'color',
     96            'css_vars'   => '--wp--preset--color--$slug',
     97            'classes'    => array(
     98                '.has-$slug-color'            => 'color',
     99                '.has-$slug-background-color' => 'background-color',
     100                '.has-$slug-border-color'     => 'border-color',
    105101            ),
     102            'properties' => array( 'color', 'background-color', 'border-color' ),
    106103        ),
    107104        array(
    108             'path'          => array( 'color', 'gradients' ),
    109             'value_key'     => 'gradient',
    110             'css_var_infix' => 'gradient',
    111             'classes'       => array(
    112                 array(
    113                     'class_suffix'  => 'gradient-background',
    114                     'property_name' => 'background',
    115                 ),
    116             ),
     105            'path'       => array( 'color', 'gradients' ),
     106            'value_key'  => 'gradient',
     107            'css_vars'   => '--wp--preset--gradient--$slug',
     108            'classes'    => array( '.has-$slug-gradient-background' => 'background' ),
     109            'properties' => array( 'background' ),
    117110        ),
    118111        array(
    119             'path'          => array( 'typography', 'fontSizes' ),
    120             'value_key'     => 'size',
    121             'css_var_infix' => 'font-size',
    122             'classes'       => array(
    123                 array(
    124                     'class_suffix'  => 'font-size',
    125                     'property_name' => 'font-size',
    126                 ),
    127             ),
     112            'path'       => array( 'color', 'duotone' ),
     113            'value_func' => 'wp_render_duotone_filter_preset',
     114            'css_vars'   => '--wp--preset--duotone--$slug',
     115            'classes'    => array(),
     116            'properties' => array( 'filter' ),
     117        ),
     118        array(
     119            'path'       => array( 'typography', 'fontSizes' ),
     120            'value_key'  => 'size',
     121            'css_vars'   => '--wp--preset--font-size--$slug',
     122            'classes'    => array( '.has-$slug-font-size' => 'font-size' ),
     123            'properties' => array( 'font-size' ),
     124        ),
     125        array(
     126            'path'       => array( 'typography', 'fontFamilies' ),
     127            'value_key'  => 'fontFamily',
     128            'css_vars'   => '--wp--preset--font-family--$slug',
     129            'classes'    => array( '.has-$slug-font-family' => 'font-family' ),
     130            'properties' => array( 'font-family' ),
    128131        ),
    129132    );
     
    132135     * Metadata for style properties.
    133136     *
    134      * Each property declares:
    135      *
    136      * - 'value': path to the value in theme.json and block attributes.
    137      *
    138      * @since 5.8.0
     137     * Each element is a direct mapping from the CSS property name to the
     138     * path to the value in theme.json & block attributes.
     139     *
     140     * @since 5.8.0
     141     * @since 5.9.0 Added new properties and simplified the metadata structure.
    139142     * @var array
    140143     */
    141144    const PROPERTIES_METADATA = array(
    142         'background'       => array(
    143             'value' => array( 'color', 'gradient' ),
    144         ),
    145         'background-color' => array(
    146             'value' => array( 'color', 'background' ),
    147         ),
    148         'color'            => array(
    149             'value' => array( 'color', 'text' ),
    150         ),
    151         'font-size'        => array(
    152             'value' => array( 'typography', 'fontSize' ),
    153         ),
    154         'line-height'      => array(
    155             'value' => array( 'typography', 'lineHeight' ),
    156         ),
    157         'margin'           => array(
    158             'value'      => array( 'spacing', 'margin' ),
    159             'properties' => array( 'top', 'right', 'bottom', 'left' ),
    160         ),
    161         'padding'          => array(
    162             'value'      => array( 'spacing', 'padding' ),
    163             'properties' => array( 'top', 'right', 'bottom', 'left' ),
    164         ),
     145        'background'                 => array( 'color', 'gradient' ),
     146        'background-color'           => array( 'color', 'background' ),
     147        'border-radius'              => array( 'border', 'radius' ),
     148        'border-top-left-radius'     => array( 'border', 'radius', 'topLeft' ),
     149        'border-top-right-radius'    => array( 'border', 'radius', 'topRight' ),
     150        'border-bottom-left-radius'  => array( 'border', 'radius', 'bottomLeft' ),
     151        'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
     152        'border-color'               => array( 'border', 'color' ),
     153        'border-width'               => array( 'border', 'width' ),
     154        'border-style'               => array( 'border', 'style' ),
     155        'color'                      => array( 'color', 'text' ),
     156        'font-family'                => array( 'typography', 'fontFamily' ),
     157        'font-size'                  => array( 'typography', 'fontSize' ),
     158        'font-style'                 => array( 'typography', 'fontStyle' ),
     159        'font-weight'                => array( 'typography', 'fontWeight' ),
     160        'letter-spacing'             => array( 'typography', 'letterSpacing' ),
     161        'line-height'                => array( 'typography', 'lineHeight' ),
     162        'margin'                     => array( 'spacing', 'margin' ),
     163        'margin-top'                 => array( 'spacing', 'margin', 'top' ),
     164        'margin-right'               => array( 'spacing', 'margin', 'right' ),
     165        'margin-bottom'              => array( 'spacing', 'margin', 'bottom' ),
     166        'margin-left'                => array( 'spacing', 'margin', 'left' ),
     167        'padding'                    => array( 'spacing', 'padding' ),
     168        'padding-top'                => array( 'spacing', 'padding', 'top' ),
     169        'padding-right'              => array( 'spacing', 'padding', 'right' ),
     170        'padding-bottom'             => array( 'spacing', 'padding', 'bottom' ),
     171        'padding-left'               => array( 'spacing', 'padding', 'left' ),
     172        '--wp--style--block-gap'     => array( 'spacing', 'blockGap' ),
     173        'text-decoration'            => array( 'typography', 'textDecoration' ),
     174        'text-transform'             => array( 'typography', 'textTransform' ),
     175        'filter'                     => array( 'filter', 'duotone' ),
    165176    );
    166177
    167178    /**
    168      * @since 5.8.0
     179     * Protected style properties.
     180     *
     181     * These style properties are only rendered if a setting enables it
     182     * via a value other than `null`.
     183     *
     184     * Each element maps the style property to the corresponding theme.json
     185     * setting key.
     186     *
     187     * @since 5.9.0
     188     */
     189    const PROTECTED_PROPERTIES = array(
     190        'spacing.blockGap' => array( 'spacing', 'blockGap' ),
     191    );
     192
     193    /**
     194     * The top-level keys a theme.json can have.
     195     *
     196     * @since 5.8.0
     197     * @since 5.9.0 Renamed from ALLOWED_TOP_LEVEL_KEYS and added new values.
    169198     * @var string[]
    170199     */
    171     const ALLOWED_TOP_LEVEL_KEYS = array(
     200    const VALID_TOP_LEVEL_KEYS = array(
     201        'customTemplates',
    172202        'settings',
    173203        'styles',
     204        'templateParts',
    174205        'version',
    175206    );
    176207
    177208    /**
    178      * @since 5.8.0
     209     * The valid properties under the settings key.
     210     *
     211     * @since 5.8.0
     212     * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties, and renamed others according to the new schema.
    179213     * @var array
    180214     */
    181     const ALLOWED_SETTINGS = array(
     215    const VALID_SETTINGS = array(
    182216        'border'     => array(
    183             'customRadius' => null,
     217            'color'  => null,
     218            'radius' => null,
     219            'style'  => null,
     220            'width'  => null,
    184221        ),
    185222        'color'      => array(
     223            'background'     => null,
    186224            'custom'         => null,
    187225            'customDuotone'  => null,
     
    191229            'link'           => null,
    192230            'palette'        => null,
     231            'text'           => null,
    193232        ),
    194233        'custom'     => null,
     
    198237        ),
    199238        'spacing'    => array(
    200             'customMargin'  => null,
    201             'customPadding' => null,
    202             'units'         => null,
     239            'blockGap' => null,
     240            'margin'   => null,
     241            'padding'  => null,
     242            'units'    => null,
    203243        ),
    204244        'typography' => array(
    205             'customFontSize'   => null,
    206             'customLineHeight' => null,
    207             'dropCap'          => null,
    208             'fontSizes'        => null,
     245            'customFontSize' => null,
     246            'dropCap'        => null,
     247            'fontFamilies'   => null,
     248            'fontSizes'      => null,
     249            'fontStyle'      => null,
     250            'fontWeight'     => null,
     251            'letterSpacing'  => null,
     252            'lineHeight'     => null,
     253            'textDecoration' => null,
     254            'textTransform'  => null,
    209255        ),
    210256    );
    211257
    212258    /**
    213      * @since 5.8.0
     259     * The valid properties under the styles key.
     260     *
     261     * @since 5.8.0
     262     * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties.
    214263     * @var array
    215264     */
    216     const ALLOWED_STYLES = array(
     265    const VALID_STYLES = array(
    217266        'border'     => array(
     267            'color'  => null,
    218268            'radius' => null,
     269            'style'  => null,
     270            'width'  => null,
    219271        ),
    220272        'color'      => array(
     
    223275            'text'       => null,
    224276        ),
     277        'filter'     => array(
     278            'duotone' => null,
     279        ),
    225280        'spacing'    => array(
    226             'margin'  => array(
    227                 'top'    => null,
    228                 'right'  => null,
    229                 'bottom' => null,
    230                 'left'   => null,
    231             ),
    232             'padding' => array(
    233                 'bottom' => null,
    234                 'left'   => null,
    235                 'right'  => null,
    236                 'top'    => null,
    237             ),
     281            'margin'   => null,
     282            'padding'  => null,
     283            'blockGap' => null,
    238284        ),
    239285        'typography' => array(
    240             'fontSize'   => null,
    241             'lineHeight' => null,
     286            'fontFamily'     => null,
     287            'fontSize'       => null,
     288            'fontStyle'      => null,
     289            'fontWeight'     => null,
     290            'letterSpacing'  => null,
     291            'lineHeight'     => null,
     292            'textDecoration' => null,
     293            'textTransform'  => null,
    242294        ),
    243295    );
     
    258310
    259311    /**
    260      * @since 5.8.0
     312     * The latest version of the schema in use.
     313     *
     314     * @since 5.8.0
     315     * @since 5.9.0 Changed value.
    261316     * @var int
    262317     */
    263     const LATEST_SCHEMA = 1;
     318    const LATEST_SCHEMA = 2;
    264319
    265320    /**
     
    277332        }
    278333
    279         if ( ! isset( $theme_json['version'] ) || self::LATEST_SCHEMA !== $theme_json['version'] ) {
    280             $this->theme_json = array();
    281             return;
    282         }
    283 
    284         $this->theme_json = self::sanitize( $theme_json );
     334        $this->theme_json    = WP_Theme_JSON_Schema::migrate( $theme_json );
     335        $valid_block_names   = array_keys( self::get_blocks_metadata() );
     336        $valid_element_names = array_keys( self::ELEMENTS );
     337        $this->theme_json    = self::sanitize( $this->theme_json, $valid_block_names, $valid_element_names );
    285338
    286339        // Internally, presets are keyed by origin.
    287340        $nodes = self::get_setting_nodes( $this->theme_json );
    288341        foreach ( $nodes as $node ) {
    289             foreach ( self::PRESETS_METADATA as $preset ) {
    290                 $path   = array_merge( $node['path'], $preset['path'] );
     342            foreach ( self::PRESETS_METADATA as $preset_metadata ) {
     343                $path   = array_merge( $node['path'], $preset_metadata['path'] );
    291344                $preset = _wp_array_get( $this->theme_json, $path, null );
    292345                if ( null !== $preset ) {
     
    301354     *
    302355     * @since 5.8.0
     356     * @since 5.9.0 Has new parameters.
    303357     *
    304358     * @param array $input Structure to sanitize.
     359     * @param array $valid_block_names List of valid block names.
     360     * @param array $valid_element_names List of valid element names.
    305361     * @return array The sanitized output.
    306362     */
    307     private static function sanitize( $input ) {
     363    private static function sanitize( $input, $valid_block_names, $valid_element_names ) {
    308364        $output = array();
    309365
     
    312368        }
    313369
    314         $allowed_top_level_keys = self::ALLOWED_TOP_LEVEL_KEYS;
    315         $allowed_settings       = self::ALLOWED_SETTINGS;
    316         $allowed_styles         = self::ALLOWED_STYLES;
    317         $allowed_blocks         = array_keys( self::get_blocks_metadata() );
    318         $allowed_elements       = array_keys( self::ELEMENTS );
    319 
    320         $output = array_intersect_key( $input, array_flip( $allowed_top_level_keys ) );
    321 
    322         // Build the schema.
     370        $output = array_intersect_key( $input, array_flip( self::VALID_TOP_LEVEL_KEYS ) );
     371
     372        // Build the schema based on valid block & element names.
    323373        $schema                 = array();
    324374        $schema_styles_elements = array();
    325         foreach ( $allowed_elements as $element ) {
    326             $schema_styles_elements[ $element ] = $allowed_styles;
     375        foreach ( $valid_element_names as $element ) {
     376            $schema_styles_elements[ $element ] = self::VALID_STYLES;
    327377        }
    328378        $schema_styles_blocks   = array();
    329379        $schema_settings_blocks = array();
    330         foreach ( $allowed_blocks as $block ) {
    331             $schema_settings_blocks[ $block ]           = $allowed_settings;
    332             $schema_styles_blocks[ $block ]             = $allowed_styles;
     380        foreach ( $valid_block_names as $block ) {
     381            $schema_settings_blocks[ $block ]           = self::VALID_SETTINGS;
     382            $schema_styles_blocks[ $block ]             = self::VALID_STYLES;
    333383            $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
    334384        }
    335         $schema['styles']             = $allowed_styles;
     385        $schema['styles']             = self::VALID_STYLES;
    336386        $schema['styles']['blocks']   = $schema_styles_blocks;
    337387        $schema['styles']['elements'] = $schema_styles_elements;
    338         $schema['settings']           = $allowed_settings;
     388        $schema['settings']           = self::VALID_SETTINGS;
    339389        $schema['settings']['blocks'] = $schema_settings_blocks;
    340390
     
    361411        return $output;
    362412    }
    363 
    364413    /**
    365414     * Returns the metadata for each block.
     
    378427     *         'selector': 'h1',
    379428     *         'elements': {}
    380      *       }
    381      *       'core/group': {
    382      *         'selector': '.wp-block-group',
     429     *       },
     430     *       'core/image': {
     431     *         'selector': '.wp-block-image',
     432     *         'duotone': 'img',
    383433     *         'elements': {}
    384434     *       }
     
    386436     *
    387437     * @since 5.8.0
     438     * @since 5.9.0 Added duotone key with CSS selector.
    388439     *
    389440     * @return array Block metadata.
     
    408459            }
    409460
    410             /*
    411              * Assign defaults, then overwrite those that the block sets by itself.
    412              * If the block selector is compounded, will append the element to each
    413              * individual block selector.
    414              */
     461            if (
     462                isset( $block_type->supports['color']['__experimentalDuotone'] ) &&
     463                is_string( $block_type->supports['color']['__experimentalDuotone'] )
     464            ) {
     465                self::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
     466            }
     467
     468            // Assign defaults, then overwrite those that the block sets by itself.
     469            // If the block selector is compounded, will append the element to each
     470            // individual block selector.
    415471            $block_selectors = explode( ',', self::$blocks_metadata[ $block_name ]['selector'] );
    416472            foreach ( self::ELEMENTS as $el_name => $el_selector ) {
     
    494550     *
    495551     * @since 5.8.0
    496      *
    497      * @param string $type Optional. Type of stylesheet we want. Accepts 'all',
    498      *                     'block_styles', and 'css_variables'. Default 'all'.
     552     * @since 5.9.0 Changed the arguments passed to the function.
     553     *
     554     * @param array $types    Types of styles to load. Will load all by default. It accepts:
     555     *                         'variables': only the CSS Custom Properties for presets & custom ones.
     556     *                         'styles': only the styles section in theme.json.
     557     *                         'presets': only the classes for the presets.
     558     * @param array $origins A list of origins to include. By default it includes self::VALID_ORIGINS.
    499559     * @return string Stylesheet.
    500560     */
    501     public function get_stylesheet( $type = 'all' ) {
     561    public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = self::VALID_ORIGINS ) {
     562        if ( is_string( $types ) ) {
     563            // Dispatch error and map old arguments to new ones.
     564            _deprecated_argument( __FUNCTION__, '5.9' );
     565            if ( 'block_styles' === $types ) {
     566                $types = array( 'styles', 'presets' );
     567            } elseif ( 'css_variables' === $types ) {
     568                $types = array( 'variables' );
     569            } else {
     570                $types = array( 'variables', 'styles', 'presets' );
     571            }
     572        }
     573
    502574        $blocks_metadata = self::get_blocks_metadata();
    503575        $style_nodes     = self::get_style_nodes( $this->theme_json, $blocks_metadata );
    504576        $setting_nodes   = self::get_setting_nodes( $this->theme_json, $blocks_metadata );
    505577
    506         switch ( $type ) {
    507             case 'block_styles':
    508                 return $this->get_block_styles( $style_nodes, $setting_nodes );
    509             case 'css_variables':
    510                 return $this->get_css_variables( $setting_nodes );
    511             default:
    512                 return $this->get_css_variables( $setting_nodes ) . $this->get_block_styles( $style_nodes, $setting_nodes );
    513         }
    514 
     578        $stylesheet = '';
     579
     580        if ( in_array( 'variables', $types, true ) ) {
     581            $stylesheet .= $this->get_css_variables( $setting_nodes, $origins );
     582        }
     583
     584        if ( in_array( 'styles', $types, true ) ) {
     585            $stylesheet .= $this->get_block_classes( $style_nodes );
     586        }
     587
     588        if ( in_array( 'presets', $types, true ) ) {
     589            $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins );
     590        }
     591
     592        return $stylesheet;
     593    }
     594
     595    /**
     596     * Returns the page templates of the current theme.
     597     *
     598     * @since 5.9.0
     599     *
     600     * @return array
     601     */
     602    public function get_custom_templates() {
     603        $custom_templates = array();
     604        if ( ! isset( $this->theme_json['customTemplates'] ) ) {
     605            return $custom_templates;
     606        }
     607
     608        foreach ( $this->theme_json['customTemplates'] as $item ) {
     609            if ( isset( $item['name'] ) ) {
     610                $custom_templates[ $item['name'] ] = array(
     611                    'title'     => isset( $item['title'] ) ? $item['title'] : '',
     612                    'postTypes' => isset( $item['postTypes'] ) ? $item['postTypes'] : array( 'page' ),
     613                );
     614            }
     615        }
     616        return $custom_templates;
     617    }
     618
     619    /**
     620     * Returns the template part data of current theme.
     621     *
     622     * @since 5.9.0
     623     *
     624     * @return array
     625     */
     626    public function get_template_parts() {
     627        $template_parts = array();
     628        if ( ! isset( $this->theme_json['templateParts'] ) ) {
     629            return $template_parts;
     630        }
     631
     632        foreach ( $this->theme_json['templateParts'] as $item ) {
     633            if ( isset( $item['name'] ) ) {
     634                $template_parts[ $item['name'] ] = array(
     635                    'title' => isset( $item['title'] ) ? $item['title'] : '',
     636                    'area'  => isset( $item['area'] ) ? $item['area'] : '',
     637                );
     638            }
     639        }
     640        return $template_parts;
    515641    }
    516642
     
    527653     *   }
    528654     *
    529      * Additionally, it'll also create new rulesets
    530      * as classes for each preset value such as:
    531      *
    532      *     .has-value-color {
    533      *       color: value;
    534      *     }
    535      *
    536      *     .has-value-background-color {
    537      *       background-color: value;
    538      *     }
    539      *
    540      *     .has-value-font-size {
    541      *       font-size: value;
    542      *     }
    543      *
    544      *     .has-value-gradient-background {
    545      *       background: value;
    546      *     }
    547      *
    548      *     p.has-value-gradient-background {
    549      *       background: value;
    550      *     }
    551      *
    552      * @since 5.8.0
    553      *
    554      * @param array $style_nodes   Nodes with styles.
    555      * @param array $setting_nodes Nodes with settings.
     655     * @since 5.8.0
     656     * @since 5.9.0 Renamed to get_block_classes and no longer returns preset classes.
     657     *
     658     * @param array $style_nodes Nodes with styles.
    556659     * @return string The new stylesheet.
    557660     */
    558     private function get_block_styles( $style_nodes, $setting_nodes ) {
     661    private function get_block_classes( $style_nodes ) {
    559662        $block_rules = '';
     663
    560664        foreach ( $style_nodes as $metadata ) {
    561665            if ( null === $metadata['selector'] ) {
     
    565669            $node         = _wp_array_get( $this->theme_json, $metadata['path'], array() );
    566670            $selector     = $metadata['selector'];
    567             $declarations = self::compute_style_properties( $node );
     671            $settings     = _wp_array_get( $this->theme_json, array( 'settings' ) );
     672            $declarations = self::compute_style_properties( $node, $settings );
     673
     674            // 1. Separate the ones who use the general selector
     675            // and the ones who use the duotone selector.
     676            $declarations_duotone = array();
     677            foreach ( $declarations as $index => $declaration ) {
     678                if ( 'filter' === $declaration['name'] ) {
     679                    unset( $declarations[ $index ] );
     680                    $declarations_duotone[] = $declaration;
     681                }
     682            }
     683
     684            // 2. Generate the rules that use the general selector.
    568685            $block_rules .= self::to_ruleset( $selector, $declarations );
    569         }
    570 
     686
     687            // 3. Generate the rules that use the duotone selector.
     688            if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
     689                $selector_duotone = self::scope_selector( $metadata['selector'], $metadata['duotone'] );
     690                $block_rules     .= self::to_ruleset( $selector_duotone, $declarations_duotone );
     691            }
     692
     693            if ( self::ROOT_BLOCK_SELECTOR === $selector ) {
     694                $block_rules .= 'body { margin: 0; }';
     695                $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
     696                $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
     697                $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
     698
     699                $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     700                if ( $has_block_gap_support ) {
     701                    $block_rules .= '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }';
     702                    $block_rules .= '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }';
     703                }
     704            }
     705        }
     706
     707        return $block_rules;
     708    }
     709
     710    /**
     711     * Creates new rulesets as classes for each preset value such as:
     712     *
     713     *   .has-value-color {
     714     *     color: value;
     715     *   }
     716     *
     717     *   .has-value-background-color {
     718     *     background-color: value;
     719     *   }
     720     *
     721     *   .has-value-font-size {
     722     *     font-size: value;
     723     *   }
     724     *
     725     *   .has-value-gradient-background {
     726     *     background: value;
     727     *   }
     728     *
     729     *   p.has-value-gradient-background {
     730     *     background: value;
     731     *   }
     732     *
     733     * @since 5.9.0
     734     *
     735     * @param array $setting_nodes Nodes with settings.
     736     * @param array $origins       List of origins to process presets from.
     737     * @return string The new stylesheet.
     738     */
     739    private function get_preset_classes( $setting_nodes, $origins ) {
    571740        $preset_rules = '';
     741
    572742        foreach ( $setting_nodes as $metadata ) {
    573743            if ( null === $metadata['selector'] ) {
     
    577747            $selector      = $metadata['selector'];
    578748            $node          = _wp_array_get( $this->theme_json, $metadata['path'], array() );
    579             $preset_rules .= self::compute_preset_classes( $node, $selector );
    580         }
    581 
    582         return $block_rules . $preset_rules;
     749            $preset_rules .= self::compute_preset_classes( $node, $selector, $origins );
     750        }
     751
     752        return $preset_rules;
    583753    }
    584754
     
    598768     *
    599769     * @since 5.8.0
     770     * @since 5.9.0 Added origins parameter.
    600771     *
    601772     * @param array $nodes Nodes with settings.
     773     * @param array $origins List of origins to process.
    602774     * @return string The new stylesheet.
    603775     */
    604     private function get_css_variables( $nodes ) {
     776    private function get_css_variables( $nodes, $origins ) {
    605777        $stylesheet = '';
    606778        foreach ( $nodes as $metadata ) {
     
    612784
    613785            $node         = _wp_array_get( $this->theme_json, $metadata['path'], array() );
    614             $declarations = array_merge( self::compute_preset_vars( $node ), self::compute_theme_vars( $node ) );
     786            $declarations = array_merge( self::compute_preset_vars( $node, $origins ), self::compute_theme_vars( $node ) );
    615787
    616788            $stylesheet .= self::to_ruleset( $selector, $declarations );
     
    669841
    670842    /**
    671      * Given an array of presets keyed by origin and the value key of the preset,
    672      * it returns an array where each key is the preset slug and each value the preset value.
    673      *
    674      * @since 5.8.0
    675      *
    676      * @param array  $preset_per_origin Array of presets keyed by origin.
    677      * @param string $value_key         The property of the preset that contains its value.
    678      * @return array Array of presets where each key is a slug and each value is the preset value.
    679      */
    680     private static function get_merged_preset_by_slug( $preset_per_origin, $value_key ) {
    681         $result = array();
    682         foreach ( self::VALID_ORIGINS as $origin ) {
    683             if ( ! isset( $preset_per_origin[ $origin ] ) ) {
    684                 continue;
    685             }
    686             foreach ( $preset_per_origin[ $origin ] as $preset ) {
    687                 /*
    688                  * We don't want to use kebabCase here,
    689                  * see https://github.com/WordPress/gutenberg/issues/32347
    690                  * However, we need to make sure the generated class or CSS variable
    691                  * doesn't contain spaces.
    692                  */
    693                 $result[ preg_replace( '/\s+/', '-', $preset['slug'] ) ] = $preset[ $value_key ];
    694             }
    695         }
    696         return $result;
    697     }
    698 
    699     /**
    700843     * Given a settings array, it returns the generated rulesets
    701844     * for the preset classes.
    702845     *
    703846     * @since 5.8.0
     847     * @since 5.9.0 Added origins parameter.
    704848     *
    705849     * @param array  $settings Settings to process.
    706850     * @param string $selector Selector wrapping the classes.
     851     * @param array  $origins  List of origins to process.
    707852     * @return string The result of processing the presets.
    708853     */
    709     private static function compute_preset_classes( $settings, $selector ) {
     854    private static function compute_preset_classes( $settings, $selector, $origins ) {
    710855        if ( self::ROOT_BLOCK_SELECTOR === $selector ) {
    711856            // Classes at the global level do not need any CSS prefixed,
     
    715860
    716861        $stylesheet = '';
    717         foreach ( self::PRESETS_METADATA as $preset ) {
    718             $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() );
    719             $preset_by_slug    = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] );
    720             foreach ( $preset['classes'] as $class ) {
    721                 foreach ( $preset_by_slug as $slug => $value ) {
     862        foreach ( self::PRESETS_METADATA as $preset_metadata ) {
     863            $slugs = self::get_settings_slugs( $settings, $preset_metadata, $origins );
     864            foreach ( $preset_metadata['classes'] as $class => $property ) {
     865                foreach ( $slugs as $slug ) {
     866                    $css_var     = self::replace_slug_in_string( $preset_metadata['css_vars'], $slug );
     867                    $class_name  = self::replace_slug_in_string( $class, $slug );
    722868                    $stylesheet .= self::to_ruleset(
    723                         self::append_to_selector( $selector, '.has-' . _wp_to_kebab_case( $slug ) . '-' . $class['class_suffix'] ),
     869                        self::append_to_selector( $selector, $class_name ),
    724870                        array(
    725871                            array(
    726                                 'name'  => $class['property_name'],
    727                                 'value' => 'var(--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ) . ') !important',
     872                                'name'  => $property,
     873                                'value' => 'var(' . $css_var . ') !important',
    728874                            ),
    729875                        )
     
    737883
    738884    /**
     885     * Function that scopes a selector with another one. This works a bit like
     886     * SCSS nesting except the `&` operator isn't supported.
     887     *
     888     * <code>
     889     * $scope = '.a, .b .c';
     890     * $selector = '> .x, .y';
     891     * $merged = scope_selector( $scope, $selector );
     892     * // $merged is '.a > .x, .a .y, .b .c > .x, .b .c .y'
     893     * </code>
     894     *
     895     * @since 5.9.0
     896     *
     897     * @param string $scope    Selector to scope to.
     898     * @param string $selector Original selector.
     899     *
     900     * @return string Scoped selector.
     901     */
     902    private static function scope_selector( $scope, $selector ) {
     903        $scopes    = explode( ',', $scope );
     904        $selectors = explode( ',', $selector );
     905
     906        $selectors_scoped = array();
     907        foreach ( $scopes as $outer ) {
     908            foreach ( $selectors as $inner ) {
     909                $selectors_scoped[] = trim( $outer ) . ' ' . trim( $inner );
     910            }
     911        }
     912
     913        return implode( ', ', $selectors_scoped );
     914    }
     915
     916    /**
     917     * Gets preset values keyed by slugs based on settings and metadata.
     918     *
     919     * <code>
     920     * $settings = array(
     921     *     'typography' => array(
     922     *         'fontFamilies' => array(
     923     *             array(
     924     *                 'slug'       => 'sansSerif',
     925     *                 'fontFamily' => '"Helvetica Neue", sans-serif',
     926     *             ),
     927     *             array(
     928     *                 'slug'   => 'serif',
     929     *                 'colors' => 'Georgia, serif',
     930     *             )
     931     *         ),
     932     *     ),
     933     * );
     934     * $meta = array(
     935     *    'path'      => array( 'typography', 'fontFamilies' ),
     936     *    'value_key' => 'fontFamily',
     937     * );
     938     * $values_by_slug = get_settings_values_by_slug();
     939     * // $values_by_slug === array(
     940     * //   'sans-serif' => '"Helvetica Neue", sans-serif',
     941     * //   'serif'      => 'Georgia, serif',
     942     * // );
     943     * </code>
     944     *
     945     * @since 5.9.0
     946     *
     947     * @param array $settings Settings to process.
     948     * @param array $preset_metadata One of the PRESETS_METADATA values.
     949     * @param array $origins List of origins to process.
     950     * @return array Array of presets where each key is a slug and each value is the preset value.
     951     */
     952    private static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) {
     953        $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
     954
     955        $result = array();
     956        foreach ( $origins as $origin ) {
     957            if ( ! isset( $preset_per_origin[ $origin ] ) ) {
     958                continue;
     959            }
     960            foreach ( $preset_per_origin[ $origin ] as $preset ) {
     961                $slug = _wp_to_kebab_case( $preset['slug'] );
     962
     963                $value = '';
     964                if ( isset( $preset_metadata['value_key'] ) ) {
     965                    $value_key = $preset_metadata['value_key'];
     966                    $value     = $preset[ $value_key ];
     967                } elseif (
     968                    isset( $preset_metadata['value_func'] ) &&
     969                    is_callable( $preset_metadata['value_func'] )
     970                ) {
     971                    $value_func = $preset_metadata['value_func'];
     972                    $value      = call_user_func( $value_func, $preset );
     973                } else {
     974                    // If we don't have a value, then don't add it to the result.
     975                    continue;
     976                }
     977
     978                $result[ $slug ] = $value;
     979            }
     980        }
     981        return $result;
     982    }
     983
     984    /**
     985     * Similar to get_settings_values_by_slug, but doesn't compute the value.
     986     *
     987     * @since 5.9.0
     988     *
     989     * @param array $settings Settings to process.
     990     * @param array $preset_metadata One of the PRESETS_METADATA values.
     991     * @param array $origins List of origins to process.
     992     * @return array Array of presets where the key and value are both the slug.
     993     */
     994    private static function get_settings_slugs( $settings, $preset_metadata, $origins = self::VALID_ORIGINS ) {
     995        $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
     996
     997        $result = array();
     998        foreach ( $origins as $origin ) {
     999            if ( ! isset( $preset_per_origin[ $origin ] ) ) {
     1000                continue;
     1001            }
     1002            foreach ( $preset_per_origin[ $origin ] as $preset ) {
     1003                $slug = _wp_to_kebab_case( $preset['slug'] );
     1004
     1005                // Use the array as a set so we don't get duplicates.
     1006                $result[ $slug ] = $slug;
     1007            }
     1008        }
     1009        return $result;
     1010    }
     1011
     1012    /**
     1013     * Transform a slug into a CSS Custom Property.
     1014     *
     1015     * @since 5.9.0
     1016     *
     1017     * @param string $input String to replace.
     1018     * @param string $slug The slug value to use to generate the custom property.
     1019     * @return string The CSS Custom Property. Something along the lines of --wp--preset--color--black.
     1020     */
     1021    private static function replace_slug_in_string( $input, $slug ) {
     1022        return strtr( $input, array( '$slug' => $slug ) );
     1023    }
     1024
     1025    /**
    7391026     * Given the block settings, it extracts the CSS Custom Properties
    7401027     * for the presets and adds them to the $declarations array
     
    7491036     *
    7501037     * @param array $settings Settings to process.
     1038     * @param array $origins  List of origins to process.
    7511039     * @return array Returns the modified $declarations.
    7521040     */
    753     private static function compute_preset_vars( $settings ) {
     1041    private static function compute_preset_vars( $settings, $origins ) {
    7541042        $declarations = array();
    755         foreach ( self::PRESETS_METADATA as $preset ) {
    756             $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() );
    757             $preset_by_slug    = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] );
    758             foreach ( $preset_by_slug as $slug => $value ) {
     1043        foreach ( self::PRESETS_METADATA as $preset_metadata ) {
     1044            $values_by_slug = self::get_settings_values_by_slug( $settings, $preset_metadata, $origins );
     1045            foreach ( $values_by_slug as $slug => $value ) {
    7591046                $declarations[] = array(
    760                     'name'  => '--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ),
     1047                    'name'  => self::replace_slug_in_string( $preset_metadata['css_vars'], $slug ),
    7611048                    'value' => $value,
    7621049                );
     
    8651152     *
    8661153     * @since 5.8.0
     1154     * @since 5.9.0 Added theme setting and properties parameters.
    8671155     *
    8681156     * @param array $styles Styles to process.
     1157     * @param array $settings Theme settings.
     1158     * @param array $properties Properties metadata.
    8691159     * @return array Returns the modified $declarations.
    8701160     */
    871     private static function compute_style_properties( $styles ) {
     1161    private static function compute_style_properties( $styles, $settings = array(), $properties = self::PROPERTIES_METADATA ) {
    8721162        $declarations = array();
    8731163        if ( empty( $styles ) ) {
     
    8751165        }
    8761166
    877         $properties = array();
    878         foreach ( self::PROPERTIES_METADATA as $name => $metadata ) {
    879             /*
    880              * Some properties can be shorthand properties, meaning that
    881              * they contain multiple values instead of a single one.
    882              * An example of this is the padding property.
    883              */
    884             if ( self::has_properties( $metadata ) ) {
    885                 foreach ( $metadata['properties'] as $property ) {
    886                     $properties[] = array(
    887                         'name'  => $name . '-' . $property,
    888                         'value' => array_merge( $metadata['value'], array( $property ) ),
    889                     );
     1167        foreach ( $properties as $css_property => $value_path ) {
     1168            $value = self::get_property_value( $styles, $value_path );
     1169
     1170            // Look up protected properties, keyed by value path.
     1171            // Skip protected properties that are explicitly set to `null`.
     1172            if ( is_array( $value_path ) ) {
     1173                $path_string = implode( '.', $value_path );
     1174                if (
     1175                    array_key_exists( $path_string, self::PROTECTED_PROPERTIES ) &&
     1176                    _wp_array_get( $settings, self::PROTECTED_PROPERTIES[ $path_string ], null ) === null
     1177                ) {
     1178                    continue;
    8901179                }
    891             } else {
    892                 $properties[] = array(
    893                     'name'  => $name,
    894                     'value' => $metadata['value'],
    895                 );
    896             }
    897         }
    898 
    899         foreach ( $properties as $prop ) {
    900             $value = self::get_property_value( $styles, $prop['value'] );
    901             if ( empty( $value ) ) {
     1180            }
     1181
     1182            // Skip if empty and not "0" or value represents array of longhand values.
     1183            $has_missing_value = empty( $value ) && ! is_numeric( $value );
     1184            if ( $has_missing_value || is_array( $value ) ) {
    9021185                continue;
    9031186            }
    9041187
    9051188            $declarations[] = array(
    906                 'name'  => $prop['name'],
     1189                'name'  => $css_property,
    9071190                'value' => $value,
    9081191            );
     
    9101193
    9111194        return $declarations;
    912     }
    913 
    914     /**
    915      * Whether the metadata contains a key named properties.
    916      *
    917      * @since 5.8.0
    918      *
    919      * @param array $metadata Description of the style property.
    920      * @return bool True if properties exists, false otherwise.
    921      */
    922     private static function has_properties( $metadata ) {
    923         if ( array_key_exists( 'properties', $metadata ) ) {
    924             return true;
    925         }
    926 
    927         return false;
    9281195    }
    9291196
     
    9361203     *
    9371204     * @since 5.8.0
     1205     * @since 5.9.0 Consider $value that are arrays as well.
    9381206     *
    9391207     * @param array $styles Styles subtree.
     
    9441212        $value = _wp_array_get( $styles, $path, '' );
    9451213
    946         if ( '' === $value ) {
     1214        if ( '' === $value || is_array( $value ) ) {
    9471215            return $value;
    9481216        }
     
    10161284    }
    10171285
    1018 
    10191286    /**
    10201287     * Builds metadata for the style nodes, which returns in the form of:
     
    10231290     *       [
    10241291     *         'path'     => [ 'path', 'to', 'some', 'node' ],
    1025      *         'selector' => 'CSS selector for some node'
     1292     *         'selector' => 'CSS selector for some node',
     1293     *         'duotone'  => 'CSS selector for duotone for some node'
    10261294     *       ],
    10271295     *       [
    10281296     *         'path'     => ['path', 'to', 'other', 'node' ],
    1029      *         'selector' => 'CSS selector for other node'
     1297     *         'selector' => 'CSS selector for other node',
     1298     *         'duotone'  => null
    10301299     *       ],
    10311300     *     ]
     
    10691338            }
    10701339
     1340            $duotone_selector = null;
     1341            if ( isset( $selectors[ $name ]['duotone'] ) ) {
     1342                $duotone_selector = $selectors[ $name ]['duotone'];
     1343            }
     1344
    10711345            $nodes[] = array(
    10721346                'path'     => array( 'styles', 'blocks', $name ),
    10731347                'selector' => $selector,
     1348                'duotone'  => $duotone_selector,
    10741349            );
    10751350
     
    10911366     *
    10921367     * @since 5.8.0
     1368     * @since 5.9.0 Duotone preset also has origins.
    10931369     *
    10941370     * @param WP_Theme_JSON $incoming Data to merge.
     
    10991375
    11001376        /*
    1101          * The array_replace_recursive() algorithm merges at the leaf level.
     1377         * The array_replace_recursive algorithm merges at the leaf level.
    11021378         * For leaf values that are arrays it will use the numeric indexes for replacement.
    11031379         * In those cases, we want to replace the existing with the incoming value, if it exists.
     
    11051381        $to_replace   = array();
    11061382        $to_replace[] = array( 'spacing', 'units' );
    1107         $to_replace[] = array( 'color', 'duotone' );
    11081383        foreach ( self::VALID_ORIGINS as $origin ) {
     1384            $to_replace[] = array( 'color', 'duotone', $origin );
    11091385            $to_replace[] = array( 'color', 'palette', $origin );
    11101386            $to_replace[] = array( 'color', 'gradients', $origin );
     
    11231399            }
    11241400        }
     1401
     1402    }
     1403
     1404    /**
     1405     * Removes insecure data from theme.json.
     1406     *
     1407     * @since 5.9.0
     1408     *
     1409     * @param array $theme_json Structure to sanitize.
     1410     * @return array Sanitized structure.
     1411     */
     1412    public static function remove_insecure_properties( $theme_json ) {
     1413        $sanitized = array();
     1414
     1415        $theme_json = WP_Theme_JSON_Schema::migrate( $theme_json );
     1416
     1417        $valid_block_names   = array_keys( self::get_blocks_metadata() );
     1418        $valid_element_names = array_keys( self::ELEMENTS );
     1419        $theme_json          = self::sanitize( $theme_json, $valid_block_names, $valid_element_names );
     1420
     1421        $blocks_metadata = self::get_blocks_metadata();
     1422        $style_nodes     = self::get_style_nodes( $theme_json, $blocks_metadata );
     1423        foreach ( $style_nodes as $metadata ) {
     1424            $input = _wp_array_get( $theme_json, $metadata['path'], array() );
     1425            if ( empty( $input ) ) {
     1426                continue;
     1427            }
     1428
     1429            $output = self::remove_insecure_styles( $input );
     1430            if ( ! empty( $output ) ) {
     1431                _wp_array_set( $sanitized, $metadata['path'], $output );
     1432            }
     1433        }
     1434
     1435        $setting_nodes = self::get_setting_nodes( $theme_json );
     1436        foreach ( $setting_nodes as $metadata ) {
     1437            $input = _wp_array_get( $theme_json, $metadata['path'], array() );
     1438            if ( empty( $input ) ) {
     1439                continue;
     1440            }
     1441
     1442            $output = self::remove_insecure_settings( $input );
     1443            if ( ! empty( $output ) ) {
     1444                _wp_array_set( $sanitized, $metadata['path'], $output );
     1445            }
     1446        }
     1447
     1448        if ( empty( $sanitized['styles'] ) ) {
     1449            unset( $theme_json['styles'] );
     1450        } else {
     1451            $theme_json['styles'] = $sanitized['styles'];
     1452        }
     1453
     1454        if ( empty( $sanitized['settings'] ) ) {
     1455            unset( $theme_json['settings'] );
     1456        } else {
     1457            $theme_json['settings'] = $sanitized['settings'];
     1458        }
     1459
     1460        return $theme_json;
     1461    }
     1462
     1463    /**
     1464     * Processes a setting node and returns the same node
     1465     * without the insecure settings.
     1466     *
     1467     * @since 5.9.0
     1468     *
     1469     * @param array $input Node to process.
     1470     * @return array
     1471     */
     1472    private static function remove_insecure_settings( $input ) {
     1473        $output = array();
     1474        foreach ( self::PRESETS_METADATA as $preset_metadata ) {
     1475            $presets = _wp_array_get( $input, $preset_metadata['path'], null );
     1476            if ( null === $presets ) {
     1477                continue;
     1478            }
     1479
     1480            $escaped_preset = array();
     1481            foreach ( $presets as $preset ) {
     1482                if (
     1483                    esc_attr( esc_html( $preset['name'] ) ) === $preset['name'] &&
     1484                    sanitize_html_class( $preset['slug'] ) === $preset['slug']
     1485                ) {
     1486                    $value = null;
     1487                    if ( isset( $preset_metadata['value_key'] ) ) {
     1488                        $value = $preset[ $preset_metadata['value_key'] ];
     1489                    } elseif (
     1490                        isset( $preset_metadata['value_func'] ) &&
     1491                        is_callable( $preset_metadata['value_func'] )
     1492                    ) {
     1493                        $value = call_user_func( $preset_metadata['value_func'], $preset );
     1494                    }
     1495
     1496                    $preset_is_valid = true;
     1497                    foreach ( $preset_metadata['properties'] as $property ) {
     1498                        if ( ! self::is_safe_css_declaration( $property, $value ) ) {
     1499                            $preset_is_valid = false;
     1500                            break;
     1501                        }
     1502                    }
     1503
     1504                    if ( $preset_is_valid ) {
     1505                        $escaped_preset[] = $preset;
     1506                    }
     1507                }
     1508            }
     1509
     1510            if ( ! empty( $escaped_preset ) ) {
     1511                _wp_array_set( $output, $preset_metadata['path'], $escaped_preset );
     1512            }
     1513        }
     1514
     1515        return $output;
     1516    }
     1517
     1518    /**
     1519     * Processes a style node and returns the same node
     1520     * without the insecure styles.
     1521     *
     1522     * @since 5.9.0
     1523     *
     1524     * @param array $input Node to process.
     1525     * @return array
     1526     */
     1527    private static function remove_insecure_styles( $input ) {
     1528        $output       = array();
     1529        $declarations = self::compute_style_properties( $input );
     1530
     1531        foreach ( $declarations as $declaration ) {
     1532            if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) {
     1533                $path = self::PROPERTIES_METADATA[ $declaration['name'] ];
     1534
     1535                // Check the value isn't an array before adding so as to not
     1536                // double up shorthand and longhand styles.
     1537                $value = _wp_array_get( $input, $path, array() );
     1538                if ( ! is_array( $value ) ) {
     1539                    _wp_array_set( $output, $path, $value );
     1540                }
     1541            }
     1542        }
     1543        return $output;
     1544    }
     1545
     1546    /**
     1547     * Checks that a declaration provided by the user is safe.
     1548     *
     1549     * @since 5.9.0
     1550     *
     1551     * @param string $property_name Property name in a CSS declaration, i.e. the `color` in `color: red`.
     1552     * @param string $property_value Value in a CSS declaration, i.e. the `red` in `color: red`.
     1553     * @return boolean
     1554     */
     1555    private static function is_safe_css_declaration( $property_name, $property_value ) {
     1556        $style_to_validate = $property_name . ': ' . $property_value;
     1557        $filtered          = esc_html( safecss_filter_attr( $style_to_validate ) );
     1558        return ! empty( trim( $filtered ) );
    11251559    }
    11261560
     
    11771611                $theme_settings['settings']['typography'] = array();
    11781612            }
    1179             $theme_settings['settings']['typography']['customLineHeight'] = $settings['enableCustomLineHeight'];
     1613            $theme_settings['settings']['typography']['lineHeight'] = $settings['enableCustomLineHeight'];
    11801614        }
    11811615
     
    12211655                $theme_settings['settings']['spacing'] = array();
    12221656            }
    1223             $theme_settings['settings']['spacing']['customPadding'] = $settings['enableCustomSpacing'];
     1657            $theme_settings['settings']['spacing']['padding'] = $settings['enableCustomSpacing'];
    12241658        }
    12251659
Note: See TracChangeset for help on using the changeset viewer.