Make WordPress Core

Changeset 53129


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