diff --git a/docs/designers-developers/developers/block-api/block-deprecation.md b/docs/designers-developers/developers/block-api/block-deprecation.md
index ba1dd2f25b1f97..d598a157d4ab71 100644
--- a/docs/designers-developers/developers/block-api/block-deprecation.md
+++ b/docs/designers-developers/developers/block-api/block-deprecation.md
@@ -1,19 +1,47 @@
# Deprecated Blocks
-When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. In order to provide a good upgrade path, you can choose one of the following strategies:
+When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. To provide a good upgrade path, you can choose one of the following strategies:
- Do not deprecate the block and create a new one (a different name)
- Provide a "deprecated" version of the block allowing users opening these in the block editor to edit them using the updated block.
-A block can have several deprecated versions. A deprecation will be tried if a parsed block appears to be invalid, or if there is a deprecation defined for which its `isEligible` property function returns true.
+A block can have several deprecated versions. A deprecation will be tried if the current state of a parsed block is invalid, or if the deprecation defines an `isEligible` function that returns true.
+
+It is important to note that if a deprecation's `save` method does not produce a valid block then it is skipped, including its `migrate` method, even if `isEligible` would return true for the given attributes. This means that if you have several deprecations for a block and want to perform a new migration, like moving content to `InnerBlocks`, you may need to include the `migrate` method in multiple deprecations for it to be applied to all previous versions of the block.
+
+Deprecations do not operate as a chain of updates in the way other software data updates, like database migrations, do. At first glance, it is easy to think that each deprecation is going to make the required changes to the data and then hand this new form of the block onto the next deprecation to make its changes. What happens instead, is that each deprecation is passed the original saved content, and if its `save` method produces valid content the deprecation is used to parse the block attributes. If it has a `migrate` method it will also be run using the attributes parsed by the deprecation. The current block is updated with the migrated attributes and inner blocks before the current block's `save` function is run to generate new valid content for the block. At this point the current block should now be in a valid state.
+
+For blocks with multiple deprecations, it may be easier to save each deprecation to a constant with the version of the block it applies to, and then add each of these to the block's `deprecated` array. The deprecations in the array should be in reverse chronological order. This allows the block editor to attempt to apply the most recent and likely deprecations first, avoiding unnecessary and expensive processing.
+
+### Example:
+
+{% codetabs %}
+{% ESNext %}
+```js
+const v1 = {};
+const v2 = {};
+const v3 = {};
+const deprecated = [ v3, v2, v1 ];
+
+```
+{% ES5 %}
+```js
+var v1 = {};
+var v2 = {};
+var v3 = {};
+var deprecated = [ v3, v2, v1 ];
+```
+{% end %}
+
+It is also recommended to keep [fixtures](https://github.com/WordPress/gutenberg/blob/master/packages/e2e-tests/fixtures/blocks/README.md) which contain the different versions of the block content to allow you to easily test that new deprecations and migrations are working across all previous versions of the block.
Deprecations are defined on a block type as its `deprecated` property, an array of deprecation objects where each object takes the form:
- `attributes` (Object): The [attributes definition](/docs/designers-developers/developers/block-api/block-attributes.md) of the deprecated form of the block.
- `supports` (Object): The [supports definition](/docs/designers-developers/developers/block-api/block-registration.md) of the deprecated form of the block.
- `save` (Function): The [save implementation](/docs/designers-developers/developers/block-api/block-edit-save.md) of the deprecated form of the block.
-- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block.
-- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration even if the block is valid. This function is not called when the block is invalid. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks.
+- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block. As mentioned above, a deprecation's `migrate` will not be run if its `save` function does not return a valid block so you will need to make sure your migrations are available in all the deprecations where they are relevant.
+- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration even if the block is valid. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks. This function is not called when the results of all previous deprecations' `save` functions were invalid.
It's important to note that `attributes`, `supports`, and `save` are not automatically inherited from the current version, since they can impact parsing and serialization of a block, so they must be defined on the deprecated object in order to be processed during a migration.
diff --git a/docs/designers-developers/developers/data/data-core-edit-post.md b/docs/designers-developers/developers/data/data-core-edit-post.md
index 85e5d162c0bf97..03be0d1e9ff917 100644
--- a/docs/designers-developers/developers/data/data-core-edit-post.md
+++ b/docs/designers-developers/developers/data/data-core-edit-post.md
@@ -372,10 +372,6 @@ _Returns_
Returns an action object used to request meta box update.
-_Returns_
-
-- `Object`: Action object.
-
# **setAvailableMetaBoxesPerLocation**
Returns an action object used in signaling
@@ -385,10 +381,6 @@ _Parameters_
- _metaBoxesPerLocation_ `Object`: Meta boxes per location.
-_Returns_
-
-- `Object`: Action object.
-
# **setIsInserterOpened**
Returns an action object used to open/close the inserter.
diff --git a/docs/designers-developers/developers/slotfills/main-dashboard-button.md b/docs/designers-developers/developers/slotfills/main-dashboard-button.md
index 43b3730a35bcf4..3392eed131218f 100644
--- a/docs/designers-developers/developers/slotfills/main-dashboard-button.md
+++ b/docs/designers-developers/developers/slotfills/main-dashboard-button.md
@@ -1,18 +1,19 @@
# MainDashboardButton
-This slot allows replacing the default main dashboard button in the post editor
-that's used for closing the editor in fullscreen mode. In the site editor this slot
-refers to the "back to dashboard" button in the navigation sidebar.
+This slot allows replacing the default main dashboard button in the post editor and site editor.
+It's used for returning back to main wp-admin dashboard when editor is in fullscreen mode.
## Examples
-Basic usage:
+### Post editor example
+
+This will override the W icon button in the header.
```js
import { registerPlugin } from '@wordpress/plugins';
import {
__experimentalMainDashboardButton as MainDashboardButton,
-} from '@wordpress/interface';
+} from '@wordpress/edit-post';
const MainDashboardButtonTest = () => (
@@ -32,16 +33,14 @@ the post editor, that can be achieved in the following way:
import { registerPlugin } from '@wordpress/plugins';
import {
__experimentalFullscreenModeClose as FullscreenModeClose,
-} from '@wordpress/edit-post';
-import {
__experimentalMainDashboardButton as MainDashboardButton,
-} from '@wordpress/interface';
+} from '@wordpress/edit-post';
import { close } from '@wordpress/icons';
const MainDashboardButtonIconTest = () => (
-
+
);
@@ -50,13 +49,15 @@ registerPlugin( 'main-dashboard-button-icon-test', {
} );
```
-Site editor example:
+### Site editor example
+
+In the site editor this slot refers to the "back to dashboard" button in the navigation sidebar.
```js
import { registerPlugin } from '@wordpress/plugins';
import {
__experimentalMainDashboardButton as MainDashboardButton,
-} from '@wordpress/interface';
+} from '@wordpress/edit-site';
import {
__experimentalNavigationBackButton as NavigationBackButton,
} from '@wordpress/components';
diff --git a/docs/designers-developers/developers/themes/theme-json.md b/docs/designers-developers/developers/themes/theme-json.md
index 86fbfc115716a9..35918fc1ec343c 100644
--- a/docs/designers-developers/developers/themes/theme-json.md
+++ b/docs/designers-developers/developers/themes/theme-json.md
@@ -97,6 +97,7 @@ The settings section has the following structure and default values:
"dropCap": true, /* false to opt-out */
"fontFamilies": [ ... ], /* font family presets */
"fontSizes": [ ... ], /* font size presets, as in add_theme_support('editor-font-sizes', ... ) */
+ "fontStyles": [ ... ], /* font style presets */
"fontWeights": [ ... ], /* font weight presets */
"textDecorations": [ ... ], /* text decoration presets */
"textTransforms": [ ... ] /* text transform presets */
@@ -349,6 +350,7 @@ These are the current typography properties supported by blocks:
| Context | Font Family | Font Size | Font Style | Font Weight | Line Height | Text Decoration | Text Transform |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Global | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+| Code | - | Yes | - | - | - | - | - |
| Heading [1] | - | Yes | - | - | Yes | - | - |
| List | - | Yes | - | - | - | - | - |
| Navigation | Yes | Yes | Yes | Yes | - | Yes | Yes |
diff --git a/docs/designers-developers/developers/tutorials/block-based-themes/README.md b/docs/designers-developers/developers/tutorials/block-based-themes/README.md
index 1b13d6214f2331..a701cd46228814 100644
--- a/docs/designers-developers/developers/tutorials/block-based-themes/README.md
+++ b/docs/designers-developers/developers/tutorials/block-based-themes/README.md
@@ -15,7 +15,7 @@ This tutorial is up to date as of Gutenberg version 9.1.
1. [What is needed to create a block-based theme?](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#what-is-needed-to-create-a-block-based-theme)
2. [Creating the theme](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#creating-the-theme)
3. [Creating the templates and template parts](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#creating-the-templates-and-template-parts)
- 4. [Experimental-theme.json -Global styles](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#experimental-theme-json-global-styles)
+ 4. [Experimental-theme.json - Global styles](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#experimental-theme-json-global-styles)
5. [Adding blocks](/docs/designers-developers/developers/tutorials/block-based-themes/block-based-themes-2-adding-blocks.md)
## What is needed to create a block-based theme?
@@ -34,7 +34,7 @@ A block based theme requires an `index.php` file, an index template file, a `sty
The theme may optionally include an [experimental-theme.json file](/docs/designers-developers/developers/themes/theme-json.md) to manage global styles. You decide what additional templates and template parts to include in your theme.
-Templates are placed inside the block-templates folder, and template parts are placed inside the block-template-parts folder:
+Templates are placed inside the `block-templates` folder, and template parts are placed inside the `block-template-parts` folder:
```
theme
@@ -57,7 +57,7 @@ theme
## Creating the theme
Create a new folder for your theme in `/wp-content/themes/`.
-Inside this folder, create the block-templates and block-template-parts folders.
+Inside this folder, create the `block-templates` and `block-template-parts` folders.
Create a `style.css` file. The file header in the `style.css` file has [the same items that you would use in a traditional theme](https://developer.wordpress.org/themes/basics/main-stylesheet-style-css/#explanations).
@@ -166,7 +166,7 @@ theme
### Creating the templates and template parts
-Create two template parts called `footer.html` and `header.html` and place them inside the block-template-parts folder. You can leave the files empty for now.
+Create two template parts called `footer.html` and `header.html` and place them inside the `block-template-parts` folder. You can leave the files empty for now.
Inside the block-templates folder, create an `index.html` file.
@@ -318,7 +318,7 @@ Below are the presets and styles combined:
```
{
"global": {
- "setttings": {
+ "settings": {
"color": {
"palette": [
{
diff --git a/docs/manifest.json b/docs/manifest.json
index dee68b8b1e8d36..5f856ce44bef88 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -191,6 +191,12 @@
"markdown_source": "../docs/designers-developers/developers/slotfills/README.md",
"parent": "developers"
},
+ {
+ "title": "MainDashboardButton",
+ "slug": "main-dashboard-button",
+ "markdown_source": "../docs/designers-developers/developers/slotfills/main-dashboard-button.md",
+ "parent": "slotfills"
+ },
{
"title": "PluginBlockSettingsMenuItem",
"slug": "plugin-block-settings-menu-item",
diff --git a/docs/toc.json b/docs/toc.json
index ff54916e323e32..08ef2cb73f2a25 100644
--- a/docs/toc.json
+++ b/docs/toc.json
@@ -35,6 +35,7 @@
{ "docs/designers-developers/developers/filters/autocomplete-filters.md": [] }
] },
{"docs/designers-developers/developers/slotfills/README.md": [
+ { "docs/designers-developers/developers/slotfills/main-dashboard-button.md": [] },
{ "docs/designers-developers/developers/slotfills/plugin-block-settings-menu-item.md": [] },
{ "docs/designers-developers/developers/slotfills/plugin-document-setting-panel.md": [] },
{ "docs/designers-developers/developers/slotfills/plugin-more-menu-item.md": [] },
diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php
index d797bdc21bac41..29454c43296fd1 100644
--- a/lib/block-supports/typography.php
+++ b/lib/block-supports/typography.php
@@ -15,12 +15,19 @@ function gutenberg_register_typography_support( $block_type ) {
return;
}
- $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false );
$has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false );
+ $has_font_style_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontStyle' ), false );
+ $has_font_weight_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontWeight' ), false );
$has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false );
- $has_typography_support = $has_font_appearance_support || $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support;
+
+ $has_typography_support = $has_font_size_support
+ || $has_font_weight_support
+ || $has_font_style_support
+ || $has_line_height_support
+ || $has_text_transform_support
+ || $has_text_decoration_support;
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
@@ -57,8 +64,9 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
$classes = array();
$styles = array();
- $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false );
$has_font_family_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontFamily' ), false );
+ $has_font_style_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontStyle' ), false );
+ $has_font_weight_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontWeight' ), false );
$has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false );
$has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
@@ -94,14 +102,17 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
}
}
- // Font appearance - style and weight.
- if ( $has_font_appearance_support ) {
+ // Font style.
+ if ( $has_font_style_support ) {
// Apply font style.
$font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' );
if ( $font_style ) {
$styles[] = $font_style;
}
+ }
+ // Font weight.
+ if ( $has_font_weight_support ) {
// Apply font weight.
$font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' );
if ( $font_weight ) {
diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php
new file mode 100644
index 00000000000000..76a7507f275362
--- /dev/null
+++ b/lib/class-wp-theme-json-resolver.php
@@ -0,0 +1,383 @@
+ __( 'Black', 'gutenberg' ),
+ 'cyan-bluish-gray' => __( 'Cyan bluish gray', 'gutenberg' ),
+ 'white' => __( 'White', 'gutenberg' ),
+ 'pale-pink' => __( 'Pale pink', 'gutenberg' ),
+ 'vivid-red' => __( 'Vivid red', 'gutenberg' ),
+ 'luminous-vivid-orange' => __( 'Luminous vivid orange', 'gutenberg' ),
+ 'luminous-vivid-amber' => __( 'Luminous vivid amber', 'gutenberg' ),
+ 'light-green-cyan' => __( 'Light green cyan', 'gutenberg' ),
+ 'vivid-green-cyan' => __( 'Vivid green cyan', 'gutenberg' ),
+ 'pale-cyan-blue' => __( 'Pale cyan blue', 'gutenberg' ),
+ 'vivid-cyan-blue' => __( 'Vivid cyan blue', 'gutenberg' ),
+ 'vivid-purple' => __( 'Vivid purple', 'gutenberg' ),
+ );
+ if ( ! empty( $config['global']['settings']['color']['palette'] ) ) {
+ foreach ( $config['global']['settings']['color']['palette'] as &$color ) {
+ $color['name'] = $default_colors_i18n[ $color['slug'] ];
+ }
+ }
+
+ $default_gradients_i18n = array(
+ 'vivid-cyan-blue-to-vivid-purple' => __( 'Vivid cyan blue to vivid purple', 'gutenberg' ),
+ 'light-green-cyan-to-vivid-green-cyan' => __( 'Light green cyan to vivid green cyan', 'gutenberg' ),
+ 'luminous-vivid-amber-to-luminous-vivid-orange' => __( 'Luminous vivid amber to luminous vivid orange', 'gutenberg' ),
+ 'luminous-vivid-orange-to-vivid-red' => __( 'Luminous vivid orange to vivid red', 'gutenberg' ),
+ 'very-light-gray-to-cyan-bluish-gray' => __( 'Very light gray to cyan bluish gray', 'gutenberg' ),
+ 'cool-to-warm-spectrum' => __( 'Cool to warm spectrum', 'gutenberg' ),
+ 'blush-light-purple' => __( 'Blush light purple', 'gutenberg' ),
+ 'blush-bordeaux' => __( 'Blush bordeaux', 'gutenberg' ),
+ 'luminous-dusk' => __( 'Luminous dusk', 'gutenberg' ),
+ 'pale-ocean' => __( 'Pale ocean', 'gutenberg' ),
+ 'electric-grass' => __( 'Electric grass', 'gutenberg' ),
+ 'midnight' => __( 'Midnight', 'gutenberg' ),
+ );
+ if ( ! empty( $config['global']['settings']['color']['gradients'] ) ) {
+ foreach ( $config['global']['settings']['color']['gradients'] as &$gradient ) {
+ $gradient['name'] = $default_gradients_i18n[ $gradient['slug'] ];
+ }
+ }
+
+ $default_font_sizes_i18n = array(
+ 'small' => __( 'Small', 'gutenberg' ),
+ 'normal' => __( 'Normal', 'gutenberg' ),
+ 'medium' => __( 'Medium', 'gutenberg' ),
+ 'large' => __( 'Large', 'gutenberg' ),
+ 'huge' => __( 'Huge', 'gutenberg' ),
+ );
+ if ( ! empty( $config['global']['settings']['typography']['fontSizes'] ) ) {
+ foreach ( $config['global']['settings']['typography']['fontSizes'] as &$font_size ) {
+ $font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ];
+ }
+ }
+
+ $default_font_styles_i18n = array(
+ 'normal' => __( 'Regular', 'gutenberg' ),
+ 'italic' => __( 'Italic', 'gutenberg' ),
+ 'initial' => __( 'Initial', 'gutenberg' ),
+ 'inherit' => __( 'Inherit', 'gutenberg' ),
+ );
+ if ( ! empty( $config['global']['settings']['typography']['fontStyles'] ) ) {
+ foreach ( $config['global']['settings']['typography']['fontStyles'] as &$font_style ) {
+ $font_style['name'] = $default_font_styles_i18n[ $font_style['slug'] ];
+ }
+ }
+
+ $default_font_weights_i18n = array(
+ '100' => __( 'Ultralight', 'gutenberg' ),
+ '200' => __( 'Thin', 'gutenberg' ),
+ '300' => __( 'Light', 'gutenberg' ),
+ '400' => __( 'Regular', 'gutenberg' ),
+ '500' => __( 'Medium', 'gutenberg' ),
+ '600' => __( 'Semibold', 'gutenberg' ),
+ '700' => __( 'Bold', 'gutenberg' ),
+ '800' => __( 'Heavy', 'gutenberg' ),
+ '900' => __( 'Black', 'gutenberg' ),
+ 'initial' => __( 'Initial', 'gutenberg' ),
+ 'inherit' => __( 'Inherit', 'gutenberg' ),
+ );
+ if ( ! empty( $config['global']['settings']['typography']['fontWeights'] ) ) {
+ foreach ( $config['global']['settings']['typography']['fontWeights'] as &$font_weight ) {
+ $font_weight['name'] = $default_font_weights_i18n[ $font_weight['slug'] ];
+ }
+ }
+ // End i18n logic to remove when JSON i18 strings are extracted.
+
+ self::$core = new WP_Theme_JSON( $config );
+
+ return self::$core;
+ }
+
+ /**
+ * Returns the theme's origin config.
+ *
+ * It uses the theme support data if
+ * the theme hasn't declared any via theme.json.
+ *
+ * @param array $theme_support_data Theme support data in theme.json format.
+ *
+ * @return WP_Theme_JSON Entity that holds theme data.
+ */
+ private function get_theme_origin( $theme_support_data = array() ) {
+ $theme_json_data = self::get_from_file( locate_template( 'experimental-theme.json' ) );
+
+ /*
+ * We want the presets and settings declared in theme.json
+ * to override the ones declared via add_theme_support.
+ */
+ $this->theme = new WP_Theme_JSON( $theme_support_data );
+ $this->theme->merge( new WP_Theme_JSON( $theme_json_data ) );
+
+ return $this->theme;
+ }
+
+ /**
+ * Returns the CPT that contains the user's origin config
+ * for the current theme or a void array if none found.
+ *
+ * It can also create and return a new draft CPT.
+ *
+ * @param bool $should_create_cpt Whether a new CPT should be created if no one was found.
+ * False by default.
+ * @param array $post_status_filter Filter CPT by post status.
+ * ['publish'] by default, so it only fetches published posts.
+ *
+ * @return array Custom Post Type for the user's origin config.
+ */
+ private static function get_user_data_from_custom_post_type( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) {
+ $user_cpt = array();
+ $post_type_filter = 'wp_global_styles';
+ $post_name_filter = 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() );
+ $recent_posts = wp_get_recent_posts(
+ array(
+ 'numberposts' => 1,
+ 'orderby' => 'date',
+ 'order' => 'desc',
+ 'post_type' => $post_type_filter,
+ 'post_status' => $post_status_filter,
+ 'name' => $post_name_filter,
+ )
+ );
+
+ if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
+ $user_cpt = $recent_posts[0];
+ } elseif ( $should_create_cpt ) {
+ $cpt_post_id = wp_insert_post(
+ array(
+ 'post_content' => '{}',
+ 'post_status' => 'publish',
+ 'post_type' => $post_type_filter,
+ 'post_name' => $post_name_filter,
+ ),
+ true
+ );
+ $user_cpt = get_post( $cpt_post_id, ARRAY_A );
+ }
+
+ return $user_cpt;
+ }
+
+ /**
+ * Returns the user's origin config.
+ *
+ * @return WP_Theme_JSON Entity that holds user data.
+ */
+ private static function get_user_origin() {
+ if ( null !== self::$user ) {
+ return self::$user;
+ }
+
+ $config = array();
+ $user_cpt = self::get_user_data_from_custom_post_type();
+ if ( array_key_exists( 'post_content', $user_cpt ) ) {
+ $decoded_data = json_decode( $user_cpt['post_content'], true );
+
+ $json_decoding_error = json_last_error();
+ if ( JSON_ERROR_NONE !== $json_decoding_error ) {
+ error_log( 'Error when decoding user schema: ' . json_last_error_msg() );
+ return $config;
+ }
+
+ if ( is_array( $decoded_data ) ) {
+ $config = $decoded_data;
+ }
+ }
+ self::$user = new WP_Theme_JSON( $config );
+
+ return self::$user;
+ }
+
+ /**
+ * There are three sources of data for a site:
+ * core, theme, and user.
+ *
+ * The main function of the resolver is to
+ * merge all this data following this algorithm:
+ * theme overrides core, and user overrides
+ * data coming from either theme or core.
+ *
+ * user data > theme data > core data
+ *
+ * The main use case for the resolver is to return
+ * the merged data up to the user level.However,
+ * there are situations in which we need the
+ * data merged up to a different level (theme)
+ * or no merged at all.
+ *
+ * @param array $theme_support_data Existing block editor settings.
+ * Empty array by default.
+ * @param string $origin The source of data the consumer wants.
+ * Valid values are 'core', 'theme', 'user'.
+ * Default is 'user'.
+ * @param boolean $merged Whether the data should be merged
+ * with the previous origins (the default).
+ *
+ * @return WP_Theme_JSON
+ */
+ public function get_origin( $theme_support_data = array(), $origin = 'user', $merged = true ) {
+
+ if ( ( 'user' === $origin ) && $merged ) {
+ $result = new WP_Theme_JSON();
+ $result->merge( self::get_core_origin() );
+ $result->merge( $this->get_theme_origin( $theme_support_data ) );
+ $result->merge( self::get_user_origin() );
+ return $result;
+ }
+
+ if ( ( 'theme' === $origin ) && $merged ) {
+ $result = new WP_Theme_JSON();
+ $result->merge( self::get_core_origin() );
+ $result->merge( $this->get_theme_origin( $theme_support_data ) );
+ return $result;
+ }
+
+ if ( 'user' === $origin ) {
+ return self::get_user_origin();
+ }
+
+ if ( 'theme' === $origin ) {
+ return $this->get_theme_origin( $theme_support_data );
+ }
+
+ return self::get_core_origin();
+ }
+
+ /**
+ * Registers a Custom Post Type to store the user's origin config.
+ */
+ public static function register_user_custom_post_type() {
+ if ( ! gutenberg_experimental_global_styles_has_theme_json_support() ) {
+ return;
+ }
+
+ $args = array(
+ 'label' => __( 'Global Styles', 'gutenberg' ),
+ 'description' => 'CPT to store user design tokens',
+ 'public' => false,
+ 'show_ui' => false,
+ 'show_in_rest' => true,
+ 'rest_base' => '__experimental/global-styles',
+ 'capabilities' => array(
+ 'read' => 'edit_theme_options',
+ 'create_posts' => 'edit_theme_options',
+ 'edit_posts' => 'edit_theme_options',
+ 'edit_published_posts' => 'edit_theme_options',
+ 'delete_published_posts' => 'edit_theme_options',
+ 'edit_others_posts' => 'edit_theme_options',
+ 'delete_others_posts' => 'edit_theme_options',
+ ),
+ 'map_meta_cap' => true,
+ 'supports' => array(
+ 'editor',
+ 'revisions',
+ ),
+ );
+ register_post_type( 'wp_global_styles', $args );
+ }
+
+ /**
+ * Returns the ID of the custom post type
+ * that stores user data.
+ *
+ * @return integer
+ */
+ public static function get_user_custom_post_type_id() {
+ if ( null !== self::$user_custom_post_type_id ) {
+ return self::$user_custom_post_type_id;
+ }
+
+ $user_cpt = self::get_user_data_from_custom_post_type( true );
+ if ( array_key_exists( 'ID', $user_cpt ) ) {
+ self::$user_custom_post_type_id = $user_cpt['ID'];
+ }
+
+ return self::$user_custom_post_type_id;
+ }
+
+}
diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php
index 0452510d306c6c..ea66bc5283a98a 100644
--- a/lib/class-wp-theme-json.php
+++ b/lib/class-wp-theme-json.php
@@ -221,7 +221,7 @@ class WP_Theme_JSON {
),
array(
'path' => array( 'settings', 'typography', 'fontStyles' ),
- 'value_key' => 'slug',
+ 'value_key' => 'value',
'css_var_infix' => 'font-style',
'classes' => array(
array(
@@ -232,7 +232,7 @@ class WP_Theme_JSON {
),
array(
'path' => array( 'settings', 'typography', 'fontWeights' ),
- 'value_key' => 'slug',
+ 'value_key' => 'value',
'css_var_infix' => 'font-weight',
'classes' => array(
array(
@@ -300,11 +300,11 @@ class WP_Theme_JSON {
),
'fontStyle' => array(
'value' => array( 'typography', 'fontStyle' ),
- 'support' => array( '__experimentalFontAppearance' ),
+ 'support' => array( '__experimentalFontStyle' ),
),
'fontWeight' => array(
'value' => array( 'typography', 'fontWeight' ),
- 'support' => array( '__experimentalFontAppearance' ),
+ 'support' => array( '__experimentalFontWeight' ),
),
'lineHeight' => array(
'value' => array( 'typography', 'lineHeight' ),
@@ -790,6 +790,9 @@ private static function compute_theme_vars( &$declarations, $context ) {
* @return string CSS ruleset.
*/
private static function to_ruleset( $selector, $declarations ) {
+ if ( empty( $declarations ) ) {
+ return '';
+ }
$ruleset = '';
if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
@@ -816,17 +819,49 @@ function ( $carry, $element ) {
/**
* Converts each context into a list of rulesets
* to be appended to the stylesheet.
+ * These rulesets contain all the css variables (custom variables and preset variables).
*
* See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax
*
* For each context this creates a new ruleset such as:
*
* context-selector {
- * style-property-one: value;
* --wp--preset--category--slug: value;
* --wp--custom--variable: value;
* }
*
+ * @param string $stylesheet Stylesheet to append new rules to.
+ * @param array $context Context to be processed.
+ *
+ * @return string The new stylesheet.
+ */
+ private static function to_css_variables( $stylesheet, $context ) {
+ if ( empty( $context['selector'] ) ) {
+ return $stylesheet;
+ }
+
+ $declarations = array();
+ self::compute_preset_vars( $declarations, $context );
+ self::compute_theme_vars( $declarations, $context );
+
+ // Attach the ruleset for style and custom properties.
+ $stylesheet .= self::to_ruleset( $context['selector'], $declarations );
+
+ return $stylesheet;
+ }
+
+ /**
+ * Converts each context into a list of rulesets
+ * containing the block styles to be appended to the stylesheet.
+ *
+ * See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax
+ *
+ * For each context this creates a new ruleset such as:
+ *
+ * context-selector {
+ * style-property-one: value;
+ * }
+ *
* Additionally, it'll also create new rulesets
* as classes for each preset value such as:
*
@@ -846,29 +881,23 @@ function ( $carry, $element ) {
* background: value;
* }
*
+ * p.has-value-gradient-background {
+ * background: value;
+ * }
+ *
* @param string $stylesheet Stylesheet to append new rules to.
* @param array $context Context to be processed.
*
* @return string The new stylesheet.
*/
- private static function to_stylesheet( $stylesheet, $context ) {
+ private static function to_block_styles( $stylesheet, $context ) {
if ( empty( $context['selector'] ) ) {
- return '';
+ return $stylesheet;
}
$declarations = array();
self::compute_style_properties( $declarations, $context );
- self::compute_preset_vars( $declarations, $context );
- self::compute_theme_vars( $declarations, $context );
- // If there are no declarations at this point,
- // it won't have any preset classes either,
- // so bail out earlier.
- if ( empty( $declarations ) ) {
- return '';
- }
-
- // Attach the ruleset for style and custom properties.
$stylesheet .= self::to_ruleset( $context['selector'], $declarations );
// Attach the rulesets for the classes.
@@ -910,10 +939,18 @@ function ( $element ) {
* Returns the stylesheet that results of processing
* the theme.json structure this object represents.
*
+ * @param string $type Type of stylesheet we want accepts 'all', 'block_styles', and 'css_variables'.
* @return string Stylesheet.
*/
- public function get_stylesheet() {
- return array_reduce( $this->contexts, array( $this, 'to_stylesheet' ), '' );
+ public function get_stylesheet( $type = 'all' ) {
+ switch ( $type ) {
+ case 'block_styles':
+ return array_reduce( $this->contexts, array( $this, 'to_block_styles' ), '' );
+ case 'css_variables':
+ return array_reduce( $this->contexts, array( $this, 'to_css_variables' ), '' );
+ default:
+ return array_reduce( $this->contexts, array( $this, 'to_css_variables' ), '' ) . array_reduce( $this->contexts, array( $this, 'to_block_styles' ), '' );
+ }
}
/**
diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php
index 891a39bf47178b..46c5995d67b805 100644
--- a/lib/edit-site-page.php
+++ b/lib/edit-site-page.php
@@ -182,7 +182,7 @@ function gutenberg_edit_site_init( $hook ) {
*/
function register_site_editor_homepage_settings() {
register_setting(
- 'general',
+ 'reading',
'show_on_front',
array(
'show_in_rest' => true,
@@ -192,7 +192,7 @@ function register_site_editor_homepage_settings() {
);
register_setting(
- 'general',
+ 'reading',
'page_on_front',
array(
'show_in_rest' => true,
diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json
index 7b35275d9af036..82c1bccf28e738 100644
--- a/lib/experimental-default-theme.json
+++ b/lib/experimental-default-theme.json
@@ -164,49 +164,60 @@
"fontStyles": [
{
"name": "Regular",
- "slug": "normal"
+ "slug": "normal",
+ "value": "normal"
},
{
"name": "Italic",
- "slug": "italic"
+ "slug": "italic",
+ "value": "italic"
}
],
"fontWeights": [
{
"name": "Ultralight",
- "slug": "100"
+ "slug": "100",
+ "value": "100"
},
{
"name": "Thin",
- "slug": "200"
+ "slug": "200",
+ "value": "200"
},
{
"name": "Light",
- "slug": "300"
+ "slug": "300",
+ "value": "300"
},
{
"name": "Regular",
- "slug": "400"
+ "slug": "400",
+ "value": "400"
},
{
"name": "Medium",
- "slug": "500"
+ "slug": "500",
+ "value": "500"
},
{
"name": "Semibold",
- "slug": "600"
+ "slug": "600",
+ "value": "600"
},
{
"name": "Bold",
- "slug": "700"
+ "slug": "700",
+ "value": "700"
},
{
"name": "Heavy",
- "slug": "800"
+ "slug": "800",
+ "value": "800"
},
{
"name": "Black",
- "slug": "900"
+ "slug": "900",
+ "value": "900"
}
],
"textTransforms": [
diff --git a/lib/full-site-editing/default-template-types.php b/lib/full-site-editing/default-template-types.php
index df70c1bd15865d..54cc722be96f37 100644
--- a/lib/full-site-editing/default-template-types.php
+++ b/lib/full-site-editing/default-template-types.php
@@ -14,68 +14,72 @@
function gutenberg_get_default_template_types() {
$default_template_types = array(
'index' => array(
- 'title' => _x( 'Default (Index)', 'Template name', 'gutenberg' ),
- 'description' => __( 'Main template, applied when no other template is found', 'gutenberg' ),
+ 'title' => _x( 'Index', 'Template name', 'gutenberg' ),
+ 'description' => __( 'The default template which is used when no other template can be found', 'gutenberg' ),
),
'home' => array(
'title' => _x( 'Home', 'Template name', 'gutenberg' ),
- 'description' => __( 'Template for the latest blog posts', 'gutenberg' ),
+ 'description' => __( 'The home page template, which is the front page by default. If you use a static front page this is the template for the page with the latest posts', 'gutenberg' ),
),
'front-page' => array(
'title' => _x( 'Front Page', 'Template name', 'gutenberg' ),
- 'description' => __( 'Front page template, whether it displays the blog posts index or a static page', 'gutenberg' ),
+ 'description' => __( 'Used when the site home page is queried', 'gutenberg' ),
),
'singular' => array(
- 'title' => _x( 'Default Singular', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays any content on a single page', 'gutenberg' ),
+ 'title' => _x( 'Singular', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a single entry is queried. This template will be overridden the Single, Post, and Page templates where appropriate', 'gutenberg' ),
),
'single' => array(
- 'title' => _x( 'Default Single', 'Template name', 'gutenberg' ),
- 'description' => __( 'Applied to individual content like a blog post', 'gutenberg' ),
+ 'title' => _x( 'Single', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a single entry that is not a Page is queried', 'gutenberg' ),
+ ),
+ 'single-post' => array(
+ 'title' => _x( 'Post', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a single Post is queried', 'gutenberg' ),
),
'page' => array(
- 'title' => _x( 'Default Page', 'Template name', 'gutenberg' ),
- 'description' => __( 'Applied to individual pages', 'gutenberg' ),
+ 'title' => _x( 'Page', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when an individual Page is queried', 'gutenberg' ),
),
'archive' => array(
- 'title' => _x( 'Default Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Applied to archives like your posts page, categories, or tags', 'gutenberg' ),
+ 'title' => _x( 'Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when multiple entries are queried. This template will be overridden the Category, Author, and Date templates where appropriate', 'gutenberg' ),
),
'author' => array(
- 'title' => _x( 'Default Author Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a list of posts by a single author', 'gutenberg' ),
+ 'title' => _x( 'Author Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a list of Posts from a single author is queried', 'gutenberg' ),
),
'category' => array(
- 'title' => _x( 'Default Post Category Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a list of posts in a category', 'gutenberg' ),
+ 'title' => _x( 'Post Category Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a list of Posts from a category is queried', 'gutenberg' ),
),
'taxonomy' => array(
- 'title' => _x( 'Default Taxonomy Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a list of posts in a taxonomy', 'gutenberg' ),
+ 'title' => _x( 'Taxonomy Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a list of posts from a taxonomy is queried', 'gutenberg' ),
),
'date' => array(
- 'title' => _x( 'Default Date Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a list of posts in a date range', 'gutenberg' ),
+ 'title' => _x( 'Date Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a list of Posts from a certain date are queried', 'gutenberg' ),
),
'tag' => array(
- 'title' => _x( 'Default Tag Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a list of posts with a tag', 'gutenberg' ),
+ 'title' => _x( 'Tag Archive', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a list of Posts with a certain tag is queried', 'gutenberg' ),
),
'attachment' => array(
'title' => __( 'Media', 'gutenberg' ),
- 'description' => __( 'Displays media content', 'gutenberg' ),
+ 'description' => __( 'Used when a Media entry is queried', 'gutenberg' ),
),
'search' => array(
- 'title' => __( 'Search Results', 'gutenberg' ),
- 'description' => __( 'Applied to search results', 'gutenberg' ),
+ 'title' => _x( 'Search Results', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when a visitor searches the site', 'gutenberg' ),
),
'privacy-policy' => array(
'title' => __( 'Privacy Policy', 'gutenberg' ),
'description' => '',
),
'404' => array(
- 'title' => _x( '404 (Not Found)', 'Template name', 'gutenberg' ),
- 'description' => __( 'Applied when content cannot be found', 'gutenberg' ),
+ 'title' => _x( '404', 'Template name', 'gutenberg' ),
+ 'description' => __( 'Used when the queried content cannot be found', 'gutenberg' ),
),
);
diff --git a/lib/full-site-editing/templates-utils.php b/lib/full-site-editing/templates-utils.php
index 62fb7f6da83bbc..5430470cfa640a 100644
--- a/lib/full-site-editing/templates-utils.php
+++ b/lib/full-site-editing/templates-utils.php
@@ -53,7 +53,10 @@ function gutenberg_render_templates_lists_custom_column( $column_name, $post_id
}
if ( 'theme' === $column_name ) {
- $terms = get_the_terms( $post_id, 'wp_theme' );
+ $terms = get_the_terms( $post_id, 'wp_theme' );
+ if ( empty( $terms ) || is_wp_error( $terms ) ) {
+ return;
+ }
$themes = array();
$is_file_based = false;
foreach ( $terms as $term ) {
@@ -77,7 +80,7 @@ function gutenberg_render_templates_lists_custom_column( $column_name, $post_id
* @param array $views The edit views to filter.
*/
function gutenberg_filter_templates_edit_views( $views ) {
- $post_type = get_current_screen()->post_type;
+ $post_type = get_current_screen()->post_type;
$url = add_query_arg(
array(
'post_type' => $post_type,
diff --git a/lib/global-styles.php b/lib/global-styles.php
index 9ec1d698da1ff9..6c62d867fc967d 100644
--- a/lib/global-styles.php
+++ b/lib/global-styles.php
@@ -14,218 +14,6 @@ function gutenberg_experimental_global_styles_has_theme_json_support() {
return is_readable( locate_template( 'experimental-theme.json' ) );
}
-/**
- * Processes a file that adheres to the theme.json
- * schema and returns an array with its contents,
- * or a void array if none found.
- *
- * @param string $file_path Path to file.
- * @return array Contents that adhere to the theme.json schema.
- */
-function gutenberg_experimental_global_styles_get_from_file( $file_path ) {
- $config = array();
- if ( file_exists( $file_path ) ) {
- $decoded_file = json_decode(
- file_get_contents( $file_path ),
- true
- );
-
- $json_decoding_error = json_last_error();
- if ( JSON_ERROR_NONE !== $json_decoding_error ) {
- error_log( 'Error when decoding file schema: ' . json_last_error_msg() );
- return $config;
- }
-
- if ( is_array( $decoded_file ) ) {
- $config = $decoded_file;
- }
- }
- return $config;
-}
-
-/**
- * Returns the user's origin config.
- *
- * @return WP_Theme_JSON Entity that holds user data.
- */
-function gutenberg_experimental_global_styles_get_user() {
- $config = array();
- $user_cpt = gutenberg_experimental_global_styles_get_user_cpt();
- if ( array_key_exists( 'post_content', $user_cpt ) ) {
- $decoded_data = json_decode( $user_cpt['post_content'], true );
-
- $json_decoding_error = json_last_error();
- if ( JSON_ERROR_NONE !== $json_decoding_error ) {
- error_log( 'Error when decoding user schema: ' . json_last_error_msg() );
- return $config;
- }
-
- if ( is_array( $decoded_data ) ) {
- $config = $decoded_data;
- }
- }
-
- return new WP_Theme_JSON( $config );
-}
-
-/**
- * Returns the CPT that contains the user's origin config
- * for the current theme or a void array if none found.
- *
- * It can also create and return a new draft CPT.
- *
- * @param bool $should_create_cpt Whether a new CPT should be created if no one was found.
- * False by default.
- * @param array $post_status_filter Filter CPT by post status.
- * ['publish'] by default, so it only fetches published posts.
- * @return array Custom Post Type for the user's origin config.
- */
-function gutenberg_experimental_global_styles_get_user_cpt( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) {
- $user_cpt = array();
- $post_type_filter = 'wp_global_styles';
- $post_name_filter = 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() );
- $recent_posts = wp_get_recent_posts(
- array(
- 'numberposts' => 1,
- 'orderby' => 'date',
- 'order' => 'desc',
- 'post_type' => $post_type_filter,
- 'post_status' => $post_status_filter,
- 'name' => $post_name_filter,
- )
- );
-
- if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
- $user_cpt = $recent_posts[0];
- } elseif ( $should_create_cpt ) {
- $cpt_post_id = wp_insert_post(
- array(
- 'post_content' => '{}',
- 'post_status' => 'publish',
- 'post_type' => $post_type_filter,
- 'post_name' => $post_name_filter,
- ),
- true
- );
- $user_cpt = get_post( $cpt_post_id, ARRAY_A );
- }
-
- return $user_cpt;
-}
-
-/**
- * Returns the post ID of the CPT containing the user's origin config.
- *
- * @return integer
- */
-function gutenberg_experimental_global_styles_get_user_cpt_id() {
- $user_cpt_id = null;
- $user_cpt = gutenberg_experimental_global_styles_get_user_cpt( true );
- if ( array_key_exists( 'ID', $user_cpt ) ) {
- $user_cpt_id = $user_cpt['ID'];
- }
- return $user_cpt_id;
-}
-
-/**
- * Return core's origin config.
- *
- * @return WP_Theme_JSON Entity that holds core data.
- */
-function gutenberg_experimental_global_styles_get_core() {
- $config = gutenberg_experimental_global_styles_get_from_file(
- __DIR__ . '/experimental-default-theme.json'
- );
-
- // Start i18n logic to remove when JSON i18 strings are extracted.
- $default_colors_i18n = array(
- 'black' => __( 'Black', 'gutenberg' ),
- 'cyan-bluish-gray' => __( 'Cyan bluish gray', 'gutenberg' ),
- 'white' => __( 'White', 'gutenberg' ),
- 'pale-pink' => __( 'Pale pink', 'gutenberg' ),
- 'vivid-red' => __( 'Vivid red', 'gutenberg' ),
- 'luminous-vivid-orange' => __( 'Luminous vivid orange', 'gutenberg' ),
- 'luminous-vivid-amber' => __( 'Luminous vivid amber', 'gutenberg' ),
- 'light-green-cyan' => __( 'Light green cyan', 'gutenberg' ),
- 'vivid-green-cyan' => __( 'Vivid green cyan', 'gutenberg' ),
- 'pale-cyan-blue' => __( 'Pale cyan blue', 'gutenberg' ),
- 'vivid-cyan-blue' => __( 'Vivid cyan blue', 'gutenberg' ),
- 'vivid-purple' => __( 'Vivid purple', 'gutenberg' ),
- );
- if ( ! empty( $config['global']['settings']['color']['palette'] ) ) {
- foreach ( $config['global']['settings']['color']['palette'] as &$color ) {
- $color['name'] = $default_colors_i18n[ $color['slug'] ];
- }
- }
-
- $default_gradients_i18n = array(
- 'vivid-cyan-blue-to-vivid-purple' => __( 'Vivid cyan blue to vivid purple', 'gutenberg' ),
- 'light-green-cyan-to-vivid-green-cyan' => __( 'Light green cyan to vivid green cyan', 'gutenberg' ),
- 'luminous-vivid-amber-to-luminous-vivid-orange' => __( 'Luminous vivid amber to luminous vivid orange', 'gutenberg' ),
- 'luminous-vivid-orange-to-vivid-red' => __( 'Luminous vivid orange to vivid red', 'gutenberg' ),
- 'very-light-gray-to-cyan-bluish-gray' => __( 'Very light gray to cyan bluish gray', 'gutenberg' ),
- 'cool-to-warm-spectrum' => __( 'Cool to warm spectrum', 'gutenberg' ),
- 'blush-light-purple' => __( 'Blush light purple', 'gutenberg' ),
- 'blush-bordeaux' => __( 'Blush bordeaux', 'gutenberg' ),
- 'luminous-dusk' => __( 'Luminous dusk', 'gutenberg' ),
- 'pale-ocean' => __( 'Pale ocean', 'gutenberg' ),
- 'electric-grass' => __( 'Electric grass', 'gutenberg' ),
- 'midnight' => __( 'Midnight', 'gutenberg' ),
- );
- if ( ! empty( $config['global']['settings']['color']['gradients'] ) ) {
- foreach ( $config['global']['settings']['color']['gradients'] as &$gradient ) {
- $gradient['name'] = $default_gradients_i18n[ $gradient['slug'] ];
- }
- }
-
- $default_font_sizes_i18n = array(
- 'small' => __( 'Small', 'gutenberg' ),
- 'normal' => __( 'Normal', 'gutenberg' ),
- 'medium' => __( 'Medium', 'gutenberg' ),
- 'large' => __( 'Large', 'gutenberg' ),
- 'huge' => __( 'Huge', 'gutenberg' ),
- );
- if ( ! empty( $config['global']['settings']['typography']['fontSizes'] ) ) {
- foreach ( $config['global']['settings']['typography']['fontSizes'] as &$font_size ) {
- $font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ];
- }
- }
-
- $default_font_styles_i18n = array(
- 'normal' => __( 'Regular', 'gutenberg' ),
- 'italic' => __( 'Italic', 'gutenberg' ),
- 'initial' => __( 'Initial', 'gutenberg' ),
- 'inherit' => __( 'Inherit', 'gutenberg' ),
- );
- if ( ! empty( $config['global']['settings']['typography']['fontStyles'] ) ) {
- foreach ( $config['global']['settings']['typography']['fontStyles'] as &$font_style ) {
- $font_style['name'] = $default_font_styles_i18n[ $font_style['slug'] ];
- }
- }
-
- $default_font_weights_i18n = array(
- '100' => __( 'Ultralight', 'gutenberg' ),
- '200' => __( 'Thin', 'gutenberg' ),
- '300' => __( 'Light', 'gutenberg' ),
- '400' => __( 'Regular', 'gutenberg' ),
- '500' => __( 'Medium', 'gutenberg' ),
- '600' => __( 'Semibold', 'gutenberg' ),
- '700' => __( 'Bold', 'gutenberg' ),
- '800' => __( 'Heavy', 'gutenberg' ),
- '900' => __( 'Black', 'gutenberg' ),
- 'initial' => __( 'Initial', 'gutenberg' ),
- 'inherit' => __( 'Inherit', 'gutenberg' ),
- );
- if ( ! empty( $config['global']['settings']['typography']['fontWeights'] ) ) {
- foreach ( $config['global']['settings']['typography']['fontWeights'] as &$font_weight ) {
- $font_weight['name'] = $default_font_weights_i18n[ $font_weight['slug'] ];
- }
- }
- // End i18n logic to remove when JSON i18 strings are extracted.
-
- return new WP_Theme_JSON( $config );
-}
-
/**
* Returns the theme presets registered via add_theme_support, if any.
*
@@ -322,43 +110,19 @@ function gutenberg_experimental_global_styles_get_theme_support_settings( $setti
return $theme_settings;
}
-/**
- * Returns the theme's origin config.
- *
- * It also fetches the existing presets the theme declared via add_theme_support
- * and uses them if the theme hasn't declared any via theme.json.
- *
- * @param array $settings Existing editor settings.
- *
- * @return WP_Theme_JSON Entity that holds theme data.
- */
-function gutenberg_experimental_global_styles_get_theme( $settings ) {
- $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings );
- $theme_json_data = gutenberg_experimental_global_styles_get_from_file(
- locate_template( 'experimental-theme.json' )
- );
-
- /*
- * We want the presets and settings declared in theme.json
- * to override the ones declared via add_theme_support.
- */
- $result = new WP_Theme_JSON( $theme_support_data );
- $result->merge( new WP_Theme_JSON( $theme_json_data ) );
-
- return $result;
-}
-
/**
* Takes a tree adhering to the theme.json schema and generates
* the corresponding stylesheet.
*
* @param WP_Theme_JSON $tree Input tree.
+ * @param string $type Type of stylesheet we want accepts 'all', 'block_styles', and 'css_variables'.
*
* @return string Stylesheet.
*/
-function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
+function gutenberg_experimental_global_styles_get_stylesheet( $tree, $type = 'all' ) {
// Check if we can use cached.
$can_use_cached = (
+ ( 'all' === $type ) &&
( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) &&
( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) &&
( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) &&
@@ -373,9 +137,9 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
}
}
- $stylesheet = $tree->get_stylesheet();
+ $stylesheet = $tree->get_stylesheet( $type );
- if ( gutenberg_experimental_global_styles_has_theme_json_support() ) {
+ if ( ( 'all' === $type || 'block_styles' === $type ) && gutenberg_experimental_global_styles_has_theme_json_support() ) {
// To support all themes, we added in the block-library stylesheet
// a style rule such as .has-link-color a { color: var(--wp--style--color--link, #00e); }
// so that existing link colors themes used didn't break.
@@ -386,7 +150,7 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
if ( $can_use_cached ) {
// Cache for a minute.
- // This cache doesn't need to be any longer, we only want to avoid spikes on high-trafic sites.
+ // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites.
set_transient( 'global_styles', $stylesheet, MINUTE_IN_SECONDS );
}
@@ -398,10 +162,11 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
* and enqueues the resulting stylesheet.
*/
function gutenberg_experimental_global_styles_enqueue_assets() {
- $settings = gutenberg_get_common_block_editor_settings();
- $all = gutenberg_experimental_global_styles_get_core();
- $all->merge( gutenberg_experimental_global_styles_get_theme( $settings ) );
- $all->merge( gutenberg_experimental_global_styles_get_user() );
+ $settings = gutenberg_get_common_block_editor_settings();
+ $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings );
+
+ $resolver = new WP_Theme_JSON_Resolver();
+ $all = $resolver->get_origin( $theme_support_data );
$stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $all );
if ( empty( $stylesheet ) ) {
@@ -420,20 +185,7 @@ function gutenberg_experimental_global_styles_enqueue_assets() {
* @return array New block editor settings
*/
function gutenberg_experimental_global_styles_settings( $settings ) {
- $base = gutenberg_experimental_global_styles_get_core();
- $all = gutenberg_experimental_global_styles_get_core();
- $theme = gutenberg_experimental_global_styles_get_theme( $settings );
- $user = gutenberg_experimental_global_styles_get_user();
-
- $base->merge( $theme );
-
- $all->merge( $theme );
- $all->merge( $user );
-
- // STEP 1: ADD FEATURES
- // These need to be added to settings always.
- // We also need to unset the deprecated settings defined by core.
- $settings['__experimentalFeatures'] = $all->get_settings();
+ $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings );
unset( $settings['colors'] );
unset( $settings['disableCustomColors'] );
unset( $settings['disableCustomFontSizes'] );
@@ -443,6 +195,14 @@ function gutenberg_experimental_global_styles_settings( $settings ) {
unset( $settings['fontSizes'] );
unset( $settings['gradients'] );
+ $resolver = new WP_Theme_JSON_Resolver();
+ $all = $resolver->get_origin( $theme_support_data );
+ $base = $resolver->get_origin( $theme_support_data, 'theme' );
+
+ // STEP 1: ADD FEATURES
+ // These need to be added to settings always.
+ $settings['__experimentalFeatures'] = $all->get_settings();
+
// STEP 2 - IF EDIT-SITE, ADD DATA REQUIRED FOR GLOBAL STYLES SIDEBAR
// The client needs some information to be able to access/update the user styles.
// We only do this if the theme has support for theme.json, though,
@@ -454,7 +214,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) &&
gutenberg_is_edit_site_page( $screen->id ) &&
gutenberg_experimental_global_styles_has_theme_json_support()
) {
- $settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id();
+ $settings['__experimentalGlobalStylesUserEntityId'] = WP_Theme_JSON_Resolver::get_user_custom_post_type_id();
$settings['__experimentalGlobalStylesBaseStyles'] = $base->get_raw_data();
} else {
// STEP 3 - OTHERWISE, ADD STYLES
@@ -463,45 +223,18 @@ function_exists( 'gutenberg_is_edit_site_page' ) &&
// we need to add the styles via the settings. This is because
// we want them processed as if they were added via add_editor_styles,
// which adds the editor wrapper class.
- $settings['styles'][] = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $all ) );
+ $settings['styles'][] = array(
+ 'css' => gutenberg_experimental_global_styles_get_stylesheet( $all, 'css_variables' ),
+ '__experimentalNoWrapper' => true,
+ );
+ $settings['styles'][] = array(
+ 'css' => gutenberg_experimental_global_styles_get_stylesheet( $all, 'block_styles' ),
+ );
}
return $settings;
}
-/**
- * Registers a Custom Post Type to store the user's origin config.
- */
-function gutenberg_experimental_global_styles_register_cpt() {
- if ( ! gutenberg_experimental_global_styles_has_theme_json_support() ) {
- return;
- }
-
- $args = array(
- 'label' => __( 'Global Styles', 'gutenberg' ),
- 'description' => 'CPT to store user design tokens',
- 'public' => false,
- 'show_ui' => false,
- 'show_in_rest' => true,
- 'rest_base' => '__experimental/global-styles',
- 'capabilities' => array(
- 'read' => 'edit_theme_options',
- 'create_posts' => 'edit_theme_options',
- 'edit_posts' => 'edit_theme_options',
- 'edit_published_posts' => 'edit_theme_options',
- 'delete_published_posts' => 'edit_theme_options',
- 'edit_others_posts' => 'edit_theme_options',
- 'delete_others_posts' => 'edit_theme_options',
- ),
- 'map_meta_cap' => true,
- 'supports' => array(
- 'editor',
- 'revisions',
- ),
- );
- register_post_type( 'wp_global_styles', $args );
-}
-
-add_action( 'init', 'gutenberg_experimental_global_styles_register_cpt' );
+add_action( 'init', array( 'WP_Theme_JSON_Resolver', 'register_user_custom_post_type' ) );
add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX );
add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' );
diff --git a/lib/load.php b/lib/load.php
index 9227918378a0fd..967c995ee413a8 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -124,6 +124,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/navigation-page.php';
require __DIR__ . '/experiments-page.php';
require __DIR__ . '/class-wp-theme-json.php';
+require __DIR__ . '/class-wp-theme-json-resolver.php';
require __DIR__ . '/global-styles.php';
if ( ! class_exists( 'WP_Block_Supports' ) ) {
diff --git a/lib/patterns/heading-paragraph.php b/lib/patterns/heading-paragraph.php
index 9a8b160a787727..d06caa1d8cc2c8 100644
--- a/lib/patterns/heading-paragraph.php
+++ b/lib/patterns/heading-paragraph.php
@@ -8,7 +8,6 @@
return array(
'title' => __( 'Heading and paragraph', 'gutenberg' ),
'content' => "\n
\n
2. " . __( 'Which treats of the first sally the ingenious Don Quixote made from home', 'gutenberg' ) . "
\n\n\n\n
" . __( 'These preliminaries settled, he did not care to put off any longer the execution of his design, urged on to it by the thought of all the world was losing by his delay, seeing what wrongs he intended to right, grievances to redress, injustices to repair, abuses to remove, and duties to discharge.', 'gutenberg' ) . "
\n
\n",
- 'viewportWidth' => 1000,
'categories' => array( 'text' ),
'description' => _x( 'A heading preceded by a chapter number, and followed by a paragraph.', 'Block pattern description', 'gutenberg' ),
);
diff --git a/lib/patterns/large-header-button.php b/lib/patterns/large-header-button.php
index 1d71e5703f5b5f..bed0ce49062d50 100644
--- a/lib/patterns/large-header-button.php
+++ b/lib/patterns/large-header-button.php
@@ -6,9 +6,8 @@
*/
return array(
- 'title' => __( 'Large header with a heading and a button ', 'gutenberg' ),
- 'content' => "\n
\n",
- 'viewportWidth' => 1000,
- 'categories' => array( 'header' ),
- 'description' => _x( 'A large hero section with a bright gradient background, a big heading and a filled button.', 'Block pattern description', 'gutenberg' ),
+ 'title' => __( 'Large header with a heading and a button ', 'gutenberg' ),
+ 'content' => "\n
\n",
+ 'categories' => array( 'header' ),
+ 'description' => _x( 'A large hero section with a bright gradient background, a big heading and a filled button.', 'Block pattern description', 'gutenberg' ),
);
diff --git a/lib/patterns/large-header.php b/lib/patterns/large-header.php
index 0213bf8bfeb724..0583e1e6a03b2b 100644
--- a/lib/patterns/large-header.php
+++ b/lib/patterns/large-header.php
@@ -6,9 +6,8 @@
*/
return array(
- 'title' => __( 'Large header with a heading', 'gutenberg' ),
- 'content' => "\n
\n
" . __( 'Don Quixote', 'gutenberg' ) . "
\n
\n",
- 'viewportWidth' => 1000,
- 'categories' => array( 'header' ),
- 'description' => _x( 'A large hero section with an example background image and a heading in the center.', 'Block pattern description', 'gutenberg' ),
+ 'title' => __( 'Large header with a heading', 'gutenberg' ),
+ 'content' => "\n
\n
" . __( 'Don Quixote', 'gutenberg' ) . "
\n
\n",
+ 'categories' => array( 'header' ),
+ 'description' => _x( 'A large hero section with an example background image and a heading in the center.', 'Block pattern description', 'gutenberg' ),
);
diff --git a/lib/patterns/text-three-columns-buttons.php b/lib/patterns/text-three-columns-buttons.php
index 629c839b89bb88..8386be74b9c5bb 100644
--- a/lib/patterns/text-three-columns-buttons.php
+++ b/lib/patterns/text-three-columns-buttons.php
@@ -6,9 +6,8 @@
*/
return array(
- 'title' => __( 'Three columns of text with buttons', 'gutenberg' ),
- 'categories' => array( 'columns' ),
- 'content' => "\n
\n
\n
\n
" . __( 'Which treats of the character and pursuits of the famous Don Quixote of La Mancha.', 'gutenberg' ) . "
\n",
- 'viewportWidth' => 1000,
- 'description' => _x( 'Three small columns of text, each with an outlined button with rounded corners at the bottom.', 'Block pattern description', 'gutenberg' ),
+ 'title' => __( 'Three columns of text with buttons', 'gutenberg' ),
+ 'categories' => array( 'columns' ),
+ 'content' => "\n
\n
\n
\n
" . __( 'Which treats of the character and pursuits of the famous Don Quixote of La Mancha.', 'gutenberg' ) . "