Make WordPress Core


Ignore:
Timestamp:
04/11/2022 10:36:02 AM (3 years ago)
Author:
gziolo
Message:

Editor: Add functionality required for theme export in the site editor

This bring across changes to theme export functionality, and related code, and tests. Relates issue in Gutenberg: https://github.com/WordPress/gutenberg/issues/39889.

Props scruffian, timothyblynjacobs, oandregal, ajlende, zieleadam.
See #55505.

File:
1 edited

Legend:

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

    r52791 r53129  
    7575     * This contains the necessary metadata to process them:
    7676     *
    77      * - path              => where to find the preset within the settings section
    78      * - override          => whether a theme preset with the same slug as a default preset
    79      *                        can override it
     77     * - path             => Where to find the preset within the settings section.
     78     * - prevent_override => Disables override of default presets by theme presets.
     79     *                       The relationship between whether to override the defaults
     80     *                       and whether the defaults are enabled is inverse:
     81     *                         - If defaults are enabled  => theme presets should not be overriden
     82     *                         - If defaults are disabled => theme presets should be overriden
     83     *                       For example, a theme sets defaultPalette to false,
     84     *                       making the default palette hidden from the user.
     85     *                       In that case, we want all the theme presets to be present,
     86     *                       so they should override the defaults by setting this false.
    8087     * - use_default_names => whether to use the default names
    81      * - value_key         => the key that represents the value
    82      * - value_func        => optionally, instead of value_key, a function to generate
    83      *                        the value that takes a preset as an argument
    84      *                        (either value_key or value_func should be present)
    85      * - css_vars          => template string to use in generating the CSS Custom Property.
    86      *                        Example output: "--wp--preset--duotone--blue: <value>" will generate
    87      *                        as many CSS Custom Properties as presets defined
    88      *                        substituting the $slug for the slug's value for each preset value.
    89      * - classes           => array containing a structure with the classes to
    90      *                        generate for the presets, where for each array item
    91      *                        the key is the class name and the value the property name.
    92      *                        The "$slug" substring will be replaced by the slug of each preset.
    93      *                        For example:
    94      *                        'classes' => array(
    95      *                           '.has-$slug-color'            => 'color',
    96      *                           '.has-$slug-background-color' => 'background-color',
    97      *                           '.has-$slug-border-color'     => 'border-color',
    98      *                        )
    99      * - properties        => array of CSS properties to be used by kses to
    100      *                        validate the content of each preset
    101      *                        by means of the remove_insecure_properties method.
     88     * - value_key        => the key that represents the value
     89     * - value_func       => optionally, instead of value_key, a function to generate
     90     *                       the value that takes a preset as an argument
     91     *                       (either value_key or value_func should be present)
     92     * - css_vars         => template string to use in generating the CSS Custom Property.
     93     *                       Example output: "--wp--preset--duotone--blue: <value>" will generate as many CSS Custom Properties as presets defined
     94     *                       substituting the $slug for the slug's value for each preset value.
     95     * - classes          => array containing a structure with the classes to
     96     *                       generate for the presets, where for each array item
     97     *                       the key is the class name and the value the property name.
     98     *                       The "$slug" substring will be replaced by the slug of each preset.
     99     *                       For example:
     100     *                       'classes' => array(
     101     *                         '.has-$slug-color'            => 'color',
     102     *                         '.has-$slug-background-color' => 'background-color',
     103     *                         '.has-$slug-border-color'     => 'border-color',
     104     *                       )
     105     * - properties       => array of CSS properties to be used by kses to
     106     *                       validate the content of each preset
     107     *                       by means of the remove_insecure_properties method.
    102108     *
    103109     * @since 5.8.0
    104110     * @since 5.9.0 Added the `color.duotone` and `typography.fontFamilies` presets,
    105111     *              `use_default_names` preset key, and simplified the metadata structure.
     112     * @since 6.0.0 Replaced `override` with `prevent_override` and updated the
     113     *              `prevent_overried` value for `color.duotone` to use `color.defaultDuotone`.
    106114     * @var array
    107115     */
     
    109117        array(
    110118            'path'              => array( 'color', 'palette' ),
    111             'override'          => array( 'color', 'defaultPalette' ),
     119            'prevent_override'  => array( 'color', 'defaultPalette' ),
    112120            'use_default_names' => false,
    113121            'value_key'         => 'color',
     
    122130        array(
    123131            'path'              => array( 'color', 'gradients' ),
    124             'override'          => array( 'color', 'defaultGradients' ),
     132            'prevent_override'  => array( 'color', 'defaultGradients' ),
    125133            'use_default_names' => false,
    126134            'value_key'         => 'gradient',
     
    131139        array(
    132140            'path'              => array( 'color', 'duotone' ),
    133             'override'          => true,
     141            'prevent_override'  => array( 'color', 'defaultDuotone' ),
    134142            'use_default_names' => false,
    135143            'value_func'        => 'wp_get_duotone_filter_property',
     
    140148        array(
    141149            'path'              => array( 'typography', 'fontSizes' ),
    142             'override'          => true,
     150            'prevent_override'  => false,
    143151            'use_default_names' => true,
    144152            'value_key'         => 'size',
     
    149157        array(
    150158            'path'              => array( 'typography', 'fontFamilies' ),
    151             'override'          => true,
     159            'prevent_override'  => false,
    152160            'use_default_names' => false,
    153161            'value_key'         => 'fontFamily',
     
    230238    const VALID_TOP_LEVEL_KEYS = array(
    231239        'customTemplates',
     240        'patterns',
    232241        'settings',
    233242        'styles',
    234243        'templateParts',
    235244        'version',
     245        'title',
    236246    );
    237247
     
    243253     *              added new properties for `border`, `color`, `spacing`,
    244254     *              and `typography`, and renamed others according to the new schema.
     255     * @since 6.0.0 Added `color.defaultDuotone`.
    245256     * @var array
    246257     */
     
    258269            'customDuotone'    => null,
    259270            'customGradient'   => null,
     271            'defaultDuotone'   => null,
    260272            'defaultGradients' => null,
    261273            'defaultPalette'   => null,
     
    349361
    350362    /**
     363     * Options that settings.appearanceTools enables.
     364     *
     365     * @since 6.0.0
     366     * @var array
     367     */
     368    const APPEARANCE_TOOLS_OPT_INS = array(
     369        array( 'border', 'color' ),
     370        array( 'border', 'radius' ),
     371        array( 'border', 'style' ),
     372        array( 'border', 'width' ),
     373        array( 'color', 'link' ),
     374        array( 'spacing', 'blockGap' ),
     375        array( 'spacing', 'margin' ),
     376        array( 'spacing', 'padding' ),
     377        array( 'typography', 'lineHeight' ),
     378    );
     379
     380    /**
    351381     * The latest version of the schema in use.
    352382     *
     
    430460     */
    431461    protected static function do_opt_in_into_settings( &$context ) {
    432         $to_opt_in = array(
    433             array( 'border', 'color' ),
    434             array( 'border', 'radius' ),
    435             array( 'border', 'style' ),
    436             array( 'border', 'width' ),
    437             array( 'color', 'link' ),
    438             array( 'spacing', 'blockGap' ),
    439             array( 'spacing', 'margin' ),
    440             array( 'spacing', 'padding' ),
    441             array( 'typography', 'lineHeight' ),
    442         );
    443 
    444         foreach ( $to_opt_in as $path ) {
     462        foreach ( static::APPEARANCE_TOOLS_OPT_INS as $path ) {
    445463            // Use "unset prop" as a marker instead of "null" because
    446464            // "null" can be a valid value for some props (e.g. blockGap).
     
    831849                $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
    832850                if ( $has_block_gap_support ) {
    833                     $block_rules .= '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }';
    834                     $block_rules .= '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }';
     851                    $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
     852                    $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
    835853                }
    836854            }
     
    15001518
    15011519        return $nodes;
     1520    }
     1521
     1522    /**
     1523     * For metadata values that can either be booleans or paths to booleans, gets the value.
     1524     *
     1525     * ```php
     1526     * $data = array(
     1527     *   'color' => array(
     1528     *     'defaultPalette' => true
     1529     *   )
     1530     * );
     1531     *
     1532     * static::get_metadata_boolean( $data, false );
     1533     * // => false
     1534     *
     1535     * static::get_metadata_boolean( $data, array( 'color', 'defaultPalette' ) );
     1536     * // => true
     1537     * ```
     1538     *
     1539     * @since 6.0.0
     1540     *
     1541     * @param array      $data The data to inspect.
     1542     * @param bool|array $path Boolean or path to a boolean.
     1543     * @param bool       $default Default value if the referenced path is missing.
     1544     * @return boolean
     1545     */
     1546    protected static function get_metadata_boolean( $data, $path, $default = false ) {
     1547        if ( is_bool( $path ) ) {
     1548            return $path;
     1549        }
     1550
     1551        if ( is_array( $path ) ) {
     1552            $value = _wp_array_get( $data, $path );
     1553            if ( null !== $value ) {
     1554                return $value;
     1555            }
     1556        }
     1557
     1558        return $default;
    15021559    }
    15031560
     
    15511608            // Replace the presets.
    15521609            foreach ( static::PRESETS_METADATA as $preset ) {
    1553                 $override_preset = static::should_override_preset( $this->theme_json, $node['path'], $preset['override'] );
     1610                $override_preset = ! static::get_metadata_boolean( $this->theme_json['settings'], $preset['prevent_override'], true );
    15541611
    15551612                foreach ( static::VALID_ORIGINS as $origin ) {
     
    16251682     *
    16261683     * @since 5.9.0
     1684     * @deprecated 6.0.0 Use {@see 'get_metadata_boolean'} instead.
    16271685     *
    16281686     * @param array      $theme_json The theme.json like structure to inspect.
    1629      * @param array      $path Path to inspect.
    1630      * @param bool|array $override Data to compute whether to override the preset.
     1687     * @param array      $path       Path to inspect.
     1688     * @param bool|array $override   Data to compute whether to override the preset.
    16311689     * @return boolean
    16321690     */
    16331691    protected static function should_override_preset( $theme_json, $path, $override ) {
     1692        _deprecated_function( __METHOD__, '6.0.0', 'get_metadata_boolean' );
     1693
    16341694        if ( is_bool( $override ) ) {
    16351695            return $override;
     
    20142074    }
    20152075
     2076    /**
     2077     * Returns the current theme's wanted patterns(slugs) to be
     2078     * registered from Pattern Directory.
     2079     *
     2080     * @since 6.0.0
     2081     *
     2082     * @return string[]
     2083     */
     2084    public function get_patterns() {
     2085        if ( isset( $this->theme_json['patterns'] ) && is_array( $this->theme_json['patterns'] ) ) {
     2086            return $this->theme_json['patterns'];
     2087        }
     2088        return array();
     2089    }
     2090
     2091    /**
     2092     * Returns a valid theme.json as provided by a theme.
     2093     *
     2094     * Unlike get_raw_data() this returns the presets flattened, as provided by a theme.
     2095     * This also uses appearanceTools instead of their opt-ins if all of them are true.
     2096     *
     2097     * @since 6.0.0
     2098     *
     2099     * @return array
     2100     */
     2101    public function get_data() {
     2102        $output = $this->theme_json;
     2103        $nodes  = static::get_setting_nodes( $output );
     2104
     2105        /**
     2106         * Flatten the theme & custom origins into a single one.
     2107         *
     2108         * For example, the following:
     2109         *
     2110         * {
     2111         *   "settings": {
     2112         *     "color": {
     2113         *       "palette": {
     2114         *         "theme": [ {} ],
     2115         *         "custom": [ {} ]
     2116         *       }
     2117         *     }
     2118         *   }
     2119         * }
     2120         *
     2121         * will be converted to:
     2122         *
     2123         * {
     2124         *   "settings": {
     2125         *     "color": {
     2126         *       "palette": [ {} ]
     2127         *     }
     2128         *   }
     2129         * }
     2130         */
     2131        foreach ( $nodes as $node ) {
     2132            foreach ( static::PRESETS_METADATA as $preset_metadata ) {
     2133                $path   = array_merge( $node['path'], $preset_metadata['path'] );
     2134                $preset = _wp_array_get( $output, $path, null );
     2135                if ( null === $preset ) {
     2136                    continue;
     2137                }
     2138
     2139                $items = array();
     2140                if ( isset( $preset['theme'] ) ) {
     2141                    foreach ( $preset['theme'] as $item ) {
     2142                        $slug = $item['slug'];
     2143                        unset( $item['slug'] );
     2144                        $items[ $slug ] = $item;
     2145                    }
     2146                }
     2147                if ( isset( $preset['custom'] ) ) {
     2148                    foreach ( $preset['custom'] as $item ) {
     2149                        $slug = $item['slug'];
     2150                        unset( $item['slug'] );
     2151                        $items[ $slug ] = $item;
     2152                    }
     2153                }
     2154                $flattened_preset = array();
     2155                foreach ( $items as $slug => $value ) {
     2156                    $flattened_preset[] = array_merge( array( 'slug' => $slug ), $value );
     2157                }
     2158                _wp_array_set( $output, $path, $flattened_preset );
     2159            }
     2160        }
     2161
     2162        // If all of the static::APPEARANCE_TOOLS_OPT_INS are true,
     2163        // this code unsets them and sets 'appearanceTools' instead.
     2164        foreach ( $nodes as $node ) {
     2165            $all_opt_ins_are_set = true;
     2166            foreach ( static::APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) {
     2167                $full_path = array_merge( $node['path'], $opt_in_path );
     2168                // Use "unset prop" as a marker instead of "null" because
     2169                // "null" can be a valid value for some props (e.g. blockGap).
     2170                $opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' );
     2171                if ( 'unset prop' === $opt_in_value ) {
     2172                    $all_opt_ins_are_set = false;
     2173                    break;
     2174                }
     2175            }
     2176
     2177            if ( $all_opt_ins_are_set ) {
     2178                _wp_array_set( $output, array_merge( $node['path'], array( 'appearanceTools' ) ), true );
     2179                foreach ( static::APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) {
     2180                    $full_path = array_merge( $node['path'], $opt_in_path );
     2181                    // Use "unset prop" as a marker instead of "null" because
     2182                    // "null" can be a valid value for some props (e.g. blockGap).
     2183                    $opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' );
     2184                    if ( true !== $opt_in_value ) {
     2185                        continue;
     2186                    }
     2187
     2188                    // The following could be improved to be path independent.
     2189                    // At the moment it relies on a couple of assumptions:
     2190                    //
     2191                    // - all opt-ins having a path of size 2.
     2192                    // - there's two sources of settings: the top-level and the block-level.
     2193                    if (
     2194                        ( 1 === count( $node['path'] ) ) &&
     2195                        ( 'settings' === $node['path'][0] )
     2196                    ) {
     2197                        // Top-level settings.
     2198                        unset( $output['settings'][ $opt_in_path[0] ][ $opt_in_path[1] ] );
     2199                        if ( empty( $output['settings'][ $opt_in_path[0] ] ) ) {
     2200                            unset( $output['settings'][ $opt_in_path[0] ] );
     2201                        }
     2202                    } elseif (
     2203                        ( 3 === count( $node['path'] ) ) &&
     2204                        ( 'settings' === $node['path'][0] ) &&
     2205                        ( 'blocks' === $node['path'][1] )
     2206                    ) {
     2207                        // Block-level settings.
     2208                        $block_name = $node['path'][2];
     2209                        unset( $output['settings']['blocks'][ $block_name ][ $opt_in_path[0] ][ $opt_in_path[1] ] );
     2210                        if ( empty( $output['settings']['blocks'][ $block_name ][ $opt_in_path[0] ] ) ) {
     2211                            unset( $output['settings']['blocks'][ $block_name ][ $opt_in_path[0] ] );
     2212                        }
     2213                    }
     2214                }
     2215            }
     2216        }
     2217
     2218        wp_recursive_ksort( $output );
     2219
     2220        return $output;
     2221    }
     2222
    20162223}
Note: See TracChangeset for help on using the changeset viewer.