Make WordPress Core


Ignore:
Timestamp:
04/11/2022 10:36:02 AM (4 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.