Make WordPress Core

Changeset 53129


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.

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-template-utils.php

    r52923 r53129  
    120120                'home'           => array(
    121121                        'title'       => _x( 'Home', 'Template name' ),
    122                         'description' => __( 'Displays as the site\'s home page, or as the Posts page when a static home page isn\'t set.' ),
     122                        'description' => __( 'Displays posts on the homepage, or on the Posts page if a static homepage is set.' ),
    123123                ),
    124124                'front-page'     => array(
    125125                        'title'       => _x( 'Front Page', 'Template name' ),
    126                         'description' => __( 'Displays as the site\'s home page.' ),
     126                        'description' => __( 'Displays the homepage.' ),
    127127                ),
    128128                'singular'       => array(
     
    163163                ),
    164164                'attachment'     => array(
    165                         'title'       => __( 'Attachment' ),
     165                        'title'       => __( 'Media' ),
    166166                        'description' => __( 'Displays individual media items or attachments.' ),
    167167                ),
    168168                'search'         => array(
    169169                        'title'       => _x( 'Search', 'Template name' ),
    170                         'description' => __( 'Template used to display search results.' ),
     170                        'description' => __( 'Displays search results.' ),
    171171                ),
    172172                'privacy-policy' => array(
     
    904904
    905905/**
     906 * Filters theme directories that should be ignored during export.
     907 *
     908 * @since 6.0.0
     909 *
     910 * @param string $path The path of the file in the theme.
     911 * @return Bool Whether this file is in an ignored directory.
     912 */
     913function wp_is_theme_directory_ignored( $path ) {
     914        $directories_to_ignore = array( '.svn', '.git', '.hg', '.bzr', 'node_modules', 'vendor' );
     915        foreach ( $directories_to_ignore as $directory ) {
     916                if ( strpos( $path, $directory ) === 0 ) {
     917                        return true;
     918                }
     919        }
     920
     921        return false;
     922}
     923
     924/**
    906925 * Creates an export of the current templates and
    907926 * template parts from the site editor at the
     
    909928 *
    910929 * @since 5.9.0
     930 * @since 6.0.0 Adds the whole theme to the export archive.
    911931 *
    912932 * @return WP_Error|string Path of the ZIP file or error on failure.
     
    917937        }
    918938
    919         $obscura  = wp_generate_password( 12, false, false );
    920         $filename = get_temp_dir() . 'edit-site-export-' . $obscura . '.zip';
     939        $obscura    = wp_generate_password( 12, false, false );
     940        $theme_name = wp_get_theme()->get( 'TextDomain' );
     941        $filename   = get_temp_dir() . $theme_name . $obscura . '.zip';
    921942
    922943        $zip = new ZipArchive();
    923         if ( true !== $zip->open( $filename, ZipArchive::CREATE ) ) {
     944        if ( true !== $zip->open( $filename, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
    924945                return new WP_Error( 'unable_to_create_zip', __( 'Unable to open export file (archive) for writing.' ) );
    925946        }
    926947
    927         $zip->addEmptyDir( 'theme' );
    928         $zip->addEmptyDir( 'theme/templates' );
    929         $zip->addEmptyDir( 'theme/parts' );
     948        $zip->addEmptyDir( 'templates' );
     949        $zip->addEmptyDir( 'parts' );
     950
     951        // Get path of the theme.
     952        $theme_path = wp_normalize_path( get_stylesheet_directory() );
     953
     954        // Create recursive directory iterator.
     955        $theme_files = new RecursiveIteratorIterator(
     956                new RecursiveDirectoryIterator( $theme_path ),
     957                RecursiveIteratorIterator::LEAVES_ONLY
     958        );
     959
     960        // Make a copy of the current theme.
     961        foreach ( $theme_files as $file ) {
     962                // Skip directories as they are added automatically.
     963                if ( ! $file->isDir() ) {
     964                        // Get real and relative path for current file.
     965                        $file_path     = wp_normalize_path( $file );
     966                        $relative_path = substr( $file_path, strlen( $theme_path ) + 1 );
     967
     968                        if ( ! wp_is_theme_directory_ignored( $relative_path ) ) {
     969                                $zip->addFile( $file_path, $relative_path );
     970                        }
     971                }
     972        }
    930973
    931974        // Load templates into the zip file.
     
    935978
    936979                $zip->addFromString(
    937                         'theme/templates/' . $template->slug . '.html',
     980                        'templates/' . $template->slug . '.html',
    938981                        $template->content
    939982                );
     
    944987        foreach ( $template_parts as $template_part ) {
    945988                $zip->addFromString(
    946                         'theme/parts/' . $template_part->slug . '.html',
     989                        'parts/' . $template_part->slug . '.html',
    947990                        $template_part->content
    948991                );
    949992        }
    950993
     994        // Load theme.json into the zip file.
     995        $tree = WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) );
     996        // Merge with user data.
     997        $tree->merge( WP_Theme_JSON_Resolver::get_user_data() );
     998
     999        $theme_json_raw = $tree->get_data();
     1000        // If a version is defined, add a schema.
     1001        if ( $theme_json_raw['version'] ) {
     1002                global $wp_version;
     1003                $theme_json_version = 'wp/' . substr( $wp_version, 0, 3 );
     1004                $schema         = array( '$schema' => 'https://schemas.wp.org/' . $theme_json_version . '/theme.json' );
     1005                $theme_json_raw = array_merge( $schema, $theme_json_raw );
     1006        }
     1007
     1008        // Convert to a string.
     1009        $theme_json_encoded = wp_json_encode( $theme_json_raw, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
     1010
     1011        // Replace 4 spaces with a tab.
     1012        $theme_json_tabbed = preg_replace( '~(?:^|\G)\h{4}~m', "\t", $theme_json_encoded );
     1013
     1014        // Add the theme.json file to the zip.
     1015        $zip->addFromString(
     1016                'theme.json',
     1017                $theme_json_tabbed
     1018        );
     1019
    9511020        // Save changes to the zip file.
    9521021        $zip->close();
  • trunk/src/wp-includes/class-wp-theme-json-resolver.php

    r53072 r53129  
    152152         * @since 5.8.0
    153153         * @since 5.9.0 Theme supports have been inlined and the `$theme_support_data` argument removed.
     154         * @since 6.0.0 Adds a second parameter to allow the theme data to be returned without theme supports.
    154155         *
    155156         * @param array $deprecated Deprecated. Not used.
     157         * @param array $options Contains a key called with_supports to determine whether to include theme supports in the data.
    156158         * @return WP_Theme_JSON Entity that holds theme data.
    157159         */
    158         public static function get_theme_data( $deprecated = array() ) {
     160        public static function get_theme_data( $deprecated = array(), $options = array() ) {
    159161                if ( ! empty( $deprecated ) ) {
    160162                        _deprecated_argument( __METHOD__, '5.9.0' );
    161163                }
     164
     165                $options = wp_parse_args( $options, array( 'with_supports' => true ) );
     166
    162167                if ( null === static::$theme ) {
    163168                        $theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
     
    176181                                static::$theme = $parent_theme;
    177182                        }
     183                }
     184
     185                if ( ! $options['with_supports'] ) {
     186                        return static::$theme;
    178187                }
    179188
     
    209218                        }
    210219                        $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients;
     220
     221                        // Classic themes without a theme.json don't support global duotone.
     222                        $theme_support_data['settings']['color']['defaultDuotone'] = false;
    211223                }
    212224                $with_theme_supports = new WP_Theme_JSON( $theme_support_data );
     
    478490                return $variations;
    479491        }
     492
    480493}
  • 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}
  • trunk/src/wp-includes/functions.php

    r53118 r53129  
    84418441        return abs( (float) $expected - (float) $actual ) <= $precision;
    84428442}
     8443
     8444/**
     8445 * Sorts the keys of an array alphabetically.
     8446 * The array is passed by reference so it doesn't get returned
     8447 * which mimics the behaviour of ksort.
     8448 *
     8449 * @since 6.0.0
     8450 *
     8451 * @param array $array The array to sort, passed by reference.
     8452 */
     8453function wp_recursive_ksort( &$array ) {
     8454        foreach ( $array as &$value ) {
     8455                if ( is_array( $value ) ) {
     8456                        wp_recursive_ksort( $value );
     8457                }
     8458        }
     8459        ksort( $array );
     8460}
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-edit-site-export-controller.php

    r52340 r53129  
    5454         */
    5555        public function permissions_check() {
    56                 if ( ! current_user_can( 'edit_theme_options' ) ) {
    57                         return new WP_Error(
    58                                 'rest_cannot_export_templates',
    59                                 __( 'Sorry, you are not allowed to export templates and template parts.' ),
    60                                 array( 'status' => rest_authorization_required_code() )
    61                         );
     56                if ( current_user_can( 'edit_theme_options' ) ) {
     57                        return true;
    6258                }
    6359
    64                 return true;
     60                return new WP_Error(
     61                        'rest_cannot_export_templates',
     62                        __( 'Sorry, you are not allowed to export templates and template parts.' ),
     63                        array( 'status' => rest_authorization_required_code() )
     64                );
    6565        }
    6666
     
    8383                }
    8484
     85                $theme_name = wp_get_theme()->get( 'TextDomain' );
    8586                header( 'Content-Type: application/zip' );
    86                 header( 'Content-Disposition: attachment; filename=edit-site-export.zip' );
     87                header( 'Content-Disposition: attachment; filename=' . $theme_name . '.zip' );
    8788                header( 'Content-Length: ' . filesize( $filename ) );
    8889                flush();
  • trunk/tests/phpunit/tests/block-template-utils.php

    r52331 r53129  
    358358                $zip = new ZipArchive();
    359359                $zip->open( $filename );
    360                 $has_theme_dir                = $zip->locateName( 'theme/' ) !== false;
    361                 $has_block_templates_dir      = $zip->locateName( 'theme/templates/' ) !== false;
    362                 $has_block_template_parts_dir = $zip->locateName( 'theme/parts/' ) !== false;
    363                 $this->assertTrue( $has_theme_dir, 'theme directory exists' );
     360                $has_theme_json               = $zip->locateName( 'theme.json' ) !== false;
     361                $has_block_templates_dir      = $zip->locateName( 'templates/' ) !== false;
     362                $has_block_template_parts_dir = $zip->locateName( 'parts/' ) !== false;
     363                $this->assertTrue( $has_theme_json, 'theme.json exists' );
    364364                $this->assertTrue( $has_block_templates_dir, 'theme/templates directory exists' );
    365365                $this->assertTrue( $has_block_template_parts_dir, 'theme/parts directory exists' );
  • trunk/tests/phpunit/tests/functions.php

    r53044 r53129  
    21742174                $this->assertEquals( 111, wp_filesize( $file ) );
    21752175        }
     2176
     2177        /**
     2178         * @ticket 55505
     2179         * @covers ::wp_recursive_ksort
     2180         */
     2181        function test_wp_recursive_ksort() {
     2182                // Create an array to test.
     2183                $theme_json = array(
     2184                        'version'  => 1,
     2185                        'settings' => array(
     2186                                'typography' => array(
     2187                                        'fontFamilies' => array(
     2188                                                'fontFamily' => 'DM Sans, sans-serif',
     2189                                                'slug'       => 'dm-sans',
     2190                                                'name'       => 'DM Sans',
     2191                                        ),
     2192                                ),
     2193                                'color'      => array(
     2194                                        'palette' => array(
     2195                                                array(
     2196                                                        'slug'  => 'foreground',
     2197                                                        'color' => '#242321',
     2198                                                        'name'  => 'Foreground',
     2199                                                ),
     2200                                                array(
     2201                                                        'slug'  => 'background',
     2202                                                        'color' => '#FCFBF8',
     2203                                                        'name'  => 'Background',
     2204                                                ),
     2205                                                array(
     2206                                                        'slug'  => 'primary',
     2207                                                        'color' => '#71706E',
     2208                                                        'name'  => 'Primary',
     2209                                                ),
     2210                                                array(
     2211                                                        'slug'  => 'tertiary',
     2212                                                        'color' => '#CFCFCF',
     2213                                                        'name'  => 'Tertiary',
     2214                                                ),
     2215                                        ),
     2216                                ),
     2217                        ),
     2218                );
     2219
     2220                // Sort the array.
     2221                wp_recursive_ksort( $theme_json );
     2222
     2223                // Expected result.
     2224                $expected_theme_json = array(
     2225                        'settings' => array(
     2226                                'color'      => array(
     2227                                        'palette' => array(
     2228                                                array(
     2229                                                        'color' => '#242321',
     2230                                                        'name'  => 'Foreground',
     2231                                                        'slug'  => 'foreground',
     2232                                                ),
     2233                                                array(
     2234                                                        'color' => '#FCFBF8',
     2235                                                        'name'  => 'Background',
     2236                                                        'slug'  => 'background',
     2237                                                ),
     2238                                                array(
     2239                                                        'color' => '#71706E',
     2240                                                        'name'  => 'Primary',
     2241                                                        'slug'  => 'primary',
     2242                                                ),
     2243                                                array(
     2244                                                        'color' => '#CFCFCF',
     2245                                                        'name'  => 'Tertiary',
     2246                                                        'slug'  => 'tertiary',
     2247                                                ),
     2248                                        ),
     2249                                ),
     2250                                'typography' => array(
     2251                                        'fontFamilies' => array(
     2252                                                'fontFamily' => 'DM Sans, sans-serif',
     2253                                                'name'       => 'DM Sans',
     2254                                                'slug'       => 'dm-sans',
     2255                                        ),
     2256                                ),
     2257                        ),
     2258                        'version'  => 1,
     2259                );
     2260                $this->assertEquals( $theme_json, $expected_theme_json );
     2261        }
     2262
    21762263}
  • trunk/tests/phpunit/tests/theme/wpThemeJson.php

    r52434 r53129  
    424424                );
    425425
    426                 $expected = 'body { margin: 0; }body{--wp--style--block-gap: 1em;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }';
    427                 $this->assertSame( $expected, $theme_json->get_stylesheet() );
    428                 $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
     426                $expected = 'body { margin: 0; }body{--wp--style--block-gap: 1em;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
     427                $this->assertEquals( $expected, $theme_json->get_stylesheet() );
     428                $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
    429429        }
    430430
     
    554554
    555555                $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
    556                 $styles    = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
     556                $styles    = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
    557557                $presets   = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
    558558                $all       = $variables . $styles . $presets;
    559                 $this->assertSame( $all, $theme_json->get_stylesheet() );
    560                 $this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
    561                 $this->assertSame( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
    562                 $this->assertSame( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
     559                $this->assertEquals( $all, $theme_json->get_stylesheet() );
     560                $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
     561                $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
     562                $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
    563563        }
    564564
     
    23852385        }
    23862386
     2387        /**
     2388         * @ticket 55505
     2389         */
     2390        function test_export_data() {
     2391                $theme = new WP_Theme_JSON(
     2392                        array(
     2393                                'version'  => 2,
     2394                                'settings' => array(
     2395                                        'color' => array(
     2396                                                'palette' => array(
     2397                                                        array(
     2398                                                                'slug'  => 'white',
     2399                                                                'color' => 'white',
     2400                                                                'label' => 'White',
     2401                                                        ),
     2402                                                        array(
     2403                                                                'slug'  => 'black',
     2404                                                                'color' => 'black',
     2405                                                                'label' => 'Black',
     2406                                                        ),
     2407                                                ),
     2408                                        ),
     2409                                ),
     2410                        )
     2411                );
     2412                $user  = new WP_Theme_JSON(
     2413                        array(
     2414                                'version'  => 2,
     2415                                'settings' => array(
     2416                                        'color' => array(
     2417                                                'palette' => array(
     2418                                                        array(
     2419                                                                'slug'  => 'white',
     2420                                                                'color' => '#fff',
     2421                                                                'label' => 'User White',
     2422                                                        ),
     2423                                                        array(
     2424                                                                'slug'  => 'hotpink',
     2425                                                                'color' => 'hotpink',
     2426                                                                'label' => 'hotpink',
     2427                                                        ),
     2428                                                ),
     2429                                        ),
     2430                                ),
     2431                        ),
     2432                        'custom'
     2433                );
     2434
     2435                $theme->merge( $user );
     2436                $actual   = $theme->get_data();
     2437                $expected = array(
     2438                        'version'  => 2,
     2439                        'settings' => array(
     2440                                'color' => array(
     2441                                        'palette' => array(
     2442                                                array(
     2443                                                        'slug'  => 'white',
     2444                                                        'color' => '#fff',
     2445                                                        'label' => 'User White',
     2446                                                ),
     2447                                                array(
     2448                                                        'slug'  => 'black',
     2449                                                        'color' => 'black',
     2450                                                        'label' => 'Black',
     2451                                                ),
     2452                                                array(
     2453                                                        'slug'  => 'hotpink',
     2454                                                        'color' => 'hotpink',
     2455                                                        'label' => 'hotpink',
     2456                                                ),
     2457                                        ),
     2458                                ),
     2459                        ),
     2460                );
     2461
     2462                $this->assertEqualSetsWithIndex( $expected, $actual );
     2463        }
     2464
     2465        /**
     2466         * @ticket 55505
     2467         */
     2468        function test_export_data_deals_with_empty_user_data() {
     2469                $theme = new WP_Theme_JSON(
     2470                        array(
     2471                                'version'  => 2,
     2472                                'settings' => array(
     2473                                        'color' => array(
     2474                                                'palette' => array(
     2475                                                        array(
     2476                                                                'slug'  => 'white',
     2477                                                                'color' => 'white',
     2478                                                                'label' => 'White',
     2479                                                        ),
     2480                                                        array(
     2481                                                                'slug'  => 'black',
     2482                                                                'color' => 'black',
     2483                                                                'label' => 'Black',
     2484                                                        ),
     2485                                                ),
     2486                                        ),
     2487                                ),
     2488                        )
     2489                );
     2490
     2491                $actual   = $theme->get_data();
     2492                $expected = array(
     2493                        'version'  => 2,
     2494                        'settings' => array(
     2495                                'color' => array(
     2496                                        'palette' => array(
     2497                                                array(
     2498                                                        'slug'  => 'white',
     2499                                                        'color' => 'white',
     2500                                                        'label' => 'White',
     2501                                                ),
     2502                                                array(
     2503                                                        'slug'  => 'black',
     2504                                                        'color' => 'black',
     2505                                                        'label' => 'Black',
     2506                                                ),
     2507                                        ),
     2508                                ),
     2509                        ),
     2510                );
     2511
     2512                $this->assertEqualSetsWithIndex( $expected, $actual );
     2513        }
     2514
     2515        /**
     2516         * @ticket 55505
     2517         */
     2518        function test_export_data_deals_with_empty_theme_data() {
     2519                $user = new WP_Theme_JSON(
     2520                        array(
     2521                                'version'  => 2,
     2522                                'settings' => array(
     2523                                        'color' => array(
     2524                                                'palette' => array(
     2525                                                        array(
     2526                                                                'slug'  => 'white',
     2527                                                                'color' => '#fff',
     2528                                                                'label' => 'User White',
     2529                                                        ),
     2530                                                        array(
     2531                                                                'slug'  => 'hotpink',
     2532                                                                'color' => 'hotpink',
     2533                                                                'label' => 'hotpink',
     2534                                                        ),
     2535                                                ),
     2536                                        ),
     2537                                ),
     2538                        ),
     2539                        'custom'
     2540                );
     2541
     2542                $actual   = $user->get_data();
     2543                $expected = array(
     2544                        'version'  => 2,
     2545                        'settings' => array(
     2546                                'color' => array(
     2547                                        'palette' => array(
     2548                                                array(
     2549                                                        'slug'  => 'white',
     2550                                                        'color' => '#fff',
     2551                                                        'label' => 'User White',
     2552                                                ),
     2553                                                array(
     2554                                                        'slug'  => 'hotpink',
     2555                                                        'color' => 'hotpink',
     2556                                                        'label' => 'hotpink',
     2557                                                ),
     2558                                        ),
     2559                                ),
     2560                        ),
     2561                );
     2562
     2563                $this->assertEqualSetsWithIndex( $expected, $actual );
     2564        }
     2565
     2566        /**
     2567         * @ticket 55505
     2568         */
     2569        function test_export_data_deals_with_empty_data() {
     2570                $theme_v2    = new WP_Theme_JSON(
     2571                        array(
     2572                                'version' => 2,
     2573                        ),
     2574                        'theme'
     2575                );
     2576                $actual_v2   = $theme_v2->get_data();
     2577                $expected_v2 = array( 'version' => 2 );
     2578                $this->assertEqualSetsWithIndex( $expected_v2, $actual_v2 );
     2579
     2580                $theme_v1    = new WP_Theme_JSON(
     2581                        array(
     2582                                'version' => 1,
     2583                        ),
     2584                        'theme'
     2585                );
     2586                $actual_v1   = $theme_v1->get_data();
     2587                $expected_v1 = array( 'version' => 2 );
     2588                $this->assertEqualSetsWithIndex( $expected_v1, $actual_v1 );
     2589        }
     2590
     2591        /**
     2592         * @ticket 55505
     2593         */
     2594        function test_export_data_sets_appearance_tools() {
     2595                $theme = new WP_Theme_JSON(
     2596                        array(
     2597                                'version'  => 2,
     2598                                'settings' => array(
     2599                                        'appearanceTools' => true,
     2600                                        'blocks'          => array(
     2601                                                'core/paragraph' => array(
     2602                                                        'appearanceTools' => true,
     2603                                                ),
     2604                                        ),
     2605                                ),
     2606                        )
     2607                );
     2608
     2609                $actual   = $theme->get_data();
     2610                $expected = array(
     2611                        'version'  => 2,
     2612                        'settings' => array(
     2613                                'appearanceTools' => true,
     2614                                'blocks'          => array(
     2615                                        'core/paragraph' => array(
     2616                                                'appearanceTools' => true,
     2617                                        ),
     2618                                ),
     2619                        ),
     2620                );
     2621
     2622                $this->assertEqualSetsWithIndex( $expected, $actual );
     2623        }
    23872624}
Note: See TracChangeset for help on using the changeset viewer.