From a0b805360808246cd3dfc7281efec0f0308f6863 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Wed, 25 May 2022 10:16:11 +0100
Subject: [PATCH 01/13] Update the minimum WP version to 5.9
---
gutenberg.php | 2 +-
lib/compat/wordpress-5.9/admin-menu.php | 112 -
.../wordpress-5.9/block-editor-settings.php | 29 -
lib/compat/wordpress-5.9/block-gallery.php | 57 -
lib/compat/wordpress-5.9/block-patterns.php | 45 -
.../wordpress-5.9/block-template-utils.php | 630 ------
lib/compat/wordpress-5.9/block-template.php | 310 ---
lib/compat/wordpress-5.9/blocks.php | 247 ---
.../class-gutenberg-block-template.php | 131 --
...class-wp-rest-global-styles-controller.php | 590 -----
.../class-wp-rest-menu-items-controller.php | 1169 ----------
...lass-wp-rest-menu-locations-controller.php | 276 ---
.../class-wp-rest-menus-controller.php | 693 ------
.../class-wp-rest-url-details-controller.php | 591 -----
.../wordpress-5.9/class-wp-theme-json-5-9.php | 1944 -----------------
.../class-wp-theme-json-resolver-5-9.php | 416 ----
.../class-wp-theme-json-schema-gutenberg.php | 489 -----
.../wordpress-5.9/default-editor-styles.php | 41 -
.../wordpress-5.9/default-theme-supports.php | 23 -
lib/compat/wordpress-5.9/edit-site-page.php | 240 --
.../global-styles-css-custom-properties.php | 20 -
lib/compat/wordpress-5.9/json-file-decode.php | 54 -
lib/compat/wordpress-5.9/kses.php | 82 -
.../move-theme-editor-menu-item.php | 24 -
lib/compat/wordpress-5.9/navigation.php | 243 ---
lib/compat/wordpress-5.9/polyfills.php | 70 -
.../register-global-styles-cpt.php | 49 -
.../rest-active-global-styles.php | 33 -
lib/compat/wordpress-5.9/rest-api.php | 148 --
lib/compat/wordpress-5.9/script-loader.php | 48 -
lib/compat/wordpress-5.9/template-canvas.php | 27 -
lib/compat/wordpress-5.9/template-parts.php | 129 --
lib/compat/wordpress-5.9/templates.php | 255 ---
lib/compat/wordpress-5.9/theme-templates.php | 87 -
lib/compat/wordpress-5.9/theme.php | 28 -
.../translate-settings-using-i18n-schema.php | 50 -
...b-rest-widget-render-endpoint-polyfill.php | 121 -
.../widget-render-api-endpoint/index.php | 22 -
.../wordpress-6.0/block-editor-settings.php | 14 -
.../wordpress-6.0/class-wp-theme-json-6-0.php | 2 +-
.../class-wp-theme-json-resolver-6-0.php | 2 +-
lib/load.php | 46 -
.../block-library/src/gallery/deprecated.js | 63 +-
.../block-library/src/gallery/edit-wrapper.js | 27 -
.../src/gallery/gallery-styles.native.scss | 2 -
packages/block-library/src/gallery/index.js | 2 +-
packages/block-library/src/gallery/save.js | 10 -
.../block-library/src/gallery/transforms.js | 169 +-
.../block-library/src/gallery/v1/constants.js | 3 -
.../src/gallery/v1/gallery-button.native.js | 47 -
.../v1/gallery-image-style.native.scss | 109 -
.../src/gallery/v1/gallery-image.js | 282 ---
.../src/gallery/v1/gallery-styles.native.scss | 8 -
.../block-library/src/gallery/v1/gallery.js | 125 --
.../src/gallery/v1/gallery.native.js | 162 --
packages/block-library/src/gallery/v1/save.js | 98 -
.../block-library/src/gallery/v1/shared.js | 19 -
.../src/gallery/v1/tiles-styles.native.scss | 11 -
.../src/gallery/v1/tiles.native.js | 79 -
59 files changed, 57 insertions(+), 10748 deletions(-)
delete mode 100644 lib/compat/wordpress-5.9/admin-menu.php
delete mode 100644 lib/compat/wordpress-5.9/block-editor-settings.php
delete mode 100644 lib/compat/wordpress-5.9/block-gallery.php
delete mode 100644 lib/compat/wordpress-5.9/block-patterns.php
delete mode 100644 lib/compat/wordpress-5.9/block-template-utils.php
delete mode 100644 lib/compat/wordpress-5.9/block-template.php
delete mode 100644 lib/compat/wordpress-5.9/blocks.php
delete mode 100644 lib/compat/wordpress-5.9/class-gutenberg-block-template.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-rest-global-styles-controller.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-rest-menu-items-controller.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-rest-menu-locations-controller.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-rest-menus-controller.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-rest-url-details-controller.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php
delete mode 100644 lib/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php
delete mode 100644 lib/compat/wordpress-5.9/default-editor-styles.php
delete mode 100644 lib/compat/wordpress-5.9/default-theme-supports.php
delete mode 100644 lib/compat/wordpress-5.9/edit-site-page.php
delete mode 100644 lib/compat/wordpress-5.9/global-styles-css-custom-properties.php
delete mode 100644 lib/compat/wordpress-5.9/json-file-decode.php
delete mode 100644 lib/compat/wordpress-5.9/kses.php
delete mode 100644 lib/compat/wordpress-5.9/move-theme-editor-menu-item.php
delete mode 100644 lib/compat/wordpress-5.9/navigation.php
delete mode 100644 lib/compat/wordpress-5.9/polyfills.php
delete mode 100644 lib/compat/wordpress-5.9/register-global-styles-cpt.php
delete mode 100644 lib/compat/wordpress-5.9/rest-active-global-styles.php
delete mode 100644 lib/compat/wordpress-5.9/rest-api.php
delete mode 100644 lib/compat/wordpress-5.9/script-loader.php
delete mode 100644 lib/compat/wordpress-5.9/template-canvas.php
delete mode 100644 lib/compat/wordpress-5.9/template-parts.php
delete mode 100644 lib/compat/wordpress-5.9/templates.php
delete mode 100644 lib/compat/wordpress-5.9/theme-templates.php
delete mode 100644 lib/compat/wordpress-5.9/theme.php
delete mode 100644 lib/compat/wordpress-5.9/translate-settings-using-i18n-schema.php
delete mode 100644 lib/compat/wordpress-5.9/widget-render-api-endpoint/class-gb-rest-widget-render-endpoint-polyfill.php
delete mode 100644 lib/compat/wordpress-5.9/widget-render-api-endpoint/index.php
delete mode 100644 packages/block-library/src/gallery/edit-wrapper.js
delete mode 100644 packages/block-library/src/gallery/v1/constants.js
delete mode 100644 packages/block-library/src/gallery/v1/gallery-button.native.js
delete mode 100644 packages/block-library/src/gallery/v1/gallery-image-style.native.scss
delete mode 100644 packages/block-library/src/gallery/v1/gallery-image.js
delete mode 100644 packages/block-library/src/gallery/v1/gallery-styles.native.scss
delete mode 100644 packages/block-library/src/gallery/v1/gallery.js
delete mode 100644 packages/block-library/src/gallery/v1/gallery.native.js
delete mode 100644 packages/block-library/src/gallery/v1/save.js
delete mode 100644 packages/block-library/src/gallery/v1/shared.js
delete mode 100644 packages/block-library/src/gallery/v1/tiles-styles.native.scss
delete mode 100644 packages/block-library/src/gallery/v1/tiles.native.js
diff --git a/gutenberg.php b/gutenberg.php
index 3457ac3f6158a..3488ca7f516c2 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -3,7 +3,7 @@
* Plugin Name: Gutenberg
* Plugin URI: https://github.com/WordPress/gutenberg
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
- * Requires at least: 5.8
+ * Requires at least: 5.9
* Requires PHP: 5.6
* Version: 13.6.0-rc.1
* Author: Gutenberg Team
diff --git a/lib/compat/wordpress-5.9/admin-menu.php b/lib/compat/wordpress-5.9/admin-menu.php
deleted file mode 100644
index c070761945092..0000000000000
--- a/lib/compat/wordpress-5.9/admin-menu.php
+++ /dev/null
@@ -1,112 +0,0 @@
- $menu_item ) {
- if ( false !== strpos( $menu_item[2], 'customize.php' ) ) {
- // Assume the first entry is the Customizer proper, if customize.php is linked > once.
- if ( is_null( $customize_menu ) ) {
- $indexes_to_remove[] = $index;
- $customize_menu = $menu_item;
- }
- }
-
- if ( false !== strpos( $menu_item[2], 'site-editor.php' ) ) {
- $indexes_to_remove[] = $index;
- }
-
- if ( false !== strpos( $menu_item[2], 'gutenberg-widgets' ) ) {
- $indexes_to_remove[] = $index;
- }
- }
-
- foreach ( $indexes_to_remove as $index ) {
- unset( $submenu['themes.php'][ $index ] );
- }
-
- // Add Customizer back but with a new sub-menu position when a site requires this feature.
- if ( gutenberg_site_requires_customizer() && $customize_menu ) {
- $submenu['themes.php'][20] = $customize_menu;
- }
-}
-add_action( 'admin_menu', 'gutenberg_remove_legacy_pages' );
-
-/**
- * Removes legacy adminbar items from FSE themes.
- *
- * @param WP_Admin_Bar $wp_admin_bar The admin-bar instance.
- */
-function gutenberg_adminbar_items( $wp_admin_bar ) {
-
- // Early exit if not a block theme.
- if ( ! wp_is_block_theme() ) {
- return;
- }
-
- // Remove customizer link, if this site does not rely on them for plugins or theme options.
- if ( ! gutenberg_site_requires_customizer() ) {
- $wp_admin_bar->remove_node( 'customize' );
- $wp_admin_bar->remove_node( 'customize-background' );
- $wp_admin_bar->remove_node( 'customize-header' );
- $wp_admin_bar->remove_node( 'widgets' );
- }
-
- // Add site-editor link.
- if ( ! is_admin() && current_user_can( 'edit_theme_options' ) ) {
- $wp_admin_bar->add_node(
- array(
- 'id' => 'site-editor',
- 'title' => __( 'Edit site', 'gutenberg' ),
- 'href' => admin_url( 'themes.php?page=gutenberg-edit-site' ),
- )
- );
- }
-}
-add_action( 'admin_bar_menu', 'gutenberg_adminbar_items', 50 );
-
-/**
- * Override Site Editor URLs to use plugin page.
- *
- * @param string $url Admin URL link with path.
- * @param string $path Path relative to the admin URL.
- * @return string Modified Admin URL link.
- */
-function gutenberg_override_site_editor_urls( $url, $path ) {
- if ( 'site-editor.php' === $path ) {
- $url = str_replace( $path, 'themes.php?page=gutenberg-edit-site', $url );
- }
-
- return $url;
-}
-add_filter( 'admin_url', 'gutenberg_override_site_editor_urls', 10, 2 );
-
-/**
- * Check if any plugin, or theme features, are using the Customizer.
- *
- * @return bool A boolean value indicating if Customizer support is needed.
- */
-function gutenberg_site_requires_customizer() {
- if ( has_action( 'customize_register' ) ) {
- return true;
- }
-
- return false;
-}
diff --git a/lib/compat/wordpress-5.9/block-editor-settings.php b/lib/compat/wordpress-5.9/block-editor-settings.php
deleted file mode 100644
index 91849b0c6afb4..0000000000000
--- a/lib/compat/wordpress-5.9/block-editor-settings.php
+++ /dev/null
@@ -1,29 +0,0 @@
-= 5.9.
- *
- * @return void.
- */
-function gutenberg_check_gallery_block_v2_compatibility() {
- $use_balance_tags = (int) get_option( 'use_balanceTags' );
- $v2_gallery_enabled = boolval( 1 !== $use_balance_tags || is_wp_version_compatible( '5.9' ) ) ? 'true' : 'false';
-
- wp_add_inline_script(
- 'wp-dom-ready',
- 'wp.galleryBlockV2Enabled = ' . $v2_gallery_enabled . ';',
- 'after'
- );
-}
-add_action( 'init', 'gutenberg_check_gallery_block_v2_compatibility' );
-
-/**
- * Prevent use_balanceTags being enabled on WordPress 5.8 or earlier as it breaks
- * the layout of the new Gallery block.
- *
- * @since 12.1.0
- * @todo This should be removed when the minimum required WP version is >= 5.9.
- *
- * @param int $new_value The new value for use_balanceTags.
- */
-function gutenberg_use_balancetags_check( $new_value ) {
- global $wp_version;
-
- if ( 1 === (int) $new_value && version_compare( $wp_version, '5.9', '<' ) ) {
- /* translators: %s: Minimum required version */
- $message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the “Correct invalidly nested XHTML automatically” option. Please upgrade WordPress before enabling.', 'gutenberg' ), '5.9' );
- add_settings_error( 'gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
- if ( class_exists( 'WP_CLI' ) ) {
- WP_CLI::error( $message );
- }
- return 0;
- }
-
- return $new_value;
-}
-add_filter( 'pre_update_option_use_balanceTags', 'gutenberg_use_balancetags_check' );
diff --git a/lib/compat/wordpress-5.9/block-patterns.php b/lib/compat/wordpress-5.9/block-patterns.php
deleted file mode 100644
index f92a6203057e6..0000000000000
--- a/lib/compat/wordpress-5.9/block-patterns.php
+++ /dev/null
@@ -1,45 +0,0 @@
-is_error() ) {
- return;
- }
- $patterns = $response->get_data();
- foreach ( $patterns as $pattern ) {
- $pattern_name = sanitize_title( $pattern['title'] );
- $registry = WP_Block_Patterns_Registry::get_instance();
- // Some patterns might be already registered as core patterns with the `core` prefix.
- $is_registered = $registry->is_registered( $pattern_name ) || $registry->is_registered( "core/$pattern_name" );
- if ( ! $is_registered ) {
- register_block_pattern( $pattern_name, (array) $pattern );
- }
- }
- }
-}
diff --git a/lib/compat/wordpress-5.9/block-template-utils.php b/lib/compat/wordpress-5.9/block-template-utils.php
deleted file mode 100644
index cfd6910e3859c..0000000000000
--- a/lib/compat/wordpress-5.9/block-template-utils.php
+++ /dev/null
@@ -1,630 +0,0 @@
- 'block-templates',
- 'wp_template_part' => 'block-template-parts',
- );
- }
-
- return array(
- 'wp_template' => 'templates',
- 'wp_template_part' => 'parts',
- );
- }
-}
-
-if ( ! function_exists( 'get_allowed_block_template_part_areas' ) ) {
- /**
- * Returns a filtered list of allowed area values for template parts.
- *
- * @return array The supported template part area values.
- */
- function get_allowed_block_template_part_areas() {
- $default_area_definitions = array(
- array(
- 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED,
- 'label' => __( 'General', 'gutenberg' ),
- 'description' => __(
- 'General templates often perform a specific role like displaying post content, and are not tied to any particular area.',
- 'gutenberg'
- ),
- 'icon' => 'layout',
- 'area_tag' => 'div',
- ),
- array(
- 'area' => WP_TEMPLATE_PART_AREA_HEADER,
- 'label' => __( 'Header', 'gutenberg' ),
- 'description' => __(
- 'The Header template defines a page area that typically contains a title, logo, and main navigation.',
- 'gutenberg'
- ),
- 'icon' => 'header',
- 'area_tag' => 'header',
- ),
- array(
- 'area' => WP_TEMPLATE_PART_AREA_FOOTER,
- 'label' => __( 'Footer', 'gutenberg' ),
- 'description' => __(
- 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.',
- 'gutenberg'
- ),
- 'icon' => 'footer',
- 'area_tag' => 'footer',
- ),
- );
-
- /**
- * Filters the list of allowed template part area values.
- *
- * @param array $default_areas An array of supported area objects.
- */
- return apply_filters( 'default_wp_template_part_areas', $default_area_definitions );
- }
-}
-
-if ( ! function_exists( 'get_default_block_template_types' ) ) {
- /**
- * Returns a filtered list of default template types, containing their
- * localized titles and descriptions.
- *
- * @return array The default template types.
- */
- function get_default_block_template_types() {
- $default_template_types = array(
- 'index' => array(
- 'title' => _x( 'Index', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays posts.', 'gutenberg' ),
- ),
- 'home' => array(
- 'title' => _x( 'Home', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays posts on the homepage, or on the Posts page if a static homepage is set.', 'gutenberg' ),
- ),
- 'front-page' => array(
- 'title' => _x( 'Front Page', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays the homepage.', 'gutenberg' ),
- ),
- 'singular' => array(
- 'title' => _x( 'Singular', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a single post or page.', 'gutenberg' ),
- ),
- 'single' => array(
- 'title' => _x( 'Single Post', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a single post.', 'gutenberg' ),
- ),
- 'page' => array(
- 'title' => _x( 'Page', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays a single page.', 'gutenberg' ),
- ),
- 'archive' => array(
- 'title' => _x( 'Archive', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays post categories, tags, and other archives.', 'gutenberg' ),
- ),
- 'author' => array(
- 'title' => _x( 'Author', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays latest posts written by a single author.', 'gutenberg' ),
- ),
- 'category' => array(
- 'title' => _x( 'Category', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays latest posts in single post category.', 'gutenberg' ),
- ),
- 'taxonomy' => array(
- 'title' => _x( 'Taxonomy', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays latest posts from a single post taxonomy.', 'gutenberg' ),
- ),
- 'date' => array(
- 'title' => _x( 'Date', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays posts from a specific date.', 'gutenberg' ),
- ),
- 'tag' => array(
- 'title' => _x( 'Tag', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays latest posts with a single post tag.', 'gutenberg' ),
- ),
- 'attachment' => array(
- 'title' => __( 'Media', 'gutenberg' ),
- 'description' => __( 'Displays individual media items or attachments.', 'gutenberg' ),
- ),
- 'search' => array(
- 'title' => _x( 'Search', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays search results.', 'gutenberg' ),
- ),
- 'privacy-policy' => array(
- 'title' => __( 'Privacy Policy', 'gutenberg' ),
- 'description' => __( 'Displays the privacy policy page.', 'gutenberg' ),
- ),
- '404' => array(
- 'title' => _x( '404', 'Template name', 'gutenberg' ),
- 'description' => __( 'Displays when no content is found.', 'gutenberg' ),
- ),
- );
-
- /**
- * Filters the list of template types.
- *
- * @param array $default_template_types An array of template types, formatted as [ slug => [ title, description ] ].
- *
- * @since 5.x.x
- */
- return apply_filters( 'default_template_types', $default_template_types );
- }
-}
-
-if ( ! function_exists( '_filter_block_template_part_area' ) ) {
- /**
- * Checks whether the input 'area' is a supported value.
- * Returns the input if supported, otherwise returns the 'uncategorized' value.
- *
- * @param string $type Template part area name.
- *
- * @return string Input if supported, else the uncategorized value.
- */
- function _filter_block_template_part_area( $type ) {
- $allowed_areas = array_map(
- static function ( $item ) {
- return $item['area'];
- },
- get_allowed_block_template_part_areas()
- );
- if ( in_array( $type, $allowed_areas, true ) ) {
- return $type;
- }
-
- /* translators: %1$s: Template area type, %2$s: the uncategorized template area value. */
- $warning_message = sprintf( __( '"%1$s" is not a supported wp_template_part area value and has been added as "%2$s".', 'gutenberg' ), $type, WP_TEMPLATE_PART_AREA_UNCATEGORIZED );
- trigger_error( $warning_message, E_USER_NOTICE );
- return WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
- }
-}
-
-if ( ! function_exists( '_get_block_templates_paths' ) ) {
- /**
- * Finds all nested template part file paths in a theme's directory.
- *
- * @access private
- *
- * @param string $base_directory The theme's file path.
- * @return array $path_list A list of paths to all template part files.
- */
- function _get_block_templates_paths( $base_directory ) {
- $path_list = array();
- if ( file_exists( $base_directory ) ) {
- $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) );
- $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH );
- foreach ( $nested_html_files as $path => $file ) {
- $path_list[] = $path;
- }
- }
- return $path_list;
- }
-}
-
-if ( ! function_exists( '_get_block_template_file' ) ) {
- /**
- * Retrieves the template file from the theme for a given slug.
- *
- * @access private
- * @internal
- *
- * @param string $template_type wp_template or wp_template_part.
- * @param string $slug template slug.
- *
- * @return array|null Template.
- */
- function _get_block_template_file( $template_type, $slug ) {
- if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) {
- return null;
- }
-
- $themes = array(
- get_stylesheet() => get_stylesheet_directory(),
- get_template() => get_template_directory(),
- );
- foreach ( $themes as $theme_slug => $theme_dir ) {
- $template_base_paths = get_block_theme_folders( $theme_slug );
- $file_path = $theme_dir . '/' . $template_base_paths[ $template_type ] . '/' . $slug . '.html';
- if ( file_exists( $file_path ) ) {
- $new_template_item = array(
- 'slug' => $slug,
- 'path' => $file_path,
- 'theme' => $theme_slug,
- 'type' => $template_type,
- );
-
- if ( 'wp_template_part' === $template_type ) {
- return _add_block_template_part_area_info( $new_template_item );
- }
-
- if ( 'wp_template' === $template_type ) {
- return _add_block_template_info( $new_template_item );
- }
-
- return $new_template_item;
- }
- }
-
- return null;
- }
-}
-
-if ( ! function_exists( '_get_block_templates_files' ) ) {
- /**
- * Retrieves the template files from the theme.
- *
- * @access private
- * @internal
- *
- * @param string $template_type wp_template or wp_template_part.
- *
- * @return array Template.
- */
- function _get_block_templates_files( $template_type ) {
- if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) {
- return null;
- }
-
- $themes = array(
- get_stylesheet() => get_stylesheet_directory(),
- get_template() => get_template_directory(),
- );
- $template_files = array();
- foreach ( $themes as $theme_slug => $theme_dir ) {
- $template_base_paths = get_block_theme_folders( $theme_slug );
- $theme_template_files = _get_block_templates_paths( $theme_dir . '/' . $template_base_paths[ $template_type ] );
- foreach ( $theme_template_files as $template_file ) {
- $template_base_path = $template_base_paths[ $template_type ];
- $template_slug = substr(
- $template_file,
- // Starting position of slug.
- strpos( $template_file, $template_base_path . DIRECTORY_SEPARATOR ) + 1 + strlen( $template_base_path ),
- // Subtract ending '.html'.
- -5
- );
- $new_template_item = array(
- 'slug' => $template_slug,
- 'path' => $template_file,
- 'theme' => $theme_slug,
- 'type' => $template_type,
- );
-
- if ( 'wp_template_part' === $template_type ) {
- $template_files[] = _add_block_template_part_area_info( $new_template_item );
- }
-
- if ( 'wp_template' === $template_type ) {
- $template_files[] = _add_block_template_info( $new_template_item );
- }
- }
- }
-
- return $template_files;
- }
-}
-
-if ( ! function_exists( '_add_block_template_info' ) ) {
- /**
- * Attempts to add custom template information to the template item.
- *
- * @param array $template_item Template to add information to (requires 'slug' field).
- * @return array Template
- */
- function _add_block_template_info( $template_item ) {
- if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
- return $template_item;
- }
-
- $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_custom_templates();
- if ( isset( $theme_data[ $template_item['slug'] ] ) ) {
- $template_item['title'] = $theme_data[ $template_item['slug'] ]['title'];
- $template_item['postTypes'] = $theme_data[ $template_item['slug'] ]['postTypes'];
- }
-
- return $template_item;
- }
-}
-
-if ( ! function_exists( '_add_block_template_part_area_info' ) ) {
- /**
- * Attempts to add the template part's area information to the input template.
- *
- * @param array $template_info Template to add information to (requires 'type' and 'slug' fields).
- *
- * @return array Template.
- */
- function _add_block_template_part_area_info( $template_info ) {
- if ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
- $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_template_parts();
- }
-
- if ( isset( $theme_data[ $template_info['slug'] ]['area'] ) ) {
- $template_info['title'] = $theme_data[ $template_info['slug'] ]['title'];
- $template_info['area'] = _filter_block_template_part_area( $theme_data[ $template_info['slug'] ]['area'] );
- } else {
- $template_info['area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
- }
-
- return $template_info;
- }
-}
-
-if ( ! function_exists( '_flatten_blocks' ) ) {
- /**
- * Returns an array containing the references of
- * the passed blocks and their inner blocks.
- *
- * @param array $blocks array of blocks.
- *
- * @return array block references to the passed blocks and their inner blocks.
- */
- function _flatten_blocks( &$blocks ) {
- $all_blocks = array();
- $queue = array();
- foreach ( $blocks as &$block ) {
- $queue[] = &$block;
- }
-
- while ( count( $queue ) > 0 ) {
- $block = &$queue[0];
- array_shift( $queue );
- $all_blocks[] = &$block;
-
- if ( ! empty( $block['innerBlocks'] ) ) {
- foreach ( $block['innerBlocks'] as &$inner_block ) {
- $queue[] = &$inner_block;
- }
- }
- }
-
- return $all_blocks;
- }
-}
-
-if ( ! function_exists( '_inject_theme_attribute_in_block_template_content' ) ) {
- /**
- * Parses wp_template content and injects the current theme's
- * stylesheet as a theme attribute into each wp_template_part
- *
- * @param string $template_content serialized wp_template content.
- *
- * @return string Updated wp_template content.
- */
- function _inject_theme_attribute_in_block_template_content( $template_content ) {
- $has_updated_content = false;
- $new_content = '';
- $template_blocks = parse_blocks( $template_content );
-
- $blocks = _flatten_blocks( $template_blocks );
- foreach ( $blocks as &$block ) {
- if (
- 'core/template-part' === $block['blockName'] &&
- ! isset( $block['attrs']['theme'] )
- ) {
- $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
- $has_updated_content = true;
- }
- }
-
- if ( $has_updated_content ) {
- foreach ( $template_blocks as &$block ) {
- $new_content .= serialize_block( $block );
- }
-
- return $new_content;
- }
-
- return $template_content;
- }
-}
-
-if ( ! function_exists( '_remove_theme_attribute_in_block_template_content' ) ) {
- /**
- * Parses wp_template content and removes the theme attribute from
- * each wp_template_part
- *
- * @param string $template_content serialized wp_template content.
- *
- * @return string Updated wp_template content.
- */
- function _remove_theme_attribute_in_block_template_content( $template_content ) {
- $has_updated_content = false;
- $new_content = '';
- $template_blocks = parse_blocks( $template_content );
-
- $blocks = _flatten_blocks( $template_blocks );
- foreach ( $blocks as $key => $block ) {
- if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) {
- unset( $blocks[ $key ]['attrs']['theme'] );
- $has_updated_content = true;
- }
- }
-
- if ( ! $has_updated_content ) {
- return $template_content;
- }
-
- foreach ( $template_blocks as $block ) {
- $new_content .= serialize_block( $block );
- }
-
- return $new_content;
- }
-}
-
-if ( ! function_exists( '_build_block_template_result_from_file' ) ) {
- /**
- * Build a unified template object based on a theme file.
- *
- * @param array $template_file Theme file.
- * @param array $template_type wp_template or wp_template_part.
- *
- * @return Gutenberg_Block_Template Template.
- */
- function _build_block_template_result_from_file( $template_file, $template_type ) {
- $default_template_types = get_default_block_template_types();
- $template_content = file_get_contents( $template_file['path'] );
- $theme = wp_get_theme()->get_stylesheet();
-
- $template = new Gutenberg_Block_Template();
- $template->id = $theme . '//' . $template_file['slug'];
- $template->theme = $theme;
- $template->content = _inject_theme_attribute_in_block_template_content( $template_content );
- $template->slug = $template_file['slug'];
- $template->source = 'theme';
- $template->type = $template_type;
- $template->title = ! empty( $template_file['title'] ) ? $template_file['title'] : $template_file['slug'];
- $template->status = 'publish';
- $template->has_theme_file = true;
- $template->is_custom = true;
-
- if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) {
- $template->description = $default_template_types[ $template_file['slug'] ]['description'];
- $template->title = $default_template_types[ $template_file['slug'] ]['title'];
- $template->is_custom = false;
- }
-
- if ( 'wp_template' === $template_type && isset( $template_file['postTypes'] ) ) {
- $template->post_types = $template_file['postTypes'];
- }
-
- if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) {
- $template->area = $template_file['area'];
- }
-
- return $template;
- }
-}
-
-if ( ! function_exists( 'get_block_file_template' ) ) {
- /**
- * Retrieves a single unified template object using its id.
- * Retrieves the file template.
- *
- * @param string $id Template unique identifier (example: theme_slug//template_slug).
- * @param array $template_type wp_template or wp_template_part.
- *
- * @return Gutenberg_Block_Template|null File template.
- */
- function get_block_file_template( $id, $template_type = 'wp_template' ) {
- /**
- * Filters the block templates array before the query takes place.
- *
- * Return a non-null value to bypass the WordPress queries.
- *
- * @since 10.8
- *
- * @param Gutenberg_Block_Template|null $block_template Return block template object to short-circuit the default query,
- * or null to allow WP to run it's normal queries.
- * @param string $id Template unique identifier (example: theme_slug//template_slug).
- * @param array $template_type wp_template or wp_template_part.
- */
- $block_template = apply_filters( 'pre_get_block_file_template', null, $id, $template_type );
- if ( ! is_null( $block_template ) ) {
- return $block_template;
- }
-
- $parts = explode( '//', $id, 2 );
- if ( count( $parts ) < 2 ) {
- /** This filter is documented at the end of this function */
- return apply_filters( 'get_block_file_template', null, $id, $template_type );
- }
- list( $theme, $slug ) = $parts;
-
- if ( wp_get_theme()->get_stylesheet() !== $theme ) {
- /** This filter is documented at the end of this function */
- return apply_filters( 'get_block_file_template', null, $id, $template_type );
- }
-
- $template_file = _get_block_template_file( $template_type, $slug );
- if ( null === $template_file ) {
- /** This filter is documented at the end of this function */
- return apply_filters( 'get_block_file_template', null, $id, $template_type );
- }
-
- $block_template = _build_block_template_result_from_file( $template_file, $template_type );
-
- /**
- * Filters the array of queried block templates array after they've been fetched.
- *
- * @since 10.8
- *
- * @param null|Gutenberg_Block_Template $block_template The found block template.
- * @param string $id Template unique identifier (example: theme_slug//template_slug).
- * @param array $template_type wp_template or wp_template_part.
- */
- return apply_filters( 'get_block_file_template', $block_template, $id, $template_type );
- }
-}
-
-if ( ! function_exists( 'block_template_part' ) ) {
- /**
- * Print a template-part.
- *
- * @param string $part The template-part to print. Use "header" or "footer".
- *
- * @return void
- */
- function block_template_part( $part ) {
- $template_part = gutenberg_get_block_template( get_stylesheet() . '//' . $part, 'wp_template_part' );
- if ( ! $template_part || empty( $template_part->content ) ) {
- return;
- }
- echo do_blocks( $template_part->content );
- }
-}
-
-if ( ! function_exists( 'block_header_area' ) ) {
- /**
- * Print the header template-part.
- *
- * @return void
- */
- function block_header_area() {
- block_template_part( 'header' );
- }
-}
-
-if ( ! function_exists( 'block_footer_area' ) ) {
- /**
- * Print the footer template-part.
- *
- * @return void
- */
- function block_footer_area() {
- block_template_part( 'footer' );
- }
-}
diff --git a/lib/compat/wordpress-5.9/block-template.php b/lib/compat/wordpress-5.9/block-template.php
deleted file mode 100644
index fd4329c2cbf32..0000000000000
--- a/lib/compat/wordpress-5.9/block-template.php
+++ /dev/null
@@ -1,310 +0,0 @@
-content ) && is_user_logged_in() ) {
- $_wp_current_template_content =
- sprintf(
- /* translators: %s: Template title */
- __( 'Empty template: %s', 'gutenberg' ),
- $block_template->title
- );
- } elseif ( ! empty( $block_template->content ) ) {
- $_wp_current_template_content = $block_template->content;
- }
- if ( isset( $_GET['_wp-find-template'] ) ) {
- wp_send_json_success( $block_template );
- }
- } else {
- if ( $template ) {
- return $template;
- }
-
- if ( 'index' === $type ) {
- if ( isset( $_GET['_wp-find-template'] ) ) {
- wp_send_json_error( array( 'message' => __( 'No matching template found.', 'gutenberg' ) ) );
- }
- } else {
- return false; // So that the template loader keeps looking for templates.
- }
- }
-
- // Add hooks for template canvas.
- // Add viewport meta tag.
- add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 );
-
- // Render title tag with content, regardless of whether theme has title-tag support.
- remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering...
- // Override WP 5.8 title-tag support.
- remove_action( 'wp_head', '_block_template_render_title_tag', 1 );
- add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); // ...and make it unconditional.
-
- // This file will be included instead of the theme's template file.
- return gutenberg_dir_path() . 'lib/compat/wordpress-5.9/template-canvas.php';
-}
-
-/**
- * Return the correct 'wp_template' to render for the request template type.
- *
- * Accepts an optional $template_hierarchy argument as a hint.
- *
- * @since 5.9.0 Added the `$fallback_template` parameter.
- *
- * @param string $template_type The current template type.
- * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority.
- * @param string $fallback_template A PHP fallback template to use if no matching block template is found.
- * @return null|Gutenberg_Block_Template A block template if found. Null if not.
- */
-function gutenberg_resolve_template( $template_type, $template_hierarchy, $fallback_template ) {
- if ( ! $template_type ) {
- return null;
- }
-
- if ( empty( $template_hierarchy ) ) {
- $template_hierarchy = array( $template_type );
- }
-
- $slugs = array_map(
- 'gutenberg_strip_template_file_suffix',
- $template_hierarchy
- );
-
- // Find all potential templates 'wp_template' post matching the hierarchy.
- $query = array(
- 'theme' => wp_get_theme()->get_stylesheet(),
- 'slug__in' => $slugs,
- );
- $templates = gutenberg_get_block_templates( $query );
-
- // Order these templates per slug priority.
- // Build map of template slugs to their priority in the current hierarchy.
- $slug_priorities = array_flip( $slugs );
-
- usort(
- $templates,
- function ( $template_a, $template_b ) use ( $slug_priorities ) {
- return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ];
- }
- );
-
- $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
- $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
-
- // Is the current theme a child theme, and is the PHP fallback template part of it?
- if (
- strpos( $fallback_template, $theme_base_path ) === 0 &&
- strpos( $fallback_template, $parent_theme_base_path ) === false
- ) {
- $fallback_template_slug = substr(
- $fallback_template,
- // Starting position of slug.
- strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ),
- // Remove '.php' suffix.
- -4
- );
-
- // Is our candidate block template's slug identical to our PHP fallback template's?
- if (
- count( $templates ) &&
- $fallback_template_slug === $templates[0]->slug &&
- 'theme' === $templates[0]->source
- ) {
- // Unfortunately, we cannot trust $templates[0]->theme, since it will always
- // be set to the current theme's slug by _build_block_template_result_from_file(),
- // even if the block template is really coming from the current theme's parent.
- // (The reason for this is that we want it to be associated with the current theme
- // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
- // Instead, we use _get_block_template_file() to locate the block template file.
- $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug );
- if ( $template_file && get_template() === $template_file['theme'] ) {
- // The block template is part of the parent theme, so we
- // have to give precedence to the child theme's PHP template.
- array_shift( $templates );
- }
- }
- }
-
- return count( $templates ) ? $templates[0] : null;
-}
-
-/**
- * Displays title tag with content, regardless of whether theme has title-tag support.
- *
- * @see _wp_render_title_tag()
- */
-function gutenberg_render_title_tag() {
- echo '' . wp_get_document_title() . ' ' . "\n";
-}
-
-/**
- * Returns the markup for the current template.
- */
-function gutenberg_get_the_template_html() {
- global $_wp_current_template_content;
- global $wp_embed;
-
- if ( ! $_wp_current_template_content ) {
- if ( is_user_logged_in() ) {
- return '' . esc_html__( 'No matching template found', 'gutenberg' ) . ' ';
- }
- return;
- }
-
- $content = $wp_embed->run_shortcode( $_wp_current_template_content );
- $content = $wp_embed->autoembed( $content );
- $content = do_blocks( $content );
- $content = wptexturize( $content );
- $content = convert_smilies( $content );
- $content = shortcode_unautop( $content );
- $content = wp_filter_content_tags( $content );
- $content = do_shortcode( $content );
- $content = str_replace( ']]>', ']]>', $content );
-
- // Wrap block template in .wp-site-blocks to allow for specific descendant styles
- // (e.g. `.wp-site-blocks > *`).
- return '' . $content . '
';
-}
-
-/**
- * Renders a 'viewport' meta tag.
- *
- * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas.
- */
-function gutenberg_viewport_meta_tag() {
- echo ' ' . "\n";
-}
-
-/**
- * Strips .php suffix from template file names.
- *
- * @access private
- *
- * @param string $template_file Template file name.
- * @return string Template file name without extension.
- */
-function gutenberg_strip_template_file_suffix( $template_file ) {
- return preg_replace( '/\.(php|html)$/', '', $template_file );
-}
-
-/**
- * Removes post details from block context when rendering a block template.
- *
- * @param array $context Default context.
- *
- * @return array Filtered context.
- */
-function gutenberg_template_render_without_post_block_context( $context ) {
- /*
- * When loading a template or template part directly and not through a page
- * that resolves it, the top-level post ID and type context get set to that
- * of the template part. Templates are just the structure of a site, and
- * they should not be available as post context because blocks like Post
- * Content would recurse infinitely.
- */
- if ( isset( $context['postType'] ) &&
- ( 'wp_template' === $context['postType'] || 'wp_template_part' === $context['postType'] ) ) {
- unset( $context['postId'] );
- unset( $context['postType'] );
- }
-
- return $context;
-}
-
-// override WordPress 5.8 filters.
-remove_filter( 'render_block_context', '_block_template_render_without_post_block_context' );
-add_filter( 'render_block_context', 'gutenberg_template_render_without_post_block_context' );
-
-/**
- * Sets the current WP_Query to return auto-draft posts.
- *
- * The auto-draft status indicates a new post, so allow the the WP_Query instance to
- * return an auto-draft post for template resolution when editing a new post.
- *
- * @param WP_Query $wp_query Current WP_Query instance, passed by reference.
- * @return void
- */
-function gutenberg_resolve_template_for_new_post( $wp_query ) {
- if ( ! $wp_query->is_main_query() ) {
- return;
- }
- remove_filter( 'pre_get_posts', 'gutenberg_resolve_template_for_new_post' );
-
- // Pages.
- $page_id = isset( $wp_query->query['page_id'] ) ? $wp_query->query['page_id'] : null;
-
- // Posts, including custom post types.
- $p = isset( $wp_query->query['p'] ) ? $wp_query->query['p'] : null;
-
- $post_id = $page_id ? $page_id : $p;
- $post = get_post( $post_id );
-
- if (
- $post &&
- 'auto-draft' === $post->post_status &&
- current_user_can( 'edit_post', $post->ID )
- ) {
- $wp_query->set( 'post_status', 'auto-draft' );
- }
-}
diff --git a/lib/compat/wordpress-5.9/blocks.php b/lib/compat/wordpress-5.9/blocks.php
deleted file mode 100644
index 5793042079871..0000000000000
--- a/lib/compat/wordpress-5.9/blocks.php
+++ /dev/null
@@ -1,247 +0,0 @@
- '',
- 'arrow' => array(
- 'next' => '→',
- 'previous' => '←',
- ),
- 'chevron' => array(
- 'next' => '»',
- 'previous' => '«',
- ),
- );
- if ( ! empty( $block->context['paginationArrow'] ) && array_key_exists( $block->context['paginationArrow'], $arrow_map ) && ! empty( $arrow_map[ $block->context['paginationArrow'] ] ) ) {
- $pagination_type = $is_next ? 'next' : 'previous';
- $arrow_attribute = $block->context['paginationArrow'];
- $arrow = $arrow_map[ $block->context['paginationArrow'] ][ $pagination_type ];
- $arrow_classes = "wp-block-query-pagination-$pagination_type-arrow is-arrow-$arrow_attribute";
- return "$arrow ";
- }
- return null;
- }
-}
-
-/**
- * Update allowed inline style attributes list.
- *
- * Note: This should be removed when the minimum required WP version is >= 5.9.
- *
- * @param string[] $attrs Array of allowed CSS attributes.
- * @return string[] CSS attributes.
- */
-function gutenberg_safe_style_attrs( $attrs ) {
- $attrs[] = 'object-position';
- $attrs[] = 'border-top-left-radius';
- $attrs[] = 'border-top-right-radius';
- $attrs[] = 'border-bottom-right-radius';
- $attrs[] = 'border-bottom-left-radius';
- $attrs[] = 'filter';
-
- return $attrs;
-}
-add_filter( 'safe_style_css', 'gutenberg_safe_style_attrs' );
-
-if ( ! function_exists( '_wp_normalize_relative_css_links' ) ) {
- /**
- * Make URLs relative to the WordPress installation.
- *
- * @since 5.9.0
- *
- * @param string $css The CSS to make URLs relative to the WordPress installation.
- * @param string $stylesheet_url The URL to the stylesheet.
- *
- * @return string The CSS with URLs made relative to the WordPress installation.
- */
- function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
- $has_src_results = preg_match_all( '#url\s*\(\s*[\'"]?\s*([^\'"\)]+)#', $css, $src_results );
- if ( $has_src_results ) {
- // Loop through the URLs to find relative ones.
- foreach ( $src_results[1] as $src_index => $src_result ) {
- // Skip if this is an absolute URL.
- if ( 0 === strpos( $src_result, 'http' ) || 0 === strpos( $src_result, '//' ) ) {
- continue;
- }
-
- // Build the absolute URL.
- $absolute_url = dirname( $stylesheet_url ) . '/' . $src_result;
- $absolute_url = str_replace( '/./', '/', $absolute_url );
- // Convert to URL related to the site root.
- $relative_url = wp_make_link_relative( $absolute_url );
-
- // Replace the URL in the CSS.
- $css = str_replace(
- $src_results[0][ $src_index ],
- str_replace( $src_result, $relative_url, $src_results[0][ $src_index ] ),
- $css
- );
- }
- }
-
- return $css;
- }
-}
-
-if ( ! function_exists( 'wp_enqueue_block_style' ) ) {
- /**
- * Enqueue a stylesheet for a specific block.
- *
- * If the theme has opted-in to separate-styles loading,
- * then the stylesheet will be enqueued on-render,
- * otherwise when the block inits.
- *
- * @since 5.9.0
- *
- * @param string $block_name The block-name, including namespace.
- * @param array $args An array of arguments [handle,src,deps,ver,media].
- *
- * @return void
- */
- function wp_enqueue_block_style( $block_name, $args ) {
- $args = wp_parse_args(
- $args,
- array(
- 'handle' => '',
- 'src' => '',
- 'deps' => array(),
- 'ver' => false,
- 'media' => 'all',
- )
- );
-
- /**
- * Callback function to register and enqueue styles.
- *
- * @param string $content When the callback is used for the render_block filter,
- * the content needs to be returned so the function parameter
- * is to ensure the content exists.
- * @return string Block content.
- */
- $callback = static function( $content ) use ( $args ) {
- // Register the stylesheet.
- if ( ! empty( $args['src'] ) ) {
- wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
- }
-
- // Add `path` data if provided.
- if ( isset( $args['path'] ) ) {
- wp_style_add_data( $args['handle'], 'path', $args['path'] );
-
- // Get the RTL file path.
- $rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
-
- // Add RTL stylesheet.
- if ( file_exists( $rtl_file_path ) ) {
- wp_style_add_data( $args['handle'], 'rtl', 'replace' );
-
- if ( is_rtl() ) {
- wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
- }
- }
- }
-
- // Enqueue the stylesheet.
- wp_enqueue_style( $args['handle'] );
-
- return $content;
- };
-
- $hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
- if ( wp_should_load_separate_core_block_assets() ) {
- /**
- * Callback function to register and enqueue styles.
- *
- * @param string $content The block content.
- * @param array $block The full block, including name and attributes.
- * @return string Block content.
- */
- $callback_separate = static function( $content, $block ) use ( $block_name, $callback ) {
- if ( ! empty( $block['blockName'] ) && $block_name === $block['blockName'] ) {
- return $callback( $content );
- }
- return $content;
- };
-
- /*
- * The filter's callback here is an anonymous function because
- * using a named function in this case is not possible.
- *
- * The function cannot be unhooked, however, users are still able
- * to dequeue the stylesheets registered/enqueued by the callback
- * which is why in this case, using an anonymous function
- * was deemed acceptable.
- */
- add_filter( 'render_block', $callback_separate, 10, 2 );
- return;
- }
-
- /*
- * The filter's callback here is an anonymous function because
- * using a named function in this case is not possible.
- *
- * The function cannot be unhooked, however, users are still able
- * to dequeue the stylesheets registered/enqueued by the callback
- * which is why in this case, using an anonymous function
- * was deemed acceptable.
- */
- add_filter( $hook, $callback );
-
- // Enqueue assets in the editor.
- add_action( 'enqueue_block_assets', $callback );
- }
-}
-
-/**
- * Allow multiple block styles.
- *
- * @since 5.9.0
- *
- * @param array $metadata Metadata for registering a block type.
- *
- * @return array
- */
-function gutenberg_multiple_block_styles( $metadata ) {
- foreach ( array( 'style', 'editorStyle' ) as $key ) {
- if ( ! empty( $metadata[ $key ] ) && is_array( $metadata[ $key ] ) ) {
- $default_style = array_shift( $metadata[ $key ] );
- foreach ( $metadata[ $key ] as $handle ) {
- $args = array( 'handle' => $handle );
- if ( 0 === strpos( $handle, 'file:' ) && isset( $metadata['file'] ) ) {
- $style_path = remove_block_asset_path_prefix( $handle );
- $args = array(
- 'handle' => sanitize_key( "{$metadata['name']}-{$style_path}" ),
- 'src' => plugins_url( $style_path, $metadata['file'] ),
- );
- }
-
- wp_enqueue_block_style( $metadata['name'], $args );
- }
-
- // Only return the 1st item in the array.
- $metadata[ $key ] = $default_style;
- }
- }
- return $metadata;
-}
-add_filter( 'block_type_metadata', 'gutenberg_multiple_block_styles' );
diff --git a/lib/compat/wordpress-5.9/class-gutenberg-block-template.php b/lib/compat/wordpress-5.9/class-gutenberg-block-template.php
deleted file mode 100644
index 4259457e77231..0000000000000
--- a/lib/compat/wordpress-5.9/class-gutenberg-block-template.php
+++ /dev/null
@@ -1,131 +0,0 @@
-namespace = 'wp/v2';
- $this->rest_base = 'global-styles';
- $this->post_type = 'wp_global_styles';
- }
-
- /**
- * Registers the controllers routes.
- *
- * @since 5.9.0
- *
- * @return void
- */
- public function register_routes() {
- // List themes global styles.
- register_rest_route(
- $this->namespace,
- // The route.
- sprintf(
- '/%s/themes/(?P%s)',
- $this->rest_base,
- // Matches theme's directory: `/themes///` or `/themes//`.
- // Excludes invalid directory name characters: `/:<>*?"|`.
- '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?'
- ),
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_theme_item' ),
- 'permission_callback' => array( $this, 'get_theme_item_permissions_check' ),
- 'args' => array(
- 'stylesheet' => array(
- 'description' => __( 'The theme identifier', 'gutenberg' ),
- 'type' => 'string',
- 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
- ),
- ),
- ),
- )
- );
-
- // Lists/updates a single global style variation based on the given id.
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base . '/(?P[\/\w-]+)',
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_item' ),
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'args' => array(
- 'id' => array(
- 'description' => __( 'The id of a template', 'gutenberg' ),
- 'type' => 'string',
- 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
- ),
- ),
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $this, 'update_item' ),
- 'permission_callback' => array( $this, 'update_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * Sanitize the global styles ID or stylesheet to decode endpoint.
- * For example, `wp/v2/global-styles/twentytwentytwo%200.4.0`
- * would be decoded to `twentytwentytwo 0.4.0`.
- *
- * @since 5.9.0
- *
- * @param string $id_or_stylesheet Global styles ID or stylesheet.
- * @return string Sanitized global styles ID or stylesheet.
- */
- public function _sanitize_global_styles_callback( $id_or_stylesheet ) {
- return urldecode( $id_or_stylesheet );
- }
-
- /**
- * Checks if a given request has access to read a single global style.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
- */
- public function get_item_permissions_check( $request ) {
- $post = $this->get_post( $request['id'] );
- if ( is_wp_error( $post ) ) {
- return $post;
- }
-
- if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
- return new WP_Error(
- 'rest_forbidden_context',
- __( 'Sorry, you are not allowed to edit this global style.', 'gutenberg' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- if ( ! $this->check_read_permission( $post ) ) {
- return new WP_Error(
- 'rest_cannot_view',
- __( 'Sorry, you are not allowed to view this global style.', 'gutenberg' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- return true;
- }
-
- /**
- * Checks if a global style can be read.
- *
- * @since 5.9.0
- *
- * @param WP_Post $post Post object.
- * @return bool Whether the post can be read.
- */
- protected function check_read_permission( $post ) {
- return current_user_can( 'read_post', $post->ID );
- }
-
- /**
- * Returns the given global styles config.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request The request instance.
- *
- * @return WP_REST_Response|WP_Error
- */
- public function get_item( $request ) {
- $post = $this->get_post( $request['id'] );
- if ( is_wp_error( $post ) ) {
- return $post;
- }
-
- return $this->prepare_item_for_response( $post, $request );
- }
-
- /**
- * Checks if a given request has access to write a single global styles config.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
- */
- public function update_item_permissions_check( $request ) {
- $post = $this->get_post( $request['id'] );
- if ( is_wp_error( $post ) ) {
- return $post;
- }
-
- if ( $post && ! $this->check_update_permission( $post ) ) {
- return new WP_Error(
- 'rest_cannot_edit',
- __( 'Sorry, you are not allowed to edit this global style.', 'gutenberg' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- return true;
- }
-
- /**
- * Checks if a global style can be edited.
- *
- * @since 5.9.0
- *
- * @param WP_Post $post Post object.
- * @return bool Whether the post can be edited.
- */
- protected function check_update_permission( $post ) {
- return current_user_can( 'edit_post', $post->ID );
- }
-
- /**
- * Updates a single global style config.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function update_item( $request ) {
- $post_before = $this->get_post( $request['id'] );
- if ( is_wp_error( $post_before ) ) {
- return $post_before;
- }
-
- $changes = $this->prepare_item_for_database( $request );
- $result = wp_update_post( wp_slash( (array) $changes ), true, false );
- if ( is_wp_error( $result ) ) {
- return $result;
- }
-
- $post = get_post( $request['id'] );
- $fields_update = $this->update_additional_fields_for_object( $post, $request );
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- wp_after_insert_post( $post, true, $post_before );
-
- $response = $this->prepare_item_for_response( $post, $request );
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Prepares a single global styles config for update.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request Request object.
- * @return stdClass Changes to pass to wp_update_post.
- */
- protected function prepare_item_for_database( $request ) {
- $changes = new stdClass();
- $changes->ID = $request['id'];
-
- $post = get_post( $request['id'] );
- $existing_config = array();
- if ( $post ) {
- $existing_config = json_decode( $post->post_content, true );
- $json_decoding_error = json_last_error();
- if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) ||
- ! $existing_config['isGlobalStylesUserThemeJSON'] ) {
- $existing_config = array();
- }
- }
-
- if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) {
- $config = array();
- if ( isset( $request['styles'] ) ) {
- $config['styles'] = $request['styles'];
- } elseif ( isset( $existing_config['styles'] ) ) {
- $config['styles'] = $existing_config['styles'];
- }
- if ( isset( $request['settings'] ) ) {
- $config['settings'] = $request['settings'];
- } elseif ( isset( $existing_config['settings'] ) ) {
- $config['settings'] = $existing_config['settings'];
- }
- $config['isGlobalStylesUserThemeJSON'] = true;
- $config['version'] = WP_Theme_JSON_Gutenberg::LATEST_SCHEMA;
- $changes->post_content = wp_json_encode( $config );
- }
-
- // Post title.
- if ( isset( $request['title'] ) ) {
- if ( is_string( $request['title'] ) ) {
- $changes->post_title = $request['title'];
- } elseif ( ! empty( $request['title']['raw'] ) ) {
- $changes->post_title = $request['title']['raw'];
- }
- }
-
- return $changes;
- }
-
- /**
- * Prepare a global styles config output for response.
- *
- * @since 5.9.0
- *
- * @param WP_Post $post Global Styles post object.
- * @param WP_REST_Request $request Request object.
- * @return WP_REST_Response Response object.
- */
- public function prepare_item_for_response( $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
- $raw_config = json_decode( $post->post_content, true );
- $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON'];
- $config = array();
- if ( $is_global_styles_user_theme_json ) {
- $config = ( new WP_Theme_JSON_Gutenberg( $raw_config, 'custom' ) )->get_raw_data();
- }
-
- // Base fields for every post.
- $data = array();
- $fields = $this->get_fields_for_response( $request );
-
- if ( rest_is_field_included( 'id', $fields ) ) {
- $data['id'] = $post->ID;
- }
-
- if ( rest_is_field_included( 'title', $fields ) ) {
- $data['title'] = array();
- }
- if ( rest_is_field_included( 'title.raw', $fields ) ) {
- $data['title']['raw'] = $post->post_title;
- }
- if ( rest_is_field_included( 'title.rendered', $fields ) ) {
- add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
-
- $data['title']['rendered'] = get_the_title( $post->ID );
-
- remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
- }
-
- if ( rest_is_field_included( 'settings', $fields ) ) {
- $data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass();
- }
-
- if ( rest_is_field_included( 'styles', $fields ) ) {
- $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass();
- }
-
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
-
- // Wrap the data in a response object.
- $response = rest_ensure_response( $data );
-
- $links = $this->prepare_links( $post->ID );
- $response->add_links( $links );
- if ( ! empty( $links['self']['href'] ) ) {
- $actions = $this->get_available_actions();
- $self = $links['self']['href'];
- foreach ( $actions as $rel ) {
- $response->add_link( $rel, $self );
- }
- }
-
- return $response;
- }
-
- /**
- * Get the post, if the ID is valid.
- *
- * @since 5.9.0
- *
- * @param int $id Supplied ID.
- * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
- */
- protected function get_post( $id ) {
- $error = new WP_Error(
- 'rest_global_styles_not_found',
- __( 'No global styles config exist with that id.', 'gutenberg' ),
- array( 'status' => 404 )
- );
-
- $id = (int) $id;
- if ( $id <= 0 ) {
- return $error;
- }
-
- $post = get_post( $id );
- if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
- return $error;
- }
-
- return $post;
- }
-
-
- /**
- * Prepares links for the request.
- *
- * @since 5.9.0
- *
- * @param integer $id ID.
- * @return array Links for the given post.
- */
- protected function prepare_links( $id ) {
- $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
-
- $links = array(
- 'self' => array(
- 'href' => rest_url( trailingslashit( $base ) . $id ),
- ),
- );
-
- return $links;
- }
-
- /**
- * Get the link relations available for the post and current user.
- *
- * @since 5.9.0
- *
- * @return array List of link relations.
- */
- protected function get_available_actions() {
- $rels = array();
-
- $post_type = get_post_type_object( $this->post_type );
- if ( current_user_can( $post_type->cap->publish_posts ) ) {
- $rels[] = 'https://api.w.org/action-publish';
- }
-
- return $rels;
- }
-
- /**
- * Overwrites the default protected title format.
- *
- * By default, WordPress will show password protected posts with a title of
- * "Protected: %s", as the REST API communicates the protected status of a post
- * in a machine readable format, we remove the "Protected: " prefix.
- *
- * @since 5.9.0
- *
- * @return string Protected title format.
- */
- public function protected_title_format() {
- return '%s';
- }
-
- /**
- * Retrieves the query params for the global styles collection.
- *
- * @since 5.9.0
- *
- * @return array Collection parameters.
- */
- public function get_collection_params() {
- return array();
- }
-
- /**
- * Retrieves the global styles type' schema, conforming to JSON Schema.
- *
- * @since 5.9.0
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- if ( $this->schema ) {
- return $this->add_additional_fields_schema( $this->schema );
- }
-
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => $this->post_type,
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'ID of global styles config.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'embed', 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'styles' => array(
- 'description' => __( 'Global styles.', 'gutenberg' ),
- 'type' => array( 'object' ),
- 'context' => array( 'view', 'edit' ),
- ),
- 'settings' => array(
- 'description' => __( 'Global settings.', 'gutenberg' ),
- 'type' => array( 'object' ),
- 'context' => array( 'view', 'edit' ),
- ),
- 'title' => array(
- 'description' => __( 'Title of the global styles variation.', 'gutenberg' ),
- 'type' => array( 'object', 'string' ),
- 'default' => '',
- 'context' => array( 'embed', 'view', 'edit' ),
- 'properties' => array(
- 'raw' => array(
- 'description' => __( 'Title for the global styles variation, as it exists in the database.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- ),
- 'rendered' => array(
- 'description' => __( 'HTML title for the post, transformed for display.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- ),
- ),
- ),
- );
-
- $this->schema = $schema;
-
- return $this->add_additional_fields_schema( $this->schema );
- }
-
- /**
- * Checks if a given request has access to read a single theme global styles config.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
- */
- public function get_theme_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
- // Verify if the current user has edit_theme_options capability.
- // This capability is required to edit/view/delete templates.
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error(
- 'rest_cannot_manage_global_styles',
- __( 'Sorry, you are not allowed to access the global styles on this site.', 'gutenberg' ),
- array(
- 'status' => rest_authorization_required_code(),
- )
- );
- }
-
- return true;
- }
-
- /**
- * Returns the given theme global styles config.
- *
- * @since 5.9.0
- *
- * @param WP_REST_Request $request The request instance.
- * @return WP_REST_Response|WP_Error
- */
- public function get_theme_item( $request ) {
- if ( wp_get_theme()->get_stylesheet() !== $request['stylesheet'] ) {
- // This endpoint only supports the active theme for now.
- return new WP_Error(
- 'rest_theme_not_found',
- __( 'Theme not found.', 'gutenberg' ),
- array( 'status' => 404 )
- );
- }
-
- $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' );
- $data = array();
- $fields = $this->get_fields_for_response( $request );
-
- if ( rest_is_field_included( 'settings', $fields ) ) {
- $data['settings'] = $theme->get_settings();
- }
-
- if ( rest_is_field_included( 'styles', $fields ) ) {
- $raw_data = $theme->get_raw_data();
- if ( isset( $raw_data['styles'] ) ) {
- $data['styles'] = $raw_data['styles'];
- }
- }
-
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
-
- $response = rest_ensure_response( $data );
-
- $links = array(
- 'self' => array(
- 'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ),
- ),
- );
-
- $response->add_links( $links );
-
- return $response;
- }
- }
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-rest-menu-items-controller.php b/lib/compat/wordpress-5.9/class-wp-rest-menu-items-controller.php
deleted file mode 100644
index 4102184864d78..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-rest-menu-items-controller.php
+++ /dev/null
@@ -1,1169 +0,0 @@
- true );
-
- /**
- * Overrides the route registration to support "allow_batch".
- *
- * @since 9.2.0
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permissions_check' ),
- 'args' => $this->get_collection_params(),
- ),
- array(
- 'methods' => WP_REST_Server::CREATABLE,
- 'callback' => array( $this, 'create_item' ),
- 'permission_callback' => array( $this, 'create_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
- ),
- 'allow_batch' => $this->allow_batch,
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
-
- $schema = $this->get_item_schema();
- $get_item_args = array(
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
- );
- if ( isset( $schema['properties']['password'] ) ) {
- $get_item_args['password'] = array(
- 'description' => __( 'The password for the post if it is password protected.', 'gutenberg' ),
- 'type' => 'string',
- );
- }
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base . '/(?P[\d]+)',
- array(
- 'args' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the object.', 'gutenberg' ),
- 'type' => 'integer',
- ),
- ),
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_item' ),
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'args' => $get_item_args,
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $this, 'update_item' ),
- 'permission_callback' => array( $this, 'update_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
- ),
- array(
- 'methods' => WP_REST_Server::DELETABLE,
- 'callback' => array( $this, 'delete_item' ),
- 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
- 'args' => array(
- 'force' => array(
- 'type' => 'boolean',
- 'default' => false,
- 'description' => __( 'Whether to bypass Trash and force deletion.', 'gutenberg' ),
- ),
- ),
- ),
- 'allow_batch' => $this->allow_batch,
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * Get the post, if the ID is valid.
- *
- * @param int $id Supplied ID.
- *
- * @return object|WP_Error Post object if ID is valid, WP_Error otherwise.
- */
- protected function get_post( $id ) {
- return $this->get_nav_menu_item( $id );
- }
-
- /**
- * Get the nav menu item, if the ID is valid.
- *
- * @param int $id Supplied ID.
- *
- * @return object|WP_Error Post object if ID is valid, WP_Error otherwise.
- */
- protected function get_nav_menu_item( $id ) {
- $post = parent::get_post( $id );
- if ( is_wp_error( $post ) ) {
- return $post;
- }
- $nav_item = wp_setup_nav_menu_item( $post );
-
- return $nav_item;
- }
-
- /**
- * Checks if a given request has access to read a menu item if they have access to edit them.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return bool|WP_Error True if the request has read access for the item, WP_Error object otherwise.
- */
- public function get_item_permissions_check( $request ) {
- $post = $this->get_post( $request['id'] );
- if ( is_wp_error( $post ) ) {
- return $post;
- }
- if ( $post && ! $this->check_update_permission( $post ) ) {
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you cannot view this menu item, unless you have access to permission edit it. ', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
-
- return parent::get_item_permissions_check( $request );
- }
-
- /**
- * Checks if a given request has access to read menu items if they have access to edit them.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
- */
- public function get_items_permissions_check( $request ) {
- $post_type = get_post_type_object( $this->post_type );
- if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
- if ( 'edit' === $request['context'] ) {
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit posts in this post type.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you cannot view these menu items, unless you have access to permission edit them. ', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return true;
- }
-
- /**
- * Creates a single post.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function create_item( $request ) {
- if ( ! empty( $request['id'] ) ) {
- return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.', 'gutenberg' ), array( 'status' => 400 ) );
- }
-
- $prepared_nav_item = $this->prepare_item_for_database( $request );
-
- if ( is_wp_error( $prepared_nav_item ) ) {
- return $prepared_nav_item;
- }
- $prepared_nav_item = (array) $prepared_nav_item;
-
- $nav_menu_item_id = wp_update_nav_menu_item( $prepared_nav_item['menu-id'], $prepared_nav_item['menu-item-db-id'], wp_slash( $prepared_nav_item ) );
- if ( is_wp_error( $nav_menu_item_id ) ) {
- if ( 'db_insert_error' === $nav_menu_item_id->get_error_code() ) {
- $nav_menu_item_id->add_data( array( 'status' => 500 ) );
- } else {
- $nav_menu_item_id->add_data( array( 'status' => 400 ) );
- }
-
- return $nav_menu_item_id;
- }
-
- $nav_menu_item = $this->get_nav_menu_item( $nav_menu_item_id );
- if ( is_wp_error( $nav_menu_item ) ) {
- $nav_menu_item->add_data( array( 'status' => 404 ) );
-
- return $nav_menu_item;
- }
-
- /**
- * Fires after a single nav menu item is created or updated via the REST API.
- *
- * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
- *
- * @param object $nav_menu_item Inserted or updated nav item object.
- * @param WP_REST_Request $request Request object.
- * @param bool $creating True when creating a post, false when updating.
- * SA
- */
- do_action( "rest_insert_{$this->post_type}", $nav_menu_item, $request, true );
-
- $schema = $this->get_item_schema();
-
- if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
- $meta_update = $this->meta->update_value( $request['meta'], $nav_menu_item_id );
-
- if ( is_wp_error( $meta_update ) ) {
- return $meta_update;
- }
- }
-
- $nav_menu_item = $this->get_nav_menu_item( $nav_menu_item_id );
- $fields_update = $this->update_additional_fields_for_object( $nav_menu_item, $request );
-
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- $request->set_param( 'context', 'edit' );
-
- /**
- * Fires after a single nav menu item is completely created or updated via the REST API.
- *
- * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
- *
- * @param object $nav_menu_item Inserted or updated nav item object.
- * @param WP_REST_Request $request Request object.
- * @param bool $creating True when creating a post, false when updating.
- */
- do_action( "rest_after_insert_{$this->post_type}", $nav_menu_item, $request, true );
-
- $response = $this->prepare_item_for_response( $nav_menu_item, $request );
- $response = rest_ensure_response( $response );
-
- $response->set_status( 201 );
- $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $nav_menu_item_id ) ) );
-
- return $response;
- }
-
- /**
- * Updates a single nav menu item.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function update_item( $request ) {
- $valid_check = $this->get_nav_menu_item( $request['id'] );
- if ( is_wp_error( $valid_check ) ) {
- return $valid_check;
- }
-
- $prepared_nav_item = $this->prepare_item_for_database( $request );
-
- if ( is_wp_error( $prepared_nav_item ) ) {
- return $prepared_nav_item;
- }
-
- $prepared_nav_item = (array) $prepared_nav_item;
-
- $nav_menu_item_id = wp_update_nav_menu_item( $prepared_nav_item['menu-id'], $prepared_nav_item['menu-item-db-id'], wp_slash( $prepared_nav_item ) );
-
- if ( is_wp_error( $nav_menu_item_id ) ) {
- if ( 'db_update_error' === $nav_menu_item_id->get_error_code() ) {
- $nav_menu_item_id->add_data( array( 'status' => 500 ) );
- } else {
- $nav_menu_item_id->add_data( array( 'status' => 400 ) );
- }
-
- return $nav_menu_item_id;
- }
-
- $nav_menu_item = $this->get_nav_menu_item( $nav_menu_item_id );
- if ( is_wp_error( $nav_menu_item ) ) {
- $nav_menu_item->add_data( array( 'status' => 404 ) );
-
- return $nav_menu_item;
- }
-
- /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
- do_action( "rest_insert_{$this->post_type}", $nav_menu_item, $request, false );
-
- $schema = $this->get_item_schema();
-
- if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
- $meta_update = $this->meta->update_value( $request['meta'], $nav_menu_item->ID );
-
- if ( is_wp_error( $meta_update ) ) {
- return $meta_update;
- }
- }
-
- $nav_menu_item = $this->get_nav_menu_item( $nav_menu_item_id );
- $fields_update = $this->update_additional_fields_for_object( $nav_menu_item, $request );
-
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- $request->set_param( 'context', 'edit' );
-
- /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
- do_action( "rest_after_insert_{$this->post_type}", $nav_menu_item, $request, false );
-
- $response = $this->prepare_item_for_response( $nav_menu_item, $request );
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Deletes a single menu item.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return WP_REST_Response|WP_Error True on success, or WP_Error object on failure.
- */
- public function delete_item( $request ) {
- $menu_item = $this->get_nav_menu_item( $request['id'] );
- if ( is_wp_error( $menu_item ) ) {
- return $menu_item;
- }
-
- $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
-
- // We don't support trashing for menu items.
- if ( ! $force ) {
- /* translators: %s: force=true */
- return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Menu items do not support trashing. Set '%s' to delete.", 'gutenberg' ), 'force=true' ), array( 'status' => 501 ) );
- }
-
- $previous = $this->prepare_item_for_response( $menu_item, $request );
-
- $result = wp_delete_post( $request['id'], true );
-
- if ( ! $result ) {
- return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.', 'gutenberg' ), array( 'status' => 500 ) );
- }
-
- $response = new WP_REST_Response();
- $response->set_data(
- array(
- 'deleted' => true,
- 'previous' => $previous->get_data(),
- )
- );
-
- /**
- * Fires immediately after a single menu item is deleted or trashed via the REST API.
- *
- * They dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
- *
- * @param Object $menu_item The deleted or trashed menu item.
- * @param WP_REST_Response $response The response data.
- * @param WP_REST_Request $request The request sent to the API.
- */
- do_action( "rest_delete_{$this->post_type}", $menu_item, $response, $request );
-
- return $response;
- }
-
- /**
- * Prepares a single post for create or update.
- *
- * @param WP_REST_Request $request Request object.
- *
- * @return stdClass|WP_Error
- */
- protected function prepare_item_for_database( $request ) {
- $menu_item_db_id = $request['id'];
- $menu_item_obj = $this->get_nav_menu_item( $menu_item_db_id );
- // Need to persist the menu item data. See https://core.trac.wordpress.org/ticket/28138 .
- if ( ! is_wp_error( $menu_item_obj ) ) {
- // Correct the menu position if this was the first item. See https://core.trac.wordpress.org/ticket/28140 .
- $position = ( 0 === $menu_item_obj->menu_order ) ? 1 : $menu_item_obj->menu_order;
-
- $prepared_nav_item = array(
- 'menu-item-db-id' => $menu_item_db_id,
- 'menu-item-object-id' => $menu_item_obj->object_id,
- 'menu-item-object' => $menu_item_obj->object,
- 'menu-item-parent-id' => $menu_item_obj->menu_item_parent,
- 'menu-item-position' => $position,
- 'menu-item-type' => $menu_item_obj->type,
- 'menu-item-title' => $menu_item_obj->title,
- 'menu-item-url' => $menu_item_obj->url,
- 'menu-item-description' => $menu_item_obj->description,
- 'menu-item-content' => $menu_item_obj->menu_item_content,
- 'menu-item-attr-title' => $menu_item_obj->attr_title,
- 'menu-item-target' => $menu_item_obj->target,
- // Stored in the database as a string.
- 'menu-item-classes' => implode( ' ', $menu_item_obj->classes ),
- 'menu-item-xfn' => $menu_item_obj->xfn,
- 'menu-item-status' => $menu_item_obj->post_status,
- 'menu-id' => $this->get_menu_id( $menu_item_db_id ),
- );
- } else {
- $prepared_nav_item = array(
- 'menu-id' => 0,
- 'menu-item-db-id' => 0,
- 'menu-item-object-id' => 0,
- 'menu-item-object' => '',
- 'menu-item-parent-id' => 0,
- 'menu-item-position' => 1,
- 'menu-item-type' => 'custom',
- 'menu-item-title' => '',
- 'menu-item-url' => '',
- 'menu-item-description' => '',
- 'menu-item-content' => '',
- 'menu-item-attr-title' => '',
- 'menu-item-target' => '',
- 'menu-item-classes' => '',
- 'menu-item-xfn' => '',
- 'menu-item-status' => 'publish',
- );
- }
-
- $mapping = array(
- 'menu-item-db-id' => 'id',
- 'menu-item-object-id' => 'object_id',
- 'menu-item-object' => 'object',
- 'menu-item-parent-id' => 'parent',
- 'menu-item-position' => 'menu_order',
- 'menu-item-type' => 'type',
- 'menu-item-url' => 'url',
- 'menu-item-description' => 'description',
- 'menu-item-attr-title' => 'attr_title',
- 'menu-item-target' => 'target',
- 'menu-item-classes' => 'classes',
- 'menu-item-xfn' => 'xfn',
- 'menu-item-status' => 'status',
- );
-
- $schema = $this->get_item_schema();
-
- foreach ( $mapping as $original => $api_request ) {
- if ( ! empty( $schema['properties'][ $api_request ] ) && isset( $request[ $api_request ] ) ) {
- $check = rest_validate_value_from_schema( $request[ $api_request ], $schema['properties'][ $api_request ] );
- if ( is_wp_error( $check ) ) {
- $check->add_data( array( 'status' => 400 ) );
- return $check;
- }
- $prepared_nav_item[ $original ] = rest_sanitize_value_from_schema( $request[ $api_request ], $schema['properties'][ $api_request ] );
- }
- }
-
- $taxonomy = get_taxonomy( 'nav_menu' );
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
- // If menus submitted, cast to int.
- if ( isset( $request[ $base ] ) && ! empty( $request[ $base ] ) ) {
- $prepared_nav_item['menu-id'] = absint( $request[ $base ] );
- }
-
- // Nav menu title.
- if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) {
- if ( is_string( $request['title'] ) ) {
- $prepared_nav_item['menu-item-title'] = $request['title'];
- } elseif ( ! empty( $request['title']['raw'] ) ) {
- $prepared_nav_item['menu-item-title'] = $request['title']['raw'];
- }
- }
-
- // Nav menu content.
- if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) {
- if ( is_string( $request['content'] ) ) {
- $prepared_nav_item['menu-item-content'] = $request['content'];
- } elseif ( isset( $request['content']['raw'] ) ) {
- $prepared_nav_item['menu-item-content'] = $request['content']['raw'];
- }
- }
-
- $error = new WP_Error();
-
- // Check if object id exists before saving.
- if ( ! $prepared_nav_item['menu-item-object'] ) {
- // If taxonony, check if term exists.
- if ( 'taxonomy' === $prepared_nav_item['menu-item-type'] ) {
- $original = get_term( absint( $prepared_nav_item['menu-item-object-id'] ) );
- if ( empty( $original ) || is_wp_error( $original ) ) {
- $error->add( 'rest_term_invalid_id', __( 'Invalid term ID.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- $prepared_nav_item['menu-item-object'] = get_term_field( 'taxonomy', $original );
-
- // If post, check if post object exists.
- } elseif ( 'post_type' === $prepared_nav_item['menu-item-type'] ) {
- $original = get_post( absint( $prepared_nav_item['menu-item-object-id'] ) );
- if ( empty( $original ) ) {
- $error->add( 'rest_post_invalid_id', __( 'Invalid post ID.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- $prepared_nav_item['menu-item-object'] = get_post_type( $original );
- }
- }
-
- // If post type archive, check if post type exists.
- if ( 'post_type_archive' === $prepared_nav_item['menu-item-type'] ) {
- $post_type = ( $prepared_nav_item['menu-item-object'] ) ? $prepared_nav_item['menu-item-object'] : false;
- $original = get_post_type_object( $post_type );
- if ( empty( $original ) ) {
- $error->add( 'rest_post_invalid_type', __( 'Invalid post type.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- }
-
- // Check if menu item is type custom, then title and url are required.
- if ( 'custom' === $prepared_nav_item['menu-item-type'] ) {
- if ( '' === $prepared_nav_item['menu-item-title'] ) {
- $error->add( 'rest_title_required', __( 'Title required if menu item of type custom.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- if ( empty( $prepared_nav_item['menu-item-url'] ) ) {
- $error->add( 'rest_url_required', __( 'URL required if menu item of type custom.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- }
-
- // If menu item is type block, then content is required.
- if ( 'block' === $prepared_nav_item['menu-item-type'] && empty( $prepared_nav_item['menu-item-content'] ) ) {
- $error->add( 'rest_content_required', __( 'Content required if menu item of type block.', 'gutenberg' ), array( 'status' => 400 ) );
- }
-
- // Valid url.
- if ( '' !== $prepared_nav_item['menu-item-url'] ) {
- $prepared_nav_item['menu-item-url'] = esc_url_raw( $prepared_nav_item['menu-item-url'] );
- if ( '' === $prepared_nav_item['menu-item-url'] ) {
- // Fail sanitization if URL is invalid.
- $error->add( 'invalid_url', __( 'Invalid URL.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- }
-
- if ( $error->has_errors() ) {
- return $error;
- }
-
- foreach ( array( 'menu-item-object-id', 'menu-item-parent-id' ) as $key ) {
- // Note we need to allow negative-integer IDs for previewed objects not inserted yet.
- $prepared_nav_item[ $key ] = (int) $prepared_nav_item[ $key ];
- }
-
- foreach ( array( 'menu-item-type', 'menu-item-object', 'menu-item-target' ) as $key ) {
- $prepared_nav_item[ $key ] = sanitize_key( $prepared_nav_item[ $key ] );
- }
-
- // Valid xfn and classes are an array.
- foreach ( array( 'menu-item-xfn', 'menu-item-classes' ) as $key ) {
- $value = $prepared_nav_item[ $key ];
- if ( ! is_array( $value ) ) {
- $value = wp_parse_list( $value );
- }
- $prepared_nav_item[ $key ] = implode( ' ', array_map( 'sanitize_html_class', $value ) );
- }
-
- // Only draft / publish are valid post status for menu items.
- if ( 'publish' !== $prepared_nav_item['menu-item-status'] ) {
- $prepared_nav_item['menu-item-status'] = 'draft';
- }
-
- $prepared_nav_item = (object) $prepared_nav_item;
-
- /**
- * Filters a post before it is inserted via the REST API.
- *
- * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
- *
- * @param stdClass $prepared_post An object representing a single post prepared
- * for inserting or updating the database.
- * @param WP_REST_Request $request Request object.
- */
- return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_nav_item, $request );
- }
-
- /**
- * Prepares a single post output for response.
- *
- * @param object $post Post object.
- * @param WP_REST_Request $request Request object.
- *
- * @return WP_REST_Response Response object.
- */
- public function prepare_item_for_response( $post, $request ) {
- $fields = $this->get_fields_for_response( $request );
-
- // Base fields for every post.
- $menu_item = wp_setup_nav_menu_item( $post );
- $data = array();
- if ( rest_is_field_included( 'id', $fields ) ) {
- $data['id'] = $menu_item->ID;
- }
-
- if ( rest_is_field_included( 'title', $fields ) ) {
- $data['title'] = array();
- }
- if ( rest_is_field_included( 'title.raw', $fields ) ) {
- $data['title']['raw'] = $menu_item->title;
- }
- if ( rest_is_field_included( 'title.rendered', $fields ) ) {
- add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
-
- /** This filter is documented in wp-includes/post-template.php */
- $title = apply_filters( 'the_title', $menu_item->title, $menu_item->ID );
-
- /** This filter is documented in wp-includes/class-walker-nav-menu.php */
- $title = apply_filters( 'nav_menu_item_title', $title, $menu_item, null, 0 );
-
- $data['title']['rendered'] = $title;
-
- remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
- }
-
- if ( rest_is_field_included( 'status', $fields ) ) {
- $data['status'] = $menu_item->post_status;
- }
-
- if ( rest_is_field_included( 'url', $fields ) ) {
- $data['url'] = $menu_item->url;
- }
-
- if ( rest_is_field_included( 'attr_title', $fields ) ) {
- // Same as post_excerpt.
- $data['attr_title'] = $menu_item->attr_title;
- }
-
- if ( rest_is_field_included( 'description', $fields ) ) {
- // Same as post_content.
- $data['description'] = $menu_item->description;
- }
-
- if ( rest_is_field_included( 'type', $fields ) ) {
- // Using 'item_type' since 'type' already exists.
- $data['type'] = $menu_item->type;
- }
-
- if ( rest_is_field_included( 'type_label', $fields ) ) {
- // Using 'item_type_label' to match up with 'item_type' - IS READ ONLY!
- $data['type_label'] = $menu_item->type_label;
- }
-
- if ( rest_is_field_included( 'object', $fields ) ) {
- $data['object'] = $menu_item->object;
- }
-
- if ( rest_is_field_included( 'object_id', $fields ) ) {
- // Usually is a string, but lets expose as an integer.
- $data['object_id'] = absint( $menu_item->object_id );
- }
-
- if ( rest_is_field_included( 'content', $fields ) ) {
- $data['content'] = array();
- }
- if ( rest_is_field_included( 'content.raw', $fields ) ) {
- $data['content']['raw'] = $menu_item->content;
- }
- if ( rest_is_field_included( 'content.rendered', $fields ) ) {
- /** This filter is documented in wp-includes/post-template.php */
- $data['content']['rendered'] = apply_filters( 'the_content', $menu_item->content );
- }
- if ( rest_is_field_included( 'content.block_version', $fields ) ) {
- $data['content']['block_version'] = block_version( $menu_item->content );
- }
-
- if ( rest_is_field_included( 'parent', $fields ) ) {
- // Same as post_parent, expose as integer.
- $data['parent'] = (int) $menu_item->menu_item_parent;
- }
-
- if ( rest_is_field_included( 'menu_order', $fields ) ) {
- // Same as post_parent, expose as integer.
- $data['menu_order'] = (int) $menu_item->menu_order;
- }
-
- if ( rest_is_field_included( 'menu_id', $fields ) ) {
- $data['menu_id'] = $this->get_menu_id( $menu_item->ID );
- }
-
- if ( rest_is_field_included( 'target', $fields ) ) {
- $data['target'] = $menu_item->target;
- }
-
- if ( rest_is_field_included( 'classes', $fields ) ) {
- $data['classes'] = (array) $menu_item->classes;
- }
-
- if ( rest_is_field_included( 'xfn', $fields ) ) {
- $data['xfn'] = array_map( 'sanitize_html_class', explode( ' ', $menu_item->xfn ) );
- }
-
- if ( rest_is_field_included( 'invalid', $fields ) ) {
- $data['invalid'] = (bool) $menu_item->_invalid;
- }
-
- if ( rest_is_field_included( 'meta', $fields ) ) {
- $data['meta'] = $this->meta->get_value( $menu_item->ID, $request );
- }
-
- $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
-
- foreach ( $taxonomies as $taxonomy ) {
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
-
- if ( rest_is_field_included( $base, $fields ) ) {
- $terms = get_the_terms( $post, $taxonomy->name );
- $term_ids = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
- if ( 'nav_menu' === $taxonomy->name ) {
- $data[ $base ] = $term_ids ? array_shift( $term_ids ) : 0;
- } else {
- $data[ $base ] = $term_ids;
- }
- }
- }
-
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
-
- // Wrap the data in a response object.
- $response = rest_ensure_response( $data );
-
- $links = $this->prepare_links( $menu_item );
- $response->add_links( $links );
-
- if ( ! empty( $links['self']['href'] ) ) {
- $actions = $this->get_available_actions( $menu_item, $request );
-
- $self = $links['self']['href'];
-
- foreach ( $actions as $rel ) {
- $response->add_link( $rel, $self );
- }
- }
-
- /**
- * Filters the post data for a response.
- *
- * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
- *
- * @param WP_REST_Response $response The response object.
- * @param object $post Post object.
- * @param WP_REST_Request $request Request object.
- */
- return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
- }
-
- /**
- * Prepares links for the request.
- *
- * @param object $menu_item Menu object.
- *
- * @return array Links for the given post.
- */
- protected function prepare_links( $menu_item ) {
- $links = parent::prepare_links( $menu_item );
- if ( empty( $menu_item->object_id ) ) {
- return $links;
- }
-
- $path = '';
- $type = '';
- $key = $menu_item->type;
- if ( 'post_type' === $menu_item->type ) {
- $path = rest_get_route_for_post( $menu_item->object_id );
- $type = get_post_type( $menu_item->object_id );
- } elseif ( 'taxonomy' === $menu_item->type ) {
- $path = rest_get_route_for_term( $menu_item->object_id );
- $type = get_term_field( 'taxonomy', $menu_item->object_id );
- }
-
- if ( $path && $type ) {
- $links['https://api.w.org/menu-item-object'][] = array(
- 'href' => rest_url( $path ),
- $key => $type,
- 'embeddable' => true,
- );
- }
-
- return $links;
- }
-
- /**
- * Retrieve Link Description Objects that should be added to the Schema for the posts collection.
- *
- * @return array
- */
- protected function get_schema_links() {
- $links = parent::get_schema_links();
- $href = rest_url( "{$this->namespace}/{$this->rest_base}/{id}" );
- $links[] = array(
- 'rel' => 'https://api.w.org/object',
- 'title' => __( 'Get linked object.', 'gutenberg' ),
- 'href' => $href,
- 'targetSchema' => array(
- 'type' => 'object',
- 'properties' => array(
- 'object' => array(
- 'type' => 'integer',
- ),
- ),
- ),
- );
-
- return $links;
- }
-
- /**
- * Retrieves the term's schema, conforming to JSON Schema.
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => $this->post_type,
- 'type' => 'object',
- );
-
- $schema['properties']['title'] = array(
- 'description' => __( 'The title for the object.', 'gutenberg' ),
- 'type' => 'object',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'arg_options' => array(
- // Note: sanitization implemented in self::prepare_item_for_database().
- 'sanitize_callback' => null,
- // Note: validation implemented in self::prepare_item_for_database().
- 'validate_callback' => null,
- ),
- 'properties' => array(
- 'raw' => array(
- 'description' => __( 'Title for the object, as it exists in the database.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'edit' ),
- ),
- 'rendered' => array(
- 'description' => __( 'HTML title for the object, transformed for display.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- ),
- );
-
- $schema['properties']['id'] = array(
- 'description' => __( 'Unique identifier for the object.', 'gutenberg' ),
- 'type' => 'integer',
- 'default' => 0,
- 'minimum' => 0,
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- );
-
- $schema['properties']['type_label'] = array(
- 'description' => __( 'Name of type.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- );
-
- $schema['properties']['type'] = array(
- 'description' => __( 'The family of objects originally represented, such as "post_type" or "taxonomy".', 'gutenberg' ),
- 'type' => 'string',
- 'enum' => array( 'taxonomy', 'post_type', 'post_type_archive', 'custom', 'block' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'default' => 'custom',
- );
-
- $schema['properties']['status'] = array(
- 'description' => __( 'A named status for the object.', 'gutenberg' ),
- 'type' => 'string',
- 'enum' => array_keys( get_post_stati( array( 'internal' => false ) ) ),
- 'default' => 'publish',
- 'context' => array( 'view', 'edit', 'embed' ),
- );
-
- $schema['properties']['parent'] = array(
- 'description' => __( 'The ID for the parent of the object.', 'gutenberg' ),
- 'type' => 'integer',
- 'minimum' => 0,
- 'default' => 0,
- 'context' => array( 'view', 'edit', 'embed' ),
- );
-
- $schema['properties']['attr_title'] = array(
- 'description' => __( 'Text for the title attribute of the link element for this menu item.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'arg_options' => array(
- 'sanitize_callback' => 'sanitize_text_field',
- ),
- );
-
- $schema['properties']['classes'] = array(
- 'description' => __( 'Class names for the link element of this menu item.', 'gutenberg' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string',
- ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'arg_options' => array(
- 'sanitize_callback' => function ( $value ) {
- return array_map( 'sanitize_html_class', wp_parse_list( $value ) );
- },
- ),
- );
-
- $schema['properties']['description'] = array(
- 'description' => __( 'The description of this menu item.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'arg_options' => array(
- 'sanitize_callback' => 'sanitize_text_field',
- ),
- );
-
- $schema['properties']['menu_order'] = array(
- 'description' => __( 'The DB ID of the nav_menu_item that is this item\'s menu parent, if any, otherwise 0.', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'integer',
- 'minimum' => 1,
- 'default' => 1,
- );
-
- $schema['properties']['object'] = array(
- 'description' => __( 'The type of object originally represented, such as "category," "post", or "attachment."', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'string',
- );
-
- $schema['properties']['object_id'] = array(
- 'description' => __( 'The DB ID of the original object this menu item represents, e . g . ID for posts and term_id for categories.', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'integer',
- 'minimum' => 0,
- 'default' => 0,
- );
-
- $schema['properties']['content'] = array(
- 'description' => __( 'HTML content to display for this block menu item.', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'object',
- 'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
- ),
- 'properties' => array(
- 'raw' => array(
- 'description' => __( 'HTML content, as it exists in the database.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'edit' ),
- ),
- 'rendered' => array(
- 'description' => __( 'HTML content, transformed for display.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'block_version' => array(
- 'description' => __( 'Version of the block format used in the HTML content.', 'gutenberg' ),
- 'type' => 'integer',
- 'context' => array( 'edit' ),
- 'readonly' => true,
- ),
- ),
- );
-
- $schema['properties']['target'] = array(
- 'description' => __( 'The target attribute of the link element for this menu item.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'enum' => array(
- '_blank',
- '',
- ),
- );
-
- $schema['properties']['type_label'] = array(
- 'description' => __( 'The singular label used to describe this type of menu item.', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'string',
- 'readonly' => true,
- );
-
- $schema['properties']['url'] = array(
- 'description' => __( 'The URL to which this menu item points.', 'gutenberg' ),
- 'type' => 'string',
- 'format' => 'uri',
- 'context' => array( 'view', 'edit', 'embed' ),
- );
-
- $schema['properties']['xfn'] = array(
- 'description' => __( 'The XFN relationship expressed in the link of this menu item.', 'gutenberg' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string',
- ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'arg_options' => array(
- 'sanitize_callback' => function ( $value ) {
- return array_map( 'sanitize_html_class', wp_parse_list( $value ) );
- },
- ),
- );
-
- $schema['properties']['invalid'] = array(
- 'description' => __( 'Whether the menu item represents an object that no longer exists.', 'gutenberg' ),
- 'context' => array( 'view', 'edit', 'embed' ),
- 'type' => 'boolean',
- 'readonly' => true,
- );
-
- $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
-
- foreach ( $taxonomies as $taxonomy ) {
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
- $schema['properties'][ $base ] = array(
- /* translators: %s: taxonomy name */
- 'description' => sprintf( __( 'The terms assigned to the object in the %s taxonomy.', 'gutenberg' ), $taxonomy->name ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'integer',
- ),
- 'context' => array( 'view', 'edit' ),
- );
-
- if ( 'nav_menu' === $taxonomy->name ) {
- $schema['properties'][ $base ]['type'] = 'integer';
- unset( $schema['properties'][ $base ]['items'] );
- }
- }
-
- $schema['properties']['meta'] = $this->meta->get_field_schema();
-
- $schema_links = $this->get_schema_links();
-
- if ( $schema_links ) {
- $schema['links'] = $schema_links;
- }
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Retrieves the query params for the posts collection.
- *
- * @return array Collection parameters.
- */
- public function get_collection_params() {
- $query_params = parent::get_collection_params();
-
- $query_params['menu_order'] = array(
- 'description' => __( 'Limit result set to posts with a specific menu_order value.', 'gutenberg' ),
- 'type' => 'integer',
- );
-
- $query_params['order'] = array(
- 'description' => __( 'Order sort attribute ascending or descending.', 'gutenberg' ),
- 'type' => 'string',
- 'default' => 'asc',
- 'enum' => array( 'asc', 'desc' ),
- );
-
- $query_params['orderby'] = array(
- 'description' => __( 'Sort collection by object attribute.', 'gutenberg' ),
- 'type' => 'string',
- 'default' => 'menu_order',
- 'enum' => array(
- 'author',
- 'date',
- 'id',
- 'include',
- 'modified',
- 'parent',
- 'relevance',
- 'slug',
- 'include_slugs',
- 'title',
- 'menu_order',
- ),
- );
- // Change default to 100 items.
- $query_params['per_page']['default'] = 100;
-
- return $query_params;
- }
-
- /**
- * Determines the allowed query_vars for a get_items() response and prepares
- * them for WP_Query.
- *
- * @param array $prepared_args Optional. Prepared WP_Query arguments. Default empty array.
- * @param WP_REST_Request $request Optional. Full details about the request.
- *
- * @return array Items query arguments.
- */
- protected function prepare_items_query( $prepared_args = array(), $request = null ) {
- $query_args = parent::prepare_items_query( $prepared_args, $request );
-
- // Map to proper WP_Query orderby param.
- if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) {
- $orderby_mappings = array(
- 'id' => 'ID',
- 'include' => 'post__in',
- 'slug' => 'post_name',
- 'include_slugs' => 'post_name__in',
- 'menu_order' => 'menu_order',
- );
-
- if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) {
- $query_args['orderby'] = $orderby_mappings[ $request['orderby'] ];
- }
- }
-
- return $query_args;
- }
-
- /**
- * Checks whether current user can assign all terms sent with the current request.
- *
- * @param WP_REST_Request $request The request object with post and terms data.
- *
- * @return bool Whether the current user can assign the provided terms.
- */
- protected function check_assign_terms_permission( $request ) {
- $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );
- foreach ( $taxonomies as $taxonomy ) {
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
-
- if ( ! isset( $request[ $base ] ) ) {
- continue;
- }
-
- foreach ( (array) $request[ $base ] as $term_id ) {
- if ( ! $term_id ) {
- continue;
- }
-
- // Invalid terms will be rejected later.
- if ( ! get_term( $term_id, $taxonomy->name ) ) {
- continue;
- }
-
- if ( ! current_user_can( 'assign_term', (int) $term_id ) ) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Get menu id of current menu item.
- *
- * @param int $menu_item_id Menu item id.
- *
- * @return int
- */
- protected function get_menu_id( $menu_item_id ) {
- $menu_ids = wp_get_post_terms( $menu_item_id, 'nav_menu', array( 'fields' => 'ids' ) );
- $menu_id = 0;
- if ( $menu_ids && ! is_wp_error( $menu_ids ) ) {
- $menu_id = array_shift( $menu_ids );
- }
-
- return $menu_id;
- }
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-rest-menu-locations-controller.php b/lib/compat/wordpress-5.9/class-wp-rest-menu-locations-controller.php
deleted file mode 100644
index 8fc9f699aeec9..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-rest-menu-locations-controller.php
+++ /dev/null
@@ -1,276 +0,0 @@
-namespace = 'wp/v2';
- $this->rest_base = 'menu-locations';
- }
-
- /**
- * Registers the routes for the objects of the controller.
- *
- * @see register_rest_route()
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permissions_check' ),
- 'args' => $this->get_collection_params(),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
-
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base . '/(?P[\w-]+)',
- array(
- 'args' => array(
- 'location' => array(
- 'description' => __( 'An alphanumeric identifier for the menu location.', 'gutenberg' ),
- 'type' => 'string',
- ),
- ),
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_item' ),
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'args' => array(
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
- ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * Checks whether a given request has permission to read menu locations.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
- */
- public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to view menu locations.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
-
- return true;
- }
-
- /**
- * Retrieves all menu locations, depending on user context.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
- */
- public function get_items( $request ) {
- $data = array();
-
- foreach ( get_registered_nav_menus() as $name => $description ) {
- $location = new stdClass();
- $location->name = $name;
- $location->description = $description;
-
- $location = $this->prepare_item_for_response( $location, $request );
- $data[ $name ] = $this->prepare_response_for_collection( $location );
- }
-
- return rest_ensure_response( $data );
- }
-
- /**
- * Checks if a given request has access to read a menu location.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
- */
- public function get_item_permissions_check( $request ) {
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to view menu locations.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- if ( ! array_key_exists( $request['location'], get_registered_nav_menus() ) ) {
- return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.', 'gutenberg' ), array( 'status' => 404 ) );
- }
-
- return true;
- }
-
- /**
- * Retrieves a specific menu location.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
- */
- public function get_item( $request ) {
- $registered_menus = get_registered_nav_menus();
- if ( ! array_key_exists( $request['location'], $registered_menus ) ) {
- return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.', 'gutenberg' ), array( 'status' => 404 ) );
- }
-
- $location = new stdClass();
- $location->name = $request['location'];
- $location->description = $registered_menus[ $location->name ];
-
- $data = $this->prepare_item_for_response( $location, $request );
-
- return rest_ensure_response( $data );
- }
-
- /**
- * Prepares a menu location object for serialization.
- *
- * @param stdClass $location Post status data.
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response Post status data.
- */
- public function prepare_item_for_response( $location, $request ) {
- $locations = get_nav_menu_locations();
- $menu = ( isset( $locations[ $location->name ] ) ) ? $locations[ $location->name ] : 0;
-
- $fields = $this->get_fields_for_response( $request );
- $data = array();
-
- if ( rest_is_field_included( 'name', $fields ) ) {
- $data['name'] = $location->name;
- }
-
- if ( rest_is_field_included( 'description', $fields ) ) {
- $data['description'] = $location->description;
- }
-
- if ( rest_is_field_included( 'menu', $fields ) ) {
- $data['menu'] = (int) $menu;
- }
-
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
-
- $response = rest_ensure_response( $data );
-
- $response->add_links( $this->prepare_links( $location ) );
-
- /**
- * Filters a menu location returned from the REST API.
- *
- * Allows modification of the menu location data right before it is
- * returned.
- *
- * @param WP_REST_Response $response The response object.
- * @param object $location The original status object.
- * @param WP_REST_Request $request Request used to generate the response.
- */
- return apply_filters( 'rest_prepare_menu_location', $response, $location, $request );
- }
-
- /**
- * Retrieves the menu location's schema, conforming to JSON Schema.
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'menu-location',
- 'type' => 'object',
- 'properties' => array(
- 'name' => array(
- 'description' => __( 'The name of the menu location.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'embed', 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'description' => array(
- 'description' => __( 'The description of the menu location.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'embed', 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'menu' => array(
- 'description' => __( 'The ID of the assigned menu.', 'gutenberg' ),
- 'type' => 'integer',
- 'context' => array( 'embed', 'view', 'edit' ),
- 'readonly' => true,
- ),
- ),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Retrieves the query params for collections.
- *
- * @return array Collection parameters.
- */
- public function get_collection_params() {
- return array(
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
- );
- }
-
- /**
- * Prepares links for the request.
- *
- * @param stdClass $location Menu location.
- *
- * @return array Links for the given menu location.
- */
- protected function prepare_links( $location ) {
- $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
-
- // Entity meta.
- $links = array(
- 'self' => array(
- 'href' => rest_url( trailingslashit( $base ) . $location->name ),
- ),
- 'collection' => array(
- 'href' => rest_url( $base ),
- ),
- );
-
- $locations = get_nav_menu_locations();
- $menu = ( isset( $locations[ $location->name ] ) ) ? $locations[ $location->name ] : 0;
- if ( $menu ) {
- $taxonomy_object = get_taxonomy( 'nav_menu' );
- if ( $taxonomy_object->show_in_rest ) {
- $rest_base = ! empty( $taxonomy_object->rest_base ) ? $taxonomy_object->rest_base : $taxonomy_object->name;
- $namespace = ! empty( $taxonomy_object->rest_namespace ) ? $taxonomy_object->rest_namespace : '__experimental';
- $url = rest_url( sprintf( '%s/%s/%d', $namespace, $rest_base, $menu ) );
-
- $links['https://api.w.org/menu'][] = array(
- 'href' => $url,
- 'embeddable' => true,
- );
- }
- }
-
- return $links;
- }
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-rest-menus-controller.php b/lib/compat/wordpress-5.9/class-wp-rest-menus-controller.php
deleted file mode 100644
index 3e839c2c958af..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-rest-menus-controller.php
+++ /dev/null
@@ -1,693 +0,0 @@
- true );
-
- /**
- * Overrides the route registration to support "allow_batch".
- *
- * @since 11.5.0
- *
- * @see register_rest_route()
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permissions_check' ),
- 'args' => $this->get_collection_params(),
- ),
- array(
- 'methods' => WP_REST_Server::CREATABLE,
- 'callback' => array( $this, 'create_item' ),
- 'permission_callback' => array( $this, 'create_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
- ),
- 'allow_batch' => $this->allow_batch,
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
-
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base . '/(?P[\d]+)',
- array(
- 'args' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the term.', 'default' ),
- 'type' => 'integer',
- ),
- ),
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_item' ),
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'args' => array(
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
- ),
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $this, 'update_item' ),
- 'permission_callback' => array( $this, 'update_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
- ),
- array(
- 'methods' => WP_REST_Server::DELETABLE,
- 'callback' => array( $this, 'delete_item' ),
- 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
- 'args' => array(
- 'force' => array(
- 'type' => 'boolean',
- 'default' => false,
- 'description' => __( 'Required to be true, as terms do not support trashing.', 'default' ),
- ),
- ),
- ),
- 'allow_batch' => $this->allow_batch,
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
-
- /**
- * Checks if a request has access to read terms in the specified taxonomy.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return bool|WP_Error True if the request has read access, otherwise false or WP_Error object.
- */
- public function get_items_permissions_check( $request ) {
- $tax_obj = get_taxonomy( $this->taxonomy );
- if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
- return false;
- }
- if ( ! current_user_can( $tax_obj->cap->edit_terms ) ) {
- if ( 'edit' === $request['context'] ) {
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit terms in this taxonomy.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you cannot view these menus, unless you have access to permission edit them. ', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return true;
- }
-
- /**
- * Checks if a request has access to read or edit the specified menu.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object.
- */
- public function get_item_permissions_check( $request ) {
- $term = $this->get_term( $request['id'] );
- if ( is_wp_error( $term ) ) {
- return $term;
- }
- if ( ! current_user_can( 'edit_term', $term->term_id ) ) {
- if ( 'edit' === $request['context'] ) {
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this term.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return new WP_Error( 'rest_cannot_view', __( 'Sorry, you cannot view this menu, unless you have access to permission edit it. ', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- return true;
- }
-
- /**
- * Get the term, if the ID is valid.
- *
- * @param int $id Supplied ID.
- *
- * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise.
- */
- protected function get_term( $id ) {
- $term = parent::get_term( $id );
-
- if ( is_wp_error( $term ) ) {
- return $term;
- }
-
- $nav_term = wp_get_nav_menu_object( $term );
- $nav_term->auto_add = $this->get_menu_auto_add( $nav_term->term_id );
-
- return $nav_term;
- }
-
- /**
- * Checks if a request has access to create a term.
- * Also check if request can assign menu locations.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return bool|WP_Error True if the request has access to create items, false or WP_Error object otherwise.
- */
- public function create_item_permissions_check( $request ) {
- $check = $this->check_assign_locations_permission( $request );
- if ( is_wp_error( $check ) ) {
- return $check;
- }
- $check = $this->check_set_auto_add_permission( $request );
- if ( is_wp_error( $check ) ) {
- return $check;
- }
-
- return parent::create_item_permissions_check( $request );
- }
-
- /**
- * Checks if a request has access to update the specified term.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise.
- */
- public function update_item_permissions_check( $request ) {
- $check = $this->check_assign_locations_permission( $request );
- if ( is_wp_error( $check ) ) {
- return $check;
- }
- $check = $this->check_set_auto_add_permission( $request );
- if ( is_wp_error( $check ) ) {
- return $check;
- }
-
- return parent::update_item_permissions_check( $request );
- }
-
- /**
- * Checks whether current user can assign all locations sent with the current request.
- *
- * @param WP_REST_Request $request The request object with post and locations data.
- *
- * @return bool|WP_Error Whether the current user can assign the provided terms.
- */
- protected function check_assign_locations_permission( $request ) {
- if ( ! isset( $request['locations'] ) ) {
- return true;
- }
-
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error( 'rest_cannot_assign_location', __( 'Sorry, you are not allowed to assign the provided locations.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
-
- foreach ( $request['locations'] as $location ) {
- if ( ! array_key_exists( $location, get_registered_nav_menus() ) ) {
- return new WP_Error(
- 'rest_menu_location_invalid',
- __( 'Invalid menu location.', 'gutenberg' ),
- array(
- 'status' => 400,
- 'location' => $location,
- )
- );
- }
- }
-
- return true;
- }
-
- /**
- * Checks whether current user can set auto add pages.
- *
- * @param WP_REST_Request $request The request object with post and locations data.
- *
- * @return true|WP_Error Whether the current user can assign the provided terms.
- */
- protected function check_set_auto_add_permission( $request ) {
- if ( ! isset( $request['auto_add'] ) ) {
- return true;
- }
-
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error( 'rest_cannot_set_auto_add', __( 'Sorry, you are not allowed to set auto add pages.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
-
- return true;
- }
-
- /**
- * Prepares a single term output for response.
- *
- * @param obj $term Term object.
- * @param WP_REST_Request $request Request object.
- *
- * @return WP_REST_Response $response Response object.
- */
- public function prepare_item_for_response( $term, $request ) {
- $nav_menu = wp_get_nav_menu_object( $term );
- $response = parent::prepare_item_for_response( $nav_menu, $request );
-
- $fields = $this->get_fields_for_response( $request );
- $data = $response->get_data();
-
- if ( rest_is_field_included( 'locations', $fields ) ) {
- $data['locations'] = $this->get_menu_locations( $nav_menu->term_id );
- }
-
- if ( rest_is_field_included( 'auto_add', $fields ) ) {
- $auto_add = $this->get_menu_auto_add( $nav_menu->term_id );
- $data['auto_add'] = $auto_add;
- }
-
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
-
- $response = rest_ensure_response( $data );
- $response->add_links( $this->prepare_links( $term ) );
-
- /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
- return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $term, $request );
- }
-
- /**
- * Prepares links for the request.
- *
- * @param object $term Term object.
- *
- * @return array Links for the given term.
- */
- protected function prepare_links( $term ) {
- $links = parent::prepare_links( $term );
-
- $locations = $this->get_menu_locations( $term->term_id );
- $rest_base = 'menu-locations';
- foreach ( $locations as $location ) {
- $url = rest_url( sprintf( 'wp/v2/%s/%s', $rest_base, $location ) );
- $links['https://api.w.org/menu-location'][] = array(
- 'href' => $url,
- 'embeddable' => true,
- );
- }
-
- return $links;
- }
-
- /**
- * Prepares a single term for create or update.
- *
- * @param WP_REST_Request $request Request object.
- *
- * @return array $prepared_term Term object.
- */
- public function prepare_item_for_database( $request ) {
- $prepared_term = parent::prepare_item_for_database( $request );
-
- $prepared_term = (array) $prepared_term;
- $schema = $this->get_item_schema();
- if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) {
- $prepared_term['menu-name'] = $request['name'];
- }
-
- return $prepared_term;
- }
-
- /**
- * Creates a single term in a taxonomy.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function create_item( $request ) {
- if ( isset( $request['parent'] ) ) {
- if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) {
- return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.', 'gutenberg' ), array( 'status' => 400 ) );
- }
-
- $parent = wp_get_nav_menu_object( (int) $request['parent'] );
-
- if ( ! $parent ) {
- return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- }
-
- $prepared_term = $this->prepare_item_for_database( $request );
-
- $term = wp_update_nav_menu_object( 0, wp_slash( (array) $prepared_term ) );
-
- if ( is_wp_error( $term ) ) {
- /*
- * If we're going to inform the client that the term already exists,
- * give them the identifier for future use.
- */
-
- if ( in_array( 'menu_exists', $term->get_error_codes(), true ) ) {
- $existing_term = get_term_by( 'name', $prepared_term['menu-name'], $this->taxonomy );
- $term->add_data( $existing_term->term_id, 'menu_exists' );
- $term->add_data(
- array(
- 'status' => 400,
- 'term_id' => $existing_term->term_id,
- )
- );
- } else {
- $term->add_data( array( 'status' => 400 ) );
- }
-
- return $term;
- }
-
- $term = $this->get_term( $term );
-
- /**
- * Fires after a single term is created or updated via the REST API.
- *
- * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
- *
- * @param WP_Term $term Inserted or updated term object.
- * @param WP_REST_Request $request Request object.
- * @param bool $creating True when creating a term, false when updating.
- */
- do_action( "rest_insert_{$this->taxonomy}", $term, $request, true );
-
- $schema = $this->get_item_schema();
- if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
- $meta_update = $this->meta->update_value( $request['meta'], $term->term_id );
-
- if ( is_wp_error( $meta_update ) ) {
- return $meta_update;
- }
- }
-
- $locations_update = $this->handle_locations( $term->term_id, $request );
-
- if ( is_wp_error( $locations_update ) ) {
- return $locations_update;
- }
-
- $this->handle_auto_add( $term->term_id, $request );
-
- $fields_update = $this->update_additional_fields_for_object( $term, $request );
-
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- $request->set_param( 'context', 'view' );
-
- /**
- * Fires after a single term is completely created or updated via the REST API.
- *
- * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
- *
- * @param WP_Term $term Inserted or updated term object.
- * @param WP_REST_Request $request Request object.
- * @param bool $creating True when creating a term, false when updating.
- *
- * @since 5.0.0
- */
- do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, true );
-
- $response = $this->prepare_item_for_response( $term, $request );
- $response = rest_ensure_response( $response );
-
- $response->set_status( 201 );
- $response->header( 'Location', rest_url( $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) );
-
- return $response;
- }
-
- /**
- * Updates a single term from a taxonomy.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function update_item( $request ) {
- $term = $this->get_term( $request['id'] );
- if ( is_wp_error( $term ) ) {
- return $term;
- }
-
- if ( isset( $request['parent'] ) ) {
- if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) {
- return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.', 'gutenberg' ), array( 'status' => 400 ) );
- }
-
- $parent = get_term( (int) $request['parent'], $this->taxonomy );
-
- if ( ! $parent ) {
- return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- }
-
- $prepared_term = $this->prepare_item_for_database( $request );
-
- // Only update the term if we haz something to update.
- if ( ! empty( $prepared_term ) ) {
- $update = wp_update_nav_menu_object( $term->term_id, wp_slash( (array) $prepared_term ) );
-
- if ( is_wp_error( $update ) ) {
- return $update;
- }
- }
-
- $term = get_term( $term->term_id, $this->taxonomy );
-
- /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
- do_action( "rest_insert_{$this->taxonomy}", $term, $request, false );
-
- $schema = $this->get_item_schema();
- if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
- $meta_update = $this->meta->update_value( $request['meta'], $term->term_id );
-
- if ( is_wp_error( $meta_update ) ) {
- return $meta_update;
- }
- }
-
- $locations_update = $this->handle_locations( $term->term_id, $request );
-
- if ( is_wp_error( $locations_update ) ) {
- return $locations_update;
- }
-
- $this->handle_auto_add( $term->term_id, $request );
-
- $fields_update = $this->update_additional_fields_for_object( $term, $request );
-
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- $request->set_param( 'context', 'view' );
-
- /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
- do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, false );
-
- $response = $this->prepare_item_for_response( $term, $request );
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Deletes a single term from a taxonomy.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function delete_item( $request ) {
- $term = $this->get_term( $request['id'] );
- if ( is_wp_error( $term ) ) {
- return $term;
- }
-
- $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
-
- // We don't support trashing for terms.
- if ( ! $force ) {
- /* translators: %s: force=true */
- return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Terms do not support trashing. Set '%s' to delete.", 'gutenberg' ), 'force=true' ), array( 'status' => 501 ) );
- }
-
- $request->set_param( 'context', 'view' );
-
- $previous = $this->prepare_item_for_response( $term, $request );
-
- $retval = wp_delete_nav_menu( $term );
-
- if ( ! $retval ) {
- return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.', 'gutenberg' ), array( 'status' => 500 ) );
- }
-
- $response = new WP_REST_Response();
- $response->set_data(
- array(
- 'deleted' => true,
- 'previous' => $previous->get_data(),
- )
- );
-
- /**
- * Fires after a single term is deleted via the REST API.
- *
- * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
- *
- * @param WP_Term $term The deleted term.
- * @param WP_REST_Response $response The response data.
- * @param WP_REST_Request $request The request sent to the API.
- */
- do_action( "rest_delete_{$this->taxonomy}", $term, $response, $request );
-
- return $response;
- }
-
- /**
- * Returns the value of a menu's auto_add
- *
- * @param int $menu_id The menu id to update the location form.
- *
- * @return bool The value of auto_add.
- */
- function get_menu_auto_add( $menu_id ) {
- $nav_menu_option = (array) get_option( 'nav_menu_options', array( 'auto_add' => array() ) );
- $check = in_array( $menu_id, $nav_menu_option['auto_add'], true );
-
- return $check;
- }
-
- /**
- * Updates the menu's auto add from a REST request.
- *
- * @param int $menu_id The menu id to update the location form.
- * @param WP_REST_Request $request The request object with menu and locations data.
- *
- * @return bool True if the auto update was successfully updated.
- */
- function handle_auto_add( $menu_id, $request ) {
- if ( ! isset( $request['auto_add'] ) ) {
- return true;
- }
-
- $nav_menu_option = (array) get_option( 'nav_menu_options', array( 'auto_add' => array() ) );
-
- if ( ! isset( $nav_menu_option['auto_add'] ) ) {
- $nav_menu_option['auto_add'] = array();
- }
-
- $auto_add = $request['auto_add'];
-
- $i = array_search( $menu_id, $nav_menu_option['auto_add'], true );
-
- if ( $auto_add && false === $i ) {
- $nav_menu_option['auto_add'][] = $menu_id;
- } elseif ( ! $auto_add && false !== $i ) {
- array_splice( $nav_menu_option['auto_add'], $i, 1 );
- }
-
- $update = update_option( 'nav_menu_options', $nav_menu_option );
-
- /** This action is documented in wp-includes/nav-menu.php */
- do_action( 'wp_update_nav_menu', $menu_id );
-
- return $update;
- }
-
- /**
- * Returns names of the locations assigned to the menu.
- *
- * @since 5.8.0
- *
- * @param int $menu_id The menu id.
- *
- * @return string[] $menu_locations The locations assigned to the menu.
- */
- protected function get_menu_locations( $menu_id ) {
- $locations = get_nav_menu_locations();
- $menu_locations = array();
-
- foreach ( $locations as $location => $assigned_menu_id ) {
- if ( $menu_id === $assigned_menu_id ) {
- $menu_locations[] = $location;
- }
- }
-
- return $menu_locations;
- }
-
- /**
- * Updates the menu's locations from a REST request.
- *
- * @param int $menu_id The menu id to update the location form.
- * @param WP_REST_Request $request The request object with menu and locations data.
- *
- * @return true|WP_Error WP_Error on an error assigning any of the locations, otherwise null.
- */
- protected function handle_locations( $menu_id, $request ) {
- if ( ! isset( $request['locations'] ) ) {
- return true;
- }
-
- $menu_locations = get_registered_nav_menus();
- $menu_locations = array_keys( $menu_locations );
- $new_locations = array();
- foreach ( $request['locations'] as $location ) {
- if ( ! in_array( $location, $menu_locations, true ) ) {
- return new WP_Error( 'invalid_menu_location', __( 'Menu location does not exist.', 'gutenberg' ), array( 'status' => 400 ) );
- }
- $new_locations[ $location ] = $menu_id;
- }
- $assigned_menu = get_nav_menu_locations();
- foreach ( $assigned_menu as $location => $term_id ) {
- if ( $term_id === $menu_id ) {
- unset( $assigned_menu[ $location ] );
- }
- }
- $new_assignments = array_merge( $assigned_menu, $new_locations );
- set_theme_mod( 'nav_menu_locations', $new_assignments );
-
- return true;
- }
-
- /**
- * Retrieves the term's schema, conforming to JSON Schema.
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- $schema = parent::get_item_schema();
- unset( $schema['properties']['count'] );
- unset( $schema['properties']['link'] );
- unset( $schema['properties']['taxonomy'] );
-
- $schema['properties']['locations'] = array(
- 'description' => __( 'The locations assigned to the menu.', 'gutenberg' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string',
- ),
- 'context' => array( 'view', 'edit' ),
- );
-
- $schema['properties']['auto_add'] = array(
- 'description' => __( 'Whether to automatically add top level pages to this menu.', 'gutenberg' ),
- 'context' => array( 'view', 'edit' ),
- 'type' => 'boolean',
- );
-
- return $schema;
- }
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-rest-url-details-controller.php b/lib/compat/wordpress-5.9/class-wp-rest-url-details-controller.php
deleted file mode 100644
index b9ff5982b4ecd..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-rest-url-details-controller.php
+++ /dev/null
@@ -1,591 +0,0 @@
-namespace = 'wp-block-editor/v1';
- $this->rest_base = 'url-details';
- }
-
- /**
- * Registers the necessary REST API routes.
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'parse_url_details' ),
- 'args' => array(
- 'url' => array(
- 'required' => true,
- 'description' => __( 'The URL to process.', 'gutenberg' ),
- 'validate_callback' => 'wp_http_validate_url',
- 'sanitize_callback' => 'esc_url_raw',
- 'type' => 'string',
- 'format' => 'uri',
- ),
- ),
- 'permission_callback' => array( $this, 'permissions_check' ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- ),
- )
- );
- }
-
- /**
- * Get the schema for the endpoint.
- *
- * @return array the schema.
- */
- public function get_item_schema() {
- if ( $this->schema ) {
- return $this->add_additional_fields_schema( $this->schema );
- }
-
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'url-details',
- 'type' => 'object',
- 'properties' => array(
- 'title' => array(
- 'description' => __( 'The contents of the element from the URL.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- 'icon' => array(
- 'description' => __( 'The favicon image link of the element from the URL.', 'gutenberg' ),
- 'type' => 'string',
- 'format' => 'uri',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- 'description' => array(
- 'description' => __( 'The content of the element from the URL.', 'gutenberg' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- 'image' => array(
- 'description' => __( 'The OG image link of the or element from the URL.', 'gutenberg' ),
- 'type' => 'string',
- 'format' => 'uri',
- 'context' => array( 'view', 'edit', 'embed' ),
- 'readonly' => true,
- ),
- ),
- );
-
- $this->schema = $schema;
-
- return $this->add_additional_fields_schema( $this->schema );
- }
-
- /**
- * Retrieves the contents of the tag from the HTML
- * response.
- *
- * @param WP_REST_REQUEST $request Full details about the request.
- * @return WP_REST_Response|WP_Error The parsed details as a response object, or an error.
- */
- public function parse_url_details( $request ) {
- $url = untrailingslashit( $request['url'] );
-
- if ( empty( $url ) ) {
- return new WP_Error( 'rest_invalid_url', __( 'Invalid URL', 'gutenberg' ), array( 'status' => 404 ) );
- }
-
- // Transient per URL.
- $cache_key = $this->build_cache_key_for_url( $url );
-
- // Attempt to retrieve cached response.
- $cached_response = $this->get_cache( $cache_key );
-
- if ( ! empty( $cached_response ) ) {
- $remote_url_response = $cached_response;
- } else {
- $remote_url_response = $this->get_remote_url( $url );
-
- // Exit if we don't have a valid body or it's empty.
- if ( is_wp_error( $remote_url_response ) || empty( $remote_url_response ) ) {
- return $remote_url_response;
- }
-
- // Cache the valid response.
- $this->set_cache( $cache_key, $remote_url_response );
- }
-
- $html_head = $this->get_document_head( $remote_url_response );
- $meta_elements = $this->get_meta_with_content_elements( $html_head );
-
- $data = $this->add_additional_fields_to_object(
- array(
- 'title' => $this->get_title( $html_head ),
- 'icon' => $this->get_icon( $html_head, $url ),
- 'description' => $this->get_description( $meta_elements ),
- 'image' => $this->get_image( $meta_elements, $url ),
- ),
- $request
- );
-
- // Wrap the data in a response object.
- $response = rest_ensure_response( $data );
-
- /**
- * Filters the URL data for the response.
- *
- * @param WP_REST_Response $response The response object.
- * @param string $url The requested URL.
- * @param WP_REST_Request $request Request object.
- * @param array $remote_url_response HTTP response body from the remote URL.
- */
- return apply_filters( 'rest_prepare_url_details', $response, $url, $request, $remote_url_response );
- }
-
- /**
- * Checks whether a given request has permission to read remote urls.
- *
- * @return WP_Error|bool True if the request has access, or WP_Error object.
- */
- public function permissions_check() {
- if ( current_user_can( 'edit_posts' ) ) {
- return true;
- }
-
- foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
- if ( current_user_can( $post_type->cap->edit_posts ) ) {
- return true;
- }
- }
-
- return new WP_Error(
- 'rest_cannot_view_url_details',
- __( 'Sorry, you are not allowed to process remote urls.', 'gutenberg' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- /**
- * Retrieves the document title from a remote URL.
- *
- * @param string $url The website url whose HTML we want to access.
- * @return string|WP_Error The HTTP response from the remote URL, or an error.
- */
- private function get_remote_url( $url ) {
-
- // Provide a modified UA string to workaround web properties which block WordPress "Pingbacks".
- // Why? The UA string used for pingback requests contains `WordPress/` which is very similar
- // to that used as the default UA string by the WP HTTP API. Therefore requests from this
- // REST endpoint are being unintentionally blocked as they are misidentified as pingback requests.
- // By slightly modifying the UA string, but still retaining the "WordPress" identification (via "WP")
- // we are able to work around this issue.
- // Example UA string: `WP-URLDetails/5.9-alpha-51389 (+http://localhost:8888)`.
- $modified_user_agent = 'WP-URLDetails/' . get_bloginfo( 'version' ) . ' (+' . get_bloginfo( 'url' ) . ')';
-
- $args = array(
- 'limit_response_size' => 150 * KB_IN_BYTES,
- 'user-agent' => $modified_user_agent,
- );
-
- /**
- * Filters the HTTP request args for URL data retrieval.
- *
- * Can be used to adjust response size limit and other WP_Http::request args.
- *
- * @param array $args Arguments used for the HTTP request
- * @param string $url The attempted URL.
- */
- $args = apply_filters( 'rest_url_details_http_request_args', $args, $url );
-
- $response = wp_safe_remote_get(
- $url,
- $args
- );
-
- if ( WP_Http::OK !== wp_remote_retrieve_response_code( $response ) ) {
- // Not saving the error response to cache since the error might be temporary.
- return new WP_Error( 'no_response', __( 'URL not found. Response returned a non-200 status code for this URL.', 'gutenberg' ), array( 'status' => WP_Http::NOT_FOUND ) );
- }
-
- $remote_body = wp_remote_retrieve_body( $response );
-
- if ( empty( $remote_body ) ) {
- return new WP_Error( 'no_content', __( 'Unable to retrieve body from response at this URL.', 'gutenberg' ), array( 'status' => WP_Http::NOT_FOUND ) );
- }
-
- return $remote_body;
- }
-
- /**
- * Parses the contents from the provided HTML
- *
- * @param string $html The HTML from the remote website at URL.
- * @return string The title tag contents on success, or an empty string.
- */
- private function get_title( $html ) {
- $pattern = '#]*>(.*?)<\s*/\s*title>#is';
- preg_match( $pattern, $html, $match_title );
-
- $title = ! empty( $match_title[1] ) && is_string( $match_title[1] ) ? trim( $match_title[1] ) : '';
-
- if ( empty( $title ) ) {
- return '';
- }
-
- return $this->prepare_metadata_for_output( $title );
- }
-
- /**
- * Parses the site icon from the provided HTML
- *
- * @param string $html The HTML from the remote website at URL.
- * @param string $url The target website URL.
- * @return string The icon URI on success, or an empty string.
- */
- private function get_icon( $html, $url ) {
- // Grab the icon's link element.
- $pattern = '# ]*rel=(?:[\"\']??)\s*(?:icon|shortcut icon|icon shortcut)\s*(?:[\"\']??)[^>]*\/?>#isU';
- preg_match( $pattern, $html, $element );
- $element = ! empty( $element[0] ) && is_string( $element[0] ) ? trim( $element[0] ) : '';
- if ( empty( $element ) ) {
- return '';
- }
-
- // Get the icon's href value.
- $pattern = '#href=([\"\']??)([^\" >]*?)\\1[^>]*#isU';
- preg_match( $pattern, $element, $icon );
- $icon = ! empty( $icon[2] ) && is_string( $icon[2] ) ? trim( $icon[2] ) : '';
- if ( empty( $icon ) ) {
- return '';
- }
-
- // If the icon is a data URL, return it.
- $parsed_icon = parse_url( $icon );
- if ( isset( $parsed_icon['schema'] ) && 'data' === $parsed_icon['scheme'] ) {
- return $icon;
- }
-
- // Attempt to convert relative URLs to absolute.
- $parsed_url = parse_url( $url );
- $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/';
- $icon = WP_Http::make_absolute_url( $icon, $root_url );
-
- return $icon;
- }
-
- /**
- * Parses the meta description from the provided HTML.
- *
- * @param array $meta_elements {
- * A multi-dimensional indexed array on success, or empty array.
- *
- * @type string[] 0 Meta elements with a content attribute.
- * @type string[] 1 Content attribute's opening quotation mark.
- * @type string[] 2 Content attribute's value for each meta element.
- * }
- * @return string The meta description contents on success, or an empty string.
- */
- private function get_description( $meta_elements ) {
- // Bail out if there are no meta elements.
- if ( empty( $meta_elements[0] ) ) {
- return '';
- }
-
- $description = $this->get_metadata_from_meta_element( $meta_elements, 'name', '(?:description|og:description)' );
-
- // Bail out if description not found.
- if ( '' === $description ) {
- return '';
- }
-
- return $this->prepare_metadata_for_output( $description );
- }
-
- /**
- * Parses the Open Graph Image from the provided HTML.
- *
- * See: https://ogp.me/.
- *
- * @param array $meta_elements {
- * A multi-dimensional indexed array on success, or empty array.
- *
- * @type string[] 0 Meta elements with a content attribute.
- * @type string[] 1 Content attribute's opening quotation mark.
- * @type string[] 2 Content attribute's value for each meta element.
- * }
- * @param string $url The target website URL.
- * @return string The OG image on success, or empty string.
- */
- private function get_image( $meta_elements, $url ) {
- $image = $this->get_metadata_from_meta_element( $meta_elements, 'property', '(?:og:image|og:image:url)' );
-
- // Bail out if image not found.
- if ( '' === $image ) {
- return '';
- }
-
- // Attempt to convert relative URLs to absolute.
- $parsed_url = parse_url( $url );
- $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/';
- $image = WP_Http::make_absolute_url( $image, $root_url );
-
- return $image;
- }
-
- /**
- * Prepare the metadata by:
- *
- * - stripping all HTML tags and tag entities
- * - converting non-tag entities into characters.
- *
- * @param string $metadata The metadata content to prepare.
- * @return string The prepared metadata.
- */
- private function prepare_metadata_for_output( $metadata ) {
- $metadata = html_entity_decode( $metadata, ENT_QUOTES, get_bloginfo( 'charset' ) );
- $metadata = wp_strip_all_tags( $metadata );
- return $metadata;
- }
-
- /**
- * Utility function to build cache key for a given URL.
- *
- * @param string $url The URL for which to build a cache key.
- * @return string The cache key.
- */
- private function build_cache_key_for_url( $url ) {
- return 'g_url_details_response_' . md5( $url );
- }
-
- /**
- * Utility function to retrieve a value from the cache at a given key.
- *
- * @param string $key The cache key.
- * @return mixed The value from the cache.
- */
- private function get_cache( $key ) {
- return get_site_transient( $key );
- }
-
- /**
- * Utility function to cache a given data set at a given cache key.
- *
- * @param string $key The cache key under which to store the value.
- * @param string $data The data to be stored at the given cache key.
- * @return bool True when transient set, or false.
- */
- private function set_cache( $key, $data = '' ) {
- $ttl = HOUR_IN_SECONDS;
-
- /**
- * Filters the cache expiration.
- *
- * Can be used to adjust the time until expiration in seconds for the cache
- * of the data retrieved for the given URL.
- *
- * @param int $ttl the time until cache expiration in seconds.
- */
- $cache_expiration = apply_filters( 'rest_url_details_cache_expiration', $ttl );
-
- return set_site_transient( $key, $data, $cache_expiration );
- }
-
- /**
- * Retrieves the `` section.
- *
- * @param string $html The string of HTML to parse.
- * @return string The `..` section on success, or original HTML.
- */
- private function get_document_head( $html ) {
- $head_html = $html;
-
- // Find the opening `` tag.
- $head_start = strpos( $html, '` tag.
- $head_end = strpos( $head_html, '' );
- if ( false === $head_end ) {
- // Didn't find it. Find the opening ` ` tag.
- $head_end = strpos( $head_html, ' elements that have a `content` attribute.
- *
- * @param string $html The string of HTML to be parsed.
- * @return array {
- * A multi-dimensional indexed array on success, or empty array.
- *
- * @type string[] 0 Meta elements with a content attribute.
- * @type string[] 1 Content attribute's opening quotation mark.
- * @type string[] 2 Content attribute's value for each meta element.
- * }
- */
- private function get_meta_with_content_elements( $html ) {
- /*
- * Parse all meta elements with a content attribute.
- *
- * Why first search for the content attribute rather than directly searching for name=description element?
- * tl;dr The content attribute's value will be truncated when it contains a > symbol.
- *
- * The content attribute's value (i.e. the description to get) can have HTML in it and be well-formed as
- * it's a string to the browser. Imagine what happens when attempting to match for the name=description
- * first. Hmm, if a > or /> symbol is in the content attribute's value, then it terminates the match
- * as the element's closing symbol. But wait, it's in the content attribute and is not the end of the
- * element. This is a limitation of using regex. It can't determine "wait a minute this is inside of quotation".
- * If this happens, what gets matched is not the entire element or all of the content.
- *
- * Why not search for the name=description and then content="(.*)"?
- * The attribute order could be opposite. Plus, additional attributes may exist including being between
- * the name and content attributes.
- *
- * Why not lookahead?
- * Lookahead is not constrained to stay within the element. The first symbol.
- */
- '[^>]*' .
-
- /*
- * Find the content attribute. When found, capture its value (.*).
- *
- * Allows for (a) single or double quotes and (b) whitespace in the value.
- *
- * Why capture the opening quotation mark, i.e. (["\']), and then backreference,
- * i.e \1, for the closing quotation mark?
- * To ensure the closing quotation mark matches the opening one. Why? Attribute values
- * can contain quotation marks, such as an apostrophe in the content.
- */
- 'content=(["\']??)(.*)\1' .
-
- /*
- * Allows for additional attributes after the content attribute.
- * Searches for anything other than > symbol.
- */
- '[^>]*' .
-
- /*
- * \/?> searches for the closing > symbol, which can be in either /> or > format.
- * # ends the pattern.
- */
- '\/?>#' .
-
- /*
- * These are the options:
- * - i : case insensitive
- * - s : allows newline characters for the . match (needed for multiline elements)
- * - U means non-greedy matching
- */
- 'isU';
-
- preg_match_all( $pattern, $html, $elements );
-
- return $elements;
- }
-
- /**
- * Gets the metadata from a target meta element.
- *
- * @param array $meta_elements {
- * A multi-dimensional indexed array on success, or empty array.
- *
- * @type string[] 0 Meta elements with a content attribute.
- * @type string[] 1 Content attribute's opening quotation mark.
- * @type string[] 2 Content attribute's value for each meta element.
- * }
- * @param string $attr Attribute that identifies the element with the target metadata.
- * @param string $attr_value The attribute's value that identifies the element with the target metadata.
- * @return string The metadata on success, or an empty string.
- */
- private function get_metadata_from_meta_element( $meta_elements, $attr, $attr_value ) {
- // Bail out if there are no meta elements.
- if ( empty( $meta_elements[0] ) ) {
- return '';
- }
-
- $metadata = '';
- $pattern = '#' .
-
- /*
- * Target this attribute and value to find the metadata element.
- *
- * Allows for (a) no, single, double quotes and (b) whitespace in the value.
- *
- * Why capture the opening quotation mark, i.e. (["\']), and then backreference,
- * i.e \1, for the closing quotation mark?
- * To ensure the closing quotation mark matches the opening one. Why? Attribute values
- * can contain quotation marks, such as an apostrophe in the content.
- */
- $attr . '=([\"\']??)\s*' . $attr_value . '\s*\1' .
-
- /*
- * These are the options:
- * - i : case insensitive
- * - s : allows newline characters for the . match (needed for multiline elements)
- * - U means non-greedy matching
- */
- '#isU';
-
- // Find the metdata element.
- foreach ( $meta_elements[0] as $index => $element ) {
- preg_match( $pattern, $element, $match );
-
- // This is not the metadata element. Skip it.
- if ( empty( $match ) ) {
- continue;
- }
-
- /*
- * Found the metadata element.
- * Get the metadata from its matching content array.
- */
- if ( isset( $meta_elements[2][ $index ] ) && is_string( $meta_elements[2][ $index ] ) ) {
- $metadata = trim( $meta_elements[2][ $index ] );
- }
-
- break;
- }
-
- return $metadata;
- }
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php b/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
deleted file mode 100644
index 19b8611df0a4d..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
+++ /dev/null
@@ -1,1944 +0,0 @@
- 'unique-name-within-the-set',
- * 'name' => 'Name for the UI',
- * => 'value'
- * ),
- * )
- * ```
- *
- * This contains the necessary metadata to process them:
- *
- * - path => where to find the preset within the settings section
- * - override => whether a theme preset with the same slug as a default preset
- * can override it
- * - use_default_names => whether to use the default names
- * - value_key => the key that represents the value
- * - value_func => optionally, instead of value_key, a function to generate
- * the value that takes a preset as an argument
- * (either value_key or value_func should be present)
- * - css_vars => template string to use in generating the CSS Custom Property.
- * Example output: "--wp--preset--duotone--blue: " will generate as many CSS Custom Properties as presets defined
- * substituting the $slug for the slug's value for each preset value.
- * - classes => array containing a structure with the classes to
- * generate for the presets, where for each array item
- * the key is the class name and the value the property name.
- * The "$slug" substring will be replaced by the slug of each preset.
- * For example:
- * 'classes' => array(
- * '.has-$slug-color' => 'color',
- * '.has-$slug-background-color' => 'background-color',
- * '.has-$slug-border-color' => 'border-color',
- * )
- * - properties => array of CSS properties to be used by kses to
- * validate the content of each preset
- * by means of the remove_insecure_properties method.
- */
- const PRESETS_METADATA = array(
- array(
- 'path' => array( 'color', 'palette' ),
- 'override' => array( 'color', 'defaultPalette' ),
- 'use_default_names' => false,
- 'value_key' => 'color',
- 'css_vars' => '--wp--preset--color--$slug',
- 'classes' => array(
- '.has-$slug-color' => 'color',
- '.has-$slug-background-color' => 'background-color',
- '.has-$slug-border-color' => 'border-color',
- ),
- 'properties' => array( 'color', 'background-color', 'border-color' ),
- ),
- array(
- 'path' => array( 'color', 'gradients' ),
- 'override' => array( 'color', 'defaultGradients' ),
- 'use_default_names' => false,
- 'value_key' => 'gradient',
- 'css_vars' => '--wp--preset--gradient--$slug',
- 'classes' => array( '.has-$slug-gradient-background' => 'background' ),
- 'properties' => array( 'background' ),
- ),
- array(
- 'path' => array( 'color', 'duotone' ),
- 'override' => true,
- 'use_default_names' => false,
- 'value_func' => 'gutenberg_get_duotone_filter_property',
- 'css_vars' => '--wp--preset--duotone--$slug',
- 'classes' => array(),
- 'properties' => array( 'filter' ),
- ),
- array(
- 'path' => array( 'typography', 'fontSizes' ),
- 'override' => true,
- 'use_default_names' => true,
- 'value_key' => 'size',
- 'css_vars' => '--wp--preset--font-size--$slug',
- 'classes' => array( '.has-$slug-font-size' => 'font-size' ),
- 'properties' => array( 'font-size' ),
- ),
- array(
- 'path' => array( 'typography', 'fontFamilies' ),
- 'override' => true,
- 'use_default_names' => false,
- 'value_key' => 'fontFamily',
- 'css_vars' => '--wp--preset--font-family--$slug',
- 'classes' => array( '.has-$slug-font-family' => 'font-family' ),
- 'properties' => array( 'font-family' ),
- ),
- );
-
- /**
- * Metadata for style properties.
- *
- * Each element is a direct mapping from the CSS property name to the
- * path to the value in theme.json & block attributes.
- */
- const PROPERTIES_METADATA = array(
- 'background' => array( 'color', 'gradient' ),
- 'background-color' => array( 'color', 'background' ),
- 'border-radius' => array( 'border', 'radius' ),
- 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
- 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
- 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ),
- 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
- 'border-color' => array( 'border', 'color' ),
- 'border-width' => array( 'border', 'width' ),
- 'border-style' => array( 'border', 'style' ),
- 'color' => array( 'color', 'text' ),
- 'font-family' => array( 'typography', 'fontFamily' ),
- 'font-size' => array( 'typography', 'fontSize' ),
- 'font-style' => array( 'typography', 'fontStyle' ),
- 'font-weight' => array( 'typography', 'fontWeight' ),
- 'letter-spacing' => array( 'typography', 'letterSpacing' ),
- 'line-height' => array( 'typography', 'lineHeight' ),
- 'margin' => array( 'spacing', 'margin' ),
- 'margin-top' => array( 'spacing', 'margin', 'top' ),
- 'margin-right' => array( 'spacing', 'margin', 'right' ),
- 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ),
- 'margin-left' => array( 'spacing', 'margin', 'left' ),
- 'padding' => array( 'spacing', 'padding' ),
- 'padding-top' => array( 'spacing', 'padding', 'top' ),
- 'padding-right' => array( 'spacing', 'padding', 'right' ),
- 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
- 'padding-left' => array( 'spacing', 'padding', 'left' ),
- '--wp--style--block-gap' => array( 'spacing', 'blockGap' ),
- 'text-decoration' => array( 'typography', 'textDecoration' ),
- 'text-transform' => array( 'typography', 'textTransform' ),
- 'filter' => array( 'filter', 'duotone' ),
- );
-
- /**
- * Protected style properties.
- *
- * These style properties are only rendered if a setting enables it
- * via a value other than `null`.
- *
- * Each element maps the style property to the corresponding theme.json
- * setting key.
- */
- const PROTECTED_PROPERTIES = array(
- 'spacing.blockGap' => array( 'spacing', 'blockGap' ),
- );
-
- /**
- * The top-level keys a theme.json can have.
- *
- * @var string[]
- */
- const VALID_TOP_LEVEL_KEYS = array(
- 'customTemplates',
- 'settings',
- 'styles',
- 'templateParts',
- 'version',
- );
-
- /**
- * The valid properties under the settings key.
- *
- * @var array
- */
- const VALID_SETTINGS = array(
- 'appearanceTools' => null,
- 'border' => array(
- 'color' => null,
- 'radius' => null,
- 'style' => null,
- 'width' => null,
- ),
- 'color' => array(
- 'background' => null,
- 'custom' => null,
- 'customDuotone' => null,
- 'customGradient' => null,
- 'defaultGradients' => null,
- 'defaultPalette' => null,
- 'duotone' => null,
- 'gradients' => null,
- 'link' => null,
- 'palette' => null,
- 'text' => null,
- ),
- 'custom' => null,
- 'layout' => array(
- 'contentSize' => null,
- 'wideSize' => null,
- ),
- 'spacing' => array(
- 'blockGap' => null,
- 'margin' => null,
- 'padding' => null,
- 'units' => null,
- ),
- 'typography' => array(
- 'customFontSize' => null,
- 'dropCap' => null,
- 'fontFamilies' => null,
- 'fontSizes' => null,
- 'fontStyle' => null,
- 'fontWeight' => null,
- 'letterSpacing' => null,
- 'lineHeight' => null,
- 'textDecoration' => null,
- 'textTransform' => null,
- ),
- );
-
- /**
- * The valid properties under the styles key.
- *
- * @var array
- */
- const VALID_STYLES = array(
- 'border' => array(
- 'color' => null,
- 'radius' => null,
- 'style' => null,
- 'width' => null,
- ),
- 'color' => array(
- 'background' => null,
- 'gradient' => null,
- 'text' => null,
- ),
- 'filter' => array(
- 'duotone' => null,
- ),
- 'spacing' => array(
- 'margin' => null,
- 'padding' => null,
- 'blockGap' => 'top',
- ),
- 'typography' => array(
- 'fontFamily' => null,
- 'fontSize' => null,
- 'fontStyle' => null,
- 'fontWeight' => null,
- 'letterSpacing' => null,
- 'lineHeight' => null,
- 'textDecoration' => null,
- 'textTransform' => null,
- ),
- );
-
- /**
- * The valid elements that can be found under styles.
- *
- * @var string[]
- */
- const ELEMENTS = array(
- 'link' => 'a',
- 'h1' => 'h1',
- 'h2' => 'h2',
- 'h3' => 'h3',
- 'h4' => 'h4',
- 'h5' => 'h5',
- 'h6' => 'h6',
- );
-
- /**
- * The latest version of the schema in use.
- *
- * @var int
- */
- const LATEST_SCHEMA = 2;
-
- /**
- * Constructor.
- *
- * @param array $theme_json A structure that follows the theme.json schema.
- * @param string $origin Optional. What source of data this object represents.
- * One of 'default', 'theme', or 'custom'. Default 'theme'.
- */
- public function __construct( $theme_json = array(), $origin = 'theme' ) {
- if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) {
- $origin = 'theme';
- }
-
- $this->theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json );
- $valid_block_names = array_keys( static::get_blocks_metadata() );
- $valid_element_names = array_keys( static::ELEMENTS );
- $theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names );
- $this->theme_json = static::maybe_opt_in_into_settings( $theme_json );
-
- // Internally, presets are keyed by origin.
- $nodes = static::get_setting_nodes( $this->theme_json );
- foreach ( $nodes as $node ) {
- foreach ( static::PRESETS_METADATA as $preset_metadata ) {
- $path = array_merge( $node['path'], $preset_metadata['path'] );
- $preset = _wp_array_get( $this->theme_json, $path, null );
- if ( null !== $preset ) {
- // If the preset is not already keyed by origin.
- if ( isset( $preset[0] ) || empty( $preset ) ) {
- _wp_array_set( $this->theme_json, $path, array( $origin => $preset ) );
- }
- }
- }
- }
- }
-
- /**
- * Enables some opt-in settings if theme declared support.
- *
- * @param array $theme_json A theme.json structure to modify.
- * @return array The modified theme.json structure.
- */
- protected static function maybe_opt_in_into_settings( $theme_json ) {
- $new_theme_json = $theme_json;
-
- if (
- isset( $new_theme_json['settings']['appearanceTools'] ) &&
- true === $new_theme_json['settings']['appearanceTools']
- ) {
- static::do_opt_in_into_settings( $new_theme_json['settings'] );
- }
-
- if ( isset( $new_theme_json['settings']['blocks'] ) && is_array( $new_theme_json['settings']['blocks'] ) ) {
- foreach ( $new_theme_json['settings']['blocks'] as &$block ) {
- if ( isset( $block['appearanceTools'] ) && ( true === $block['appearanceTools'] ) ) {
- static::do_opt_in_into_settings( $block );
- }
- }
- }
-
- return $new_theme_json;
- }
-
- /**
- * Enables some settings.
- *
- * @param array $context The context to which the settings belong.
- */
- protected static function do_opt_in_into_settings( &$context ) {
- $to_opt_in = array(
- array( 'border', 'color' ),
- array( 'border', 'radius' ),
- array( 'border', 'style' ),
- array( 'border', 'width' ),
- array( 'color', 'link' ),
- array( 'spacing', 'blockGap' ),
- array( 'spacing', 'margin' ),
- array( 'spacing', 'padding' ),
- array( 'typography', 'lineHeight' ),
- );
-
- foreach ( $to_opt_in as $path ) {
- // Use "unset prop" as a marker instead of "null" because
- // "null" can be a valid value for some props (e.g. blockGap).
- if ( 'unset prop' === _wp_array_get( $context, $path, 'unset prop' ) ) {
- _wp_array_set( $context, $path, true );
- }
- }
-
- unset( $context['appearanceTools'] );
- }
-
- /**
- * Sanitizes the input according to the schemas.
- *
- * @param array $input Structure to sanitize.
- * @param array $valid_block_names List of valid block names.
- * @param array $valid_element_names List of valid element names.
- * @return array The sanitized output.
- */
- protected static function sanitize( $input, $valid_block_names, $valid_element_names ) {
- $output = array();
-
- if ( ! is_array( $input ) ) {
- return $output;
- }
-
- $output = array_intersect_key( $input, array_flip( static::VALID_TOP_LEVEL_KEYS ) );
-
- // Some styles are only meant to be available at the top-level (e.g.: blockGap),
- // hence, the schema for blocks & elements should not have them.
- $styles_non_top_level = static::VALID_STYLES;
- foreach ( array_keys( $styles_non_top_level ) as $section ) {
- foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) {
- if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) {
- unset( $styles_non_top_level[ $section ][ $prop ] );
- }
- }
- }
-
- // Build the schema based on valid block & element names.
- $schema = array();
- $schema_styles_elements = array();
- foreach ( $valid_element_names as $element ) {
- $schema_styles_elements[ $element ] = $styles_non_top_level;
- }
- $schema_styles_blocks = array();
- $schema_settings_blocks = array();
- foreach ( $valid_block_names as $block ) {
- $schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
- $schema_styles_blocks[ $block ] = $styles_non_top_level;
- $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
- }
- $schema['styles'] = static::VALID_STYLES;
- $schema['styles']['blocks'] = $schema_styles_blocks;
- $schema['styles']['elements'] = $schema_styles_elements;
- $schema['settings'] = static::VALID_SETTINGS;
- $schema['settings']['blocks'] = $schema_settings_blocks;
-
- // Remove anything that's not present in the schema.
- foreach ( array( 'styles', 'settings' ) as $subtree ) {
- if ( ! isset( $input[ $subtree ] ) ) {
- continue;
- }
-
- if ( ! is_array( $input[ $subtree ] ) ) {
- unset( $output[ $subtree ] );
- continue;
- }
-
- $result = static::remove_keys_not_in_schema( $input[ $subtree ], $schema[ $subtree ] );
-
- if ( empty( $result ) ) {
- unset( $output[ $subtree ] );
- } else {
- $output[ $subtree ] = $result;
- }
- }
-
- return $output;
- }
-
- /**
- * Returns the metadata for each block.
- *
- * Example:
- *
- * {
- * 'core/paragraph': {
- * 'selector': 'p',
- * 'elements': {
- * 'link' => 'link selector',
- * 'etc' => 'element selector'
- * }
- * },
- * 'core/heading': {
- * 'selector': 'h1',
- * 'elements': {}
- * },
- * 'core/image': {
- * 'selector': '.wp-block-image',
- * 'duotone': 'img',
- * 'elements': {}
- * }
- * }
- *
- * @return array Block metadata.
- */
- protected static function get_blocks_metadata() {
- if ( null !== static::$blocks_metadata ) {
- return static::$blocks_metadata;
- }
-
- static::$blocks_metadata = array();
-
- $registry = WP_Block_Type_Registry::get_instance();
- $blocks = $registry->get_all_registered();
- foreach ( $blocks as $block_name => $block_type ) {
- if (
- isset( $block_type->supports['__experimentalSelector'] ) &&
- is_string( $block_type->supports['__experimentalSelector'] )
- ) {
- static::$blocks_metadata[ $block_name ]['selector'] = $block_type->supports['__experimentalSelector'];
- } else {
- static::$blocks_metadata[ $block_name ]['selector'] = '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) );
- }
-
- if (
- isset( $block_type->supports['color']['__experimentalDuotone'] ) &&
- is_string( $block_type->supports['color']['__experimentalDuotone'] )
- ) {
- static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
- }
-
- // Assign defaults, then overwrite those that the block sets by itself.
- // If the block selector is compounded, will append the element to each
- // individual block selector.
- $block_selectors = explode( ',', static::$blocks_metadata[ $block_name ]['selector'] );
- foreach ( static::ELEMENTS as $el_name => $el_selector ) {
- $element_selector = array();
- foreach ( $block_selectors as $selector ) {
- if ( $selector === $el_selector ) {
- $element_selector = array( $el_selector );
- break;
- }
-
- $element_selector[] = $selector . ' ' . $el_selector;
- }
- static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector );
- }
- }
-
- return static::$blocks_metadata;
- }
-
- /**
- * Given a tree, removes the keys that are not present in the schema.
- *
- * It is recursive and modifies the input in-place.
- *
- * @param array $tree Input to process.
- * @param array $schema Schema to adhere to.
- * @return array Returns the modified $tree.
- */
- protected static function remove_keys_not_in_schema( $tree, $schema ) {
- $tree = array_intersect_key( $tree, $schema );
-
- foreach ( $schema as $key => $data ) {
- if ( ! isset( $tree[ $key ] ) ) {
- continue;
- }
-
- if ( is_array( $schema[ $key ] ) && is_array( $tree[ $key ] ) ) {
- $tree[ $key ] = static::remove_keys_not_in_schema( $tree[ $key ], $schema[ $key ] );
-
- if ( empty( $tree[ $key ] ) ) {
- unset( $tree[ $key ] );
- }
- } elseif ( is_array( $schema[ $key ] ) && ! is_array( $tree[ $key ] ) ) {
- unset( $tree[ $key ] );
- }
- }
-
- return $tree;
- }
-
- /**
- * Returns the existing settings for each block.
- *
- * Example:
- *
- * {
- * 'root': {
- * 'color': {
- * 'custom': true
- * }
- * },
- * 'core/paragraph': {
- * 'spacing': {
- * 'customPadding': true
- * }
- * }
- * }
- *
- * @return array Settings per block.
- */
- public function get_settings() {
- if ( ! isset( $this->theme_json['settings'] ) ) {
- return array();
- } else {
- return $this->theme_json['settings'];
- }
- }
-
- /**
- * Returns the stylesheet that results of processing
- * the theme.json structure this object represents.
- *
- * @param array $types Types of styles to load. Will load all by default. It accepts:
- * 'variables': only the CSS Custom Properties for presets & custom ones.
- * 'styles': only the styles section in theme.json.
- * 'presets': only the classes for the presets.
- * @param array $origins A list of origins to include. By default it includes VALID_ORIGINS.
- * @return string Stylesheet.
- */
- public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null ) {
- if ( null === $origins ) {
- $origins = static::VALID_ORIGINS;
- }
-
- if ( is_string( $types ) ) {
- // Dispatch error and map old arguments to new ones.
- _deprecated_argument( __FUNCTION__, '5.9' );
- if ( 'block_styles' === $types ) {
- $types = array( 'styles', 'presets' );
- } elseif ( 'css_variables' === $types ) {
- $types = array( 'variables' );
- } else {
- $types = array( 'variables', 'styles', 'presets' );
- }
- }
-
- $blocks_metadata = static::get_blocks_metadata();
- $style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata );
- $setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata );
-
- $stylesheet = '';
-
- if ( in_array( 'variables', $types, true ) ) {
- $stylesheet .= $this->get_css_variables( $setting_nodes, $origins );
- }
-
- if ( in_array( 'styles', $types, true ) ) {
- $stylesheet .= $this->get_block_classes( $style_nodes );
- }
-
- if ( in_array( 'presets', $types, true ) ) {
- $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins );
- }
-
- return $stylesheet;
- }
-
- /**
- * Returns the page templates of the current theme.
- *
- * @return array
- */
- public function get_custom_templates() {
- $custom_templates = array();
- if ( ! isset( $this->theme_json['customTemplates'] ) || ! is_array( $this->theme_json['customTemplates'] ) ) {
- return $custom_templates;
- }
-
- foreach ( $this->theme_json['customTemplates'] as $item ) {
- if ( isset( $item['name'] ) ) {
- $custom_templates[ $item['name'] ] = array(
- 'title' => isset( $item['title'] ) ? $item['title'] : '',
- 'postTypes' => isset( $item['postTypes'] ) ? $item['postTypes'] : array( 'page' ),
- );
- }
- }
- return $custom_templates;
- }
-
- /**
- * Returns the template part data of current theme.
- *
- * @return array
- */
- public function get_template_parts() {
- $template_parts = array();
- if ( ! isset( $this->theme_json['templateParts'] ) || ! is_array( $this->theme_json['templateParts'] ) ) {
- return $template_parts;
- }
-
- foreach ( $this->theme_json['templateParts'] as $item ) {
- if ( isset( $item['name'] ) ) {
- $template_parts[ $item['name'] ] = array(
- 'title' => isset( $item['title'] ) ? $item['title'] : '',
- 'area' => isset( $item['area'] ) ? $item['area'] : '',
- );
- }
- }
- return $template_parts;
- }
-
- /**
- * Converts each style section 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 section this creates a new ruleset such as:
- *
- * block-selector {
- * style-property-one: value;
- * }
- *
- * @param array $style_nodes Nodes with styles.
- * @return string The new stylesheet.
- */
- protected function get_block_classes( $style_nodes ) {
- $block_rules = '';
-
- foreach ( $style_nodes as $metadata ) {
- if ( null === $metadata['selector'] ) {
- continue;
- }
-
- $node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
- $selector = $metadata['selector'];
- $settings = _wp_array_get( $this->theme_json, array( 'settings' ) );
- $declarations = static::compute_style_properties( $node, $settings );
-
- // 1. Separate the ones who use the general selector
- // and the ones who use the duotone selector.
- $declarations_duotone = array();
- foreach ( $declarations as $index => $declaration ) {
- if ( 'filter' === $declaration['name'] ) {
- unset( $declarations[ $index ] );
- $declarations_duotone[] = $declaration;
- }
- }
-
- /*
- * Reset default browser margin on the root body element.
- * This is set on the root selector **before** generating the ruleset
- * from the `theme.json`. This is to ensure that if the `theme.json` declares
- * `margin` in its `spacing` declaration for the `body` element then these
- * user-generated values take precedence in the CSS cascade.
- * @link https://github.com/WordPress/gutenberg/issues/36147.
- */
- if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
- $block_rules .= 'body { margin: 0; }';
- }
-
- // 2. Generate the rules that use the general selector.
- $block_rules .= static::to_ruleset( $selector, $declarations );
-
- // 3. Generate the rules that use the duotone selector.
- if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
- $selector_duotone = static::scope_selector( $metadata['selector'], $metadata['duotone'] );
- $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone );
- }
-
- if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
- $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
- $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
- $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
-
- $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
- if ( $has_block_gap_support ) {
- $block_rules .= '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }';
- $block_rules .= '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }';
- }
- }
- }
-
- return $block_rules;
- }
-
- /**
- * Creates new rulesets as classes for each preset value such as:
- *
- * .has-value-color {
- * color: value;
- * }
- *
- * .has-value-background-color {
- * background-color: value;
- * }
- *
- * .has-value-font-size {
- * font-size: value;
- * }
- *
- * .has-value-gradient-background {
- * background: value;
- * }
- *
- * p.has-value-gradient-background {
- * background: value;
- * }
- *
- * @param array $setting_nodes Nodes with settings.
- * @param array $origins List of origins to process presets from.
- * @return string The new stylesheet.
- */
- protected function get_preset_classes( $setting_nodes, $origins ) {
- $preset_rules = '';
-
- foreach ( $setting_nodes as $metadata ) {
- if ( null === $metadata['selector'] ) {
- continue;
- }
-
- $selector = $metadata['selector'];
- $node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
- $preset_rules .= static::compute_preset_classes( $node, $selector, $origins );
- }
-
- return $preset_rules;
- }
-
- /**
- * Converts each styles section 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 section this creates a new ruleset such as:
- *
- * block-selector {
- * --wp--preset--category--slug: value;
- * --wp--custom--variable: value;
- * }
- *
- * @param array $nodes Nodes with settings.
- * @param array $origins List of origins to process.
- * @return string The new stylesheet.
- */
- protected function get_css_variables( $nodes, $origins ) {
- $stylesheet = '';
- foreach ( $nodes as $metadata ) {
- if ( null === $metadata['selector'] ) {
- continue;
- }
-
- $selector = $metadata['selector'];
-
- $node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
- $declarations = array_merge( static::compute_preset_vars( $node, $origins ), static::compute_theme_vars( $node ) );
-
- $stylesheet .= static::to_ruleset( $selector, $declarations );
- }
-
- return $stylesheet;
- }
-
- /**
- * Given a selector and a declaration list,
- * creates the corresponding ruleset.
- *
- * To help debugging, will add some space
- * if SCRIPT_DEBUG is defined and true.
- *
- * @param string $selector CSS selector.
- * @param array $declarations List of declarations.
- *
- * @return string CSS ruleset.
- */
- protected static function to_ruleset( $selector, $declarations ) {
- if ( empty( $declarations ) ) {
- return '';
- }
- $ruleset = '';
-
- if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
- $declaration_block = array_reduce(
- $declarations,
- function ( $carry, $element ) {
- return $carry .= "\t" . $element['name'] . ': ' . $element['value'] . ";\n"; },
- ''
- );
- $ruleset .= $selector . " {\n" . $declaration_block . "}\n";
- } else {
- $declaration_block = array_reduce(
- $declarations,
- function ( $carry, $element ) {
- return $carry .= $element['name'] . ': ' . $element['value'] . ';'; },
- ''
- );
- $ruleset .= $selector . '{' . $declaration_block . '}';
- }
-
- return $ruleset;
- }
-
- /**
- * Function that appends a sub-selector to a existing one.
- *
- * Given the compounded $selector "h1, h2, h3"
- * and the $to_append selector ".some-class" the result will be
- * "h1.some-class, h2.some-class, h3.some-class".
- *
- * @param string $selector Original selector.
- * @param string $to_append Selector to append.
- * @return string
- */
- protected static function append_to_selector( $selector, $to_append ) {
- $new_selectors = array();
- $selectors = explode( ',', $selector );
- foreach ( $selectors as $sel ) {
- $new_selectors[] = $sel . $to_append;
- }
-
- return implode( ',', $new_selectors );
- }
-
- /**
- * Given a settings array, it returns the generated rulesets
- * for the preset classes.
- *
- * @param array $settings Settings to process.
- * @param string $selector Selector wrapping the classes.
- * @param array $origins List of origins to process.
- * @return string The result of processing the presets.
- */
- protected static function compute_preset_classes( $settings, $selector, $origins ) {
- if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
- // Classes at the global level do not need any CSS prefixed,
- // and we don't want to increase its specificity.
- $selector = '';
- }
-
- $stylesheet = '';
- foreach ( static::PRESETS_METADATA as $preset_metadata ) {
- $slugs = static::get_settings_slugs( $settings, $preset_metadata, $origins );
- foreach ( $preset_metadata['classes'] as $class => $property ) {
- foreach ( $slugs as $slug ) {
- $css_var = static::replace_slug_in_string( $preset_metadata['css_vars'], $slug );
- $class_name = static::replace_slug_in_string( $class, $slug );
- $stylesheet .= static::to_ruleset(
- static::append_to_selector( $selector, $class_name ),
- array(
- array(
- 'name' => $property,
- 'value' => 'var(' . $css_var . ') !important',
- ),
- )
- );
- }
- }
- }
-
- return $stylesheet;
- }
-
- /**
- * Function that scopes a selector with another one. This works a bit like
- * SCSS nesting except the `&` operator isn't supported.
- *
- *
- * $scope = '.a, .b .c';
- * $selector = '> .x, .y';
- * $merged = scope_selector( $scope, $selector );
- * // $merged is '.a > .x, .a .y, .b .c > .x, .b .c .y'
- *
- *
- * @param string $scope Selector to scope to.
- * @param string $selector Original selector.
- *
- * @return string Scoped selector.
- */
- protected static function scope_selector( $scope, $selector ) {
- $scopes = explode( ',', $scope );
- $selectors = explode( ',', $selector );
-
- $selectors_scoped = array();
- foreach ( $scopes as $outer ) {
- foreach ( $selectors as $inner ) {
- $selectors_scoped[] = trim( $outer ) . ' ' . trim( $inner );
- }
- }
-
- return implode( ', ', $selectors_scoped );
- }
-
- /**
- * Gets preset values keyed by slugs based on settings and metadata.
- *
- *
- * $settings = array(
- * 'typography' => array(
- * 'fontFamilies' => array(
- * array(
- * 'slug' => 'sansSerif',
- * 'fontFamily' => '"Helvetica Neue", sans-serif',
- * ),
- * array(
- * 'slug' => 'serif',
- * 'colors' => 'Georgia, serif',
- * )
- * ),
- * ),
- * );
- * $meta = array(
- * 'path' => array( 'typography', 'fontFamilies' ),
- * 'value_key' => 'fontFamily',
- * );
- * $values_by_slug = get_settings_values_by_slug();
- * // $values_by_slug === array(
- * // 'sans-serif' => '"Helvetica Neue", sans-serif',
- * // 'serif' => 'Georgia, serif',
- * // );
- *
- *
- * @param array $settings Settings to process.
- * @param array $preset_metadata One of the PRESETS_METADATA values.
- * @param array $origins List of origins to process.
- * @return array Array of presets where each key is a slug and each value is the preset value.
- */
- protected static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) {
- $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
-
- $result = array();
- foreach ( $origins as $origin ) {
- if ( ! isset( $preset_per_origin[ $origin ] ) ) {
- continue;
- }
- foreach ( $preset_per_origin[ $origin ] as $preset ) {
- $slug = _wp_to_kebab_case( $preset['slug'] );
-
- $value = '';
- if ( isset( $preset_metadata['value_key'], $preset[ $preset_metadata['value_key'] ] ) ) {
- $value_key = $preset_metadata['value_key'];
- $value = $preset[ $value_key ];
- } elseif (
- isset( $preset_metadata['value_func'] ) &&
- is_callable( $preset_metadata['value_func'] )
- ) {
- $value_func = $preset_metadata['value_func'];
- $value = call_user_func( $value_func, $preset );
- } else {
- // If we don't have a value, then don't add it to the result.
- continue;
- }
-
- $result[ $slug ] = $value;
- }
- }
- return $result;
- }
-
- /**
- * Similar to get_settings_values_by_slug, but doesn't compute the value.
- *
- * @param array $settings Settings to process.
- * @param array $preset_metadata One of the PRESETS_METADATA values.
- * @param array $origins List of origins to process.
- * @return array Array of presets where the key and value are both the slug.
- */
- protected static function get_settings_slugs( $settings, $preset_metadata, $origins = null ) {
- if ( null === $origins ) {
- $origins = static::VALID_ORIGINS;
- }
-
- $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
-
- $result = array();
- foreach ( $origins as $origin ) {
- if ( ! isset( $preset_per_origin[ $origin ] ) ) {
- continue;
- }
- foreach ( $preset_per_origin[ $origin ] as $preset ) {
- $slug = _wp_to_kebab_case( $preset['slug'] );
-
- // Use the array as a set so we don't get duplicates.
- $result[ $slug ] = $slug;
- }
- }
- return $result;
- }
-
- /**
- * Transform a slug into a CSS Custom Property.
- *
- * @param string $input String to replace.
- * @param string $slug The slug value to use to generate the custom property.
- * @return string The CSS Custom Property. Something along the lines of --wp--preset--color--black.
- */
- protected static function replace_slug_in_string( $input, $slug ) {
- return strtr( $input, array( '$slug' => $slug ) );
- }
-
- /**
- * Given the block settings, it extracts the CSS Custom Properties
- * for the presets and adds them to the $declarations array
- * following the format:
- *
- * ```php
- * array(
- * 'name' => 'property_name',
- * 'value' => 'property_value,
- * )
- * ```
- *
- * @param array $settings Settings to process.
- * @param array $origins List of origins to process.
- * @return array Returns the modified $declarations.
- */
- protected static function compute_preset_vars( $settings, $origins ) {
- $declarations = array();
- foreach ( static::PRESETS_METADATA as $preset_metadata ) {
- $values_by_slug = static::get_settings_values_by_slug( $settings, $preset_metadata, $origins );
- foreach ( $values_by_slug as $slug => $value ) {
- $declarations[] = array(
- 'name' => static::replace_slug_in_string( $preset_metadata['css_vars'], $slug ),
- 'value' => $value,
- );
- }
- }
-
- return $declarations;
- }
-
- /**
- * Given an array of settings, it extracts the CSS Custom Properties
- * for the custom values and adds them to the $declarations
- * array following the format:
- *
- * ```php
- * array(
- * 'name' => 'property_name',
- * 'value' => 'property_value,
- * )
- * ```
- *
- * @param array $settings Settings to process.
- * @return array Returns the modified $declarations.
- */
- protected static function compute_theme_vars( $settings ) {
- $declarations = array();
- $custom_values = _wp_array_get( $settings, array( 'custom' ), array() );
- $css_vars = static::flatten_tree( $custom_values );
- foreach ( $css_vars as $key => $value ) {
- $declarations[] = array(
- 'name' => '--wp--custom--' . $key,
- 'value' => $value,
- );
- }
-
- return $declarations;
- }
-
- /**
- * Given a tree, it creates a flattened one
- * by merging the keys and binding the leaf values
- * to the new keys.
- *
- * It also transforms camelCase names into kebab-case
- * and substitutes '/' by '-'.
- *
- * This is thought to be useful to generate
- * CSS Custom Properties from a tree,
- * although there's nothing in the implementation
- * of this function that requires that format.
- *
- * For example, assuming the given prefix is '--wp'
- * and the token is '--', for this input tree:
- *
- * {
- * 'some/property': 'value',
- * 'nestedProperty': {
- * 'sub-property': 'value'
- * }
- * }
- *
- * it'll return this output:
- *
- * {
- * '--wp--some-property': 'value',
- * '--wp--nested-property--sub-property': 'value'
- * }
- *
- * @param array $tree Input tree to process.
- * @param string $prefix Optional. Prefix to prepend to each variable. Default empty string.
- * @param string $token Optional. Token to use between levels. Default '--'.
- * @return array The flattened tree.
- */
- protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) {
- $result = array();
- foreach ( $tree as $property => $value ) {
- $new_key = $prefix . str_replace(
- '/',
- '-',
- strtolower( _wp_to_kebab_case( $property ) )
- );
-
- if ( is_array( $value ) ) {
- $new_prefix = $new_key . $token;
- $result = array_merge(
- $result,
- static::flatten_tree( $value, $new_prefix, $token )
- );
- } else {
- $result[ $new_key ] = $value;
- }
- }
- return $result;
- }
-
- /**
- * Given a styles array, it extracts the style properties
- * and adds them to the $declarations array following the format:
- *
- * ```php
- * array(
- * 'name' => 'property_name',
- * 'value' => 'property_value,
- * )
- * ```
- *
- * @param array $styles Styles to process.
- * @param array $settings Theme settings.
- * @param array $properties Properties metadata.
- * @return array Returns the modified $declarations.
- */
- protected static function compute_style_properties( $styles, $settings = array(), $properties = null ) {
- if ( null === $properties ) {
- $properties = static::PROPERTIES_METADATA;
- }
-
- $declarations = array();
- if ( empty( $styles ) ) {
- return $declarations;
- }
-
- foreach ( $properties as $css_property => $value_path ) {
- $value = static::get_property_value( $styles, $value_path );
-
- // Look up protected properties, keyed by value path.
- // Skip protected properties that are explicitly set to `null`.
- if ( is_array( $value_path ) ) {
- $path_string = implode( '.', $value_path );
- if (
- array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) &&
- _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null
- ) {
- continue;
- }
- }
-
- // Skip if empty and not "0" or value represents array of longhand values.
- $has_missing_value = empty( $value ) && ! is_numeric( $value );
- if ( $has_missing_value || is_array( $value ) ) {
- continue;
- }
-
- $declarations[] = array(
- 'name' => $css_property,
- 'value' => $value,
- );
- }
-
- return $declarations;
- }
-
- /**
- * Returns the style property for the given path.
- *
- * It also converts CSS Custom Property stored as
- * "var:preset|color|secondary" to the form
- * "--wp--preset--color--secondary".
- *
- * @param array $styles Styles subtree.
- * @param array $path Which property to process.
- * @return string Style property value.
- */
- protected static function get_property_value( $styles, $path ) {
- $value = _wp_array_get( $styles, $path, '' );
-
- if ( '' === $value || is_array( $value ) ) {
- return $value;
- }
-
- $prefix = 'var:';
- $prefix_len = strlen( $prefix );
- $token_in = '|';
- $token_out = '--';
- if ( 0 === strncmp( $value, $prefix, $prefix_len ) ) {
- $unwrapped_name = str_replace(
- $token_in,
- $token_out,
- substr( $value, $prefix_len )
- );
- $value = "var(--wp--$unwrapped_name)";
- }
-
- return $value;
- }
-
- /**
- * Builds metadata for the setting nodes, which returns in the form of:
- *
- * [
- * [
- * 'path' => ['path', 'to', 'some', 'node' ],
- * 'selector' => 'CSS selector for some node'
- * ],
- * [
- * 'path' => [ 'path', 'to', 'other', 'node' ],
- * 'selector' => 'CSS selector for other node'
- * ],
- * ]
- *
- * @param array $theme_json The tree to extract setting nodes from.
- * @param array $selectors List of selectors per block.
- * @return array
- */
- protected static function get_setting_nodes( $theme_json, $selectors = array() ) {
- $nodes = array();
- if ( ! isset( $theme_json['settings'] ) ) {
- return $nodes;
- }
-
- // Top-level.
- $nodes[] = array(
- 'path' => array( 'settings' ),
- 'selector' => static::ROOT_BLOCK_SELECTOR,
- );
-
- // Calculate paths for blocks.
- if ( ! isset( $theme_json['settings']['blocks'] ) ) {
- return $nodes;
- }
-
- foreach ( $theme_json['settings']['blocks'] as $name => $node ) {
- $selector = null;
- if ( isset( $selectors[ $name ]['selector'] ) ) {
- $selector = $selectors[ $name ]['selector'];
- }
-
- $nodes[] = array(
- 'path' => array( 'settings', 'blocks', $name ),
- 'selector' => $selector,
- );
- }
-
- return $nodes;
- }
-
- /**
- * Builds metadata for the style nodes, which returns in the form of:
- *
- * [
- * [
- * 'path' => [ 'path', 'to', 'some', 'node' ],
- * 'selector' => 'CSS selector for some node',
- * 'duotone' => 'CSS selector for duotone for some node'
- * ],
- * [
- * 'path' => ['path', 'to', 'other', 'node' ],
- * 'selector' => 'CSS selector for other node',
- * 'duotone' => null
- * ],
- * ]
- *
- * @param array $theme_json The tree to extract style nodes from.
- * @param array $selectors List of selectors per block.
- * @return array
- */
- protected static function get_style_nodes( $theme_json, $selectors = array() ) {
- $nodes = array();
- if ( ! isset( $theme_json['styles'] ) ) {
- return $nodes;
- }
-
- // Top-level.
- $nodes[] = array(
- 'path' => array( 'styles' ),
- 'selector' => static::ROOT_BLOCK_SELECTOR,
- );
-
- if ( isset( $theme_json['styles']['elements'] ) ) {
- foreach ( $theme_json['styles']['elements'] as $element => $node ) {
- $nodes[] = array(
- 'path' => array( 'styles', 'elements', $element ),
- 'selector' => static::ELEMENTS[ $element ],
- );
- }
- }
-
- // Blocks.
- if ( ! isset( $theme_json['styles']['blocks'] ) ) {
- return $nodes;
- }
-
- foreach ( $theme_json['styles']['blocks'] as $name => $node ) {
- $selector = null;
- if ( isset( $selectors[ $name ]['selector'] ) ) {
- $selector = $selectors[ $name ]['selector'];
- }
-
- $duotone_selector = null;
- if ( isset( $selectors[ $name ]['duotone'] ) ) {
- $duotone_selector = $selectors[ $name ]['duotone'];
- }
-
- $nodes[] = array(
- 'path' => array( 'styles', 'blocks', $name ),
- 'selector' => $selector,
- 'duotone' => $duotone_selector,
- );
-
- if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
- foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) {
- $nodes[] = array(
- 'path' => array( 'styles', 'blocks', $name, 'elements', $element ),
- 'selector' => $selectors[ $name ]['elements'][ $element ],
- );
- }
- }
- }
-
- return $nodes;
- }
-
- /**
- * Merge new incoming data.
- *
- * @param WP_Theme_JSON $incoming Data to merge.
- */
- public function merge( $incoming ) {
- $incoming_data = $incoming->get_raw_data();
- $this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data );
-
- /*
- * The array_replace_recursive algorithm merges at the leaf level,
- * but we don't want leaf arrays to be merged, so we overwrite it.
- *
- * For leaf values that are sequential arrays it will use the numeric indexes for replacement.
- * We rather replace the existing with the incoming value, if it exists.
- * This is the case of spacing.units.
- *
- * For leaf values that are associative arrays it will merge them as expected.
- * This is also not the behavior we want for the current associative arrays (presets).
- * We rather replace the existing with the incoming value, if it exists.
- * This happens, for example, when we merge data from theme.json upon existing
- * theme supports or when we merge anything coming from the same source twice.
- * This is the case of color.palette, color.gradients, color.duotone,
- * typography.fontSizes, or typography.fontFamilies.
- *
- * Additionally, for some preset types, we also want to make sure the
- * values they introduce don't conflict with default values. We do so
- * by checking the incoming slugs for theme presets and compare them
- * with the equivalent default presets: if a slug is present as a default
- * we remove it from the theme presets.
- */
- $nodes = static::get_setting_nodes( $incoming_data );
- $slugs_global = static::get_default_slugs( $this->theme_json, array( 'settings' ) );
- foreach ( $nodes as $node ) {
- $slugs_node = static::get_default_slugs( $this->theme_json, $node['path'] );
- $slugs = array_merge_recursive( $slugs_global, $slugs_node );
-
- // Replace the spacing.units.
- $path = array_merge( $node['path'], array( 'spacing', 'units' ) );
- $content = _wp_array_get( $incoming_data, $path, null );
- if ( isset( $content ) ) {
- _wp_array_set( $this->theme_json, $path, $content );
- }
-
- // Replace the presets.
- foreach ( static::PRESETS_METADATA as $preset ) {
- $override_preset = static::should_override_preset( $this->theme_json, $node['path'], $preset['override'] );
-
- foreach ( static::VALID_ORIGINS as $origin ) {
- $base_path = array_merge( $node['path'], $preset['path'] );
- $path = array_merge( $base_path, array( $origin ) );
- $content = _wp_array_get( $incoming_data, $path, null );
- if ( ! isset( $content ) ) {
- continue;
- }
-
- if ( 'theme' === $origin && $preset['use_default_names'] ) {
- foreach ( $content as &$item ) {
- if ( ! array_key_exists( 'name', $item ) ) {
- $name = static::get_name_from_defaults( $item['slug'], $base_path );
- if ( null !== $name ) {
- $item['name'] = $name;
- }
- }
- }
- }
-
- if (
- ( 'theme' !== $origin ) ||
- ( 'theme' === $origin && $override_preset )
- ) {
- _wp_array_set( $this->theme_json, $path, $content );
- } else {
- $slugs_for_preset = _wp_array_get( $slugs, $preset['path'], array() );
- $content = static::filter_slugs( $content, $slugs_for_preset );
- _wp_array_set( $this->theme_json, $path, $content );
- }
- }
- }
- }
- }
-
- /**
- * Converts all filter (duotone) presets into SVGs.
- *
- * @param array $origins List of origins to process.
- *
- * @return string SVG filters.
- */
- public function get_svg_filters( $origins ) {
- $blocks_metadata = static::get_blocks_metadata();
- $setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata );
-
- $filters = '';
- foreach ( $setting_nodes as $metadata ) {
- $node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
- if ( empty( $node['color']['duotone'] ) ) {
- continue;
- }
-
- $duotone_presets = $node['color']['duotone'];
-
- foreach ( $origins as $origin ) {
- if ( ! isset( $duotone_presets[ $origin ] ) ) {
- continue;
- }
- foreach ( $duotone_presets[ $origin ] as $duotone_preset ) {
- $filters .= gutenberg_get_duotone_filter_svg( $duotone_preset );
- }
- }
- }
-
- return $filters;
- }
-
- /**
- * Returns whether a presets should be overriden or not.
- *
- * @param array $theme_json The theme.json like structure to inspect.
- * @param array $path Path to inspect.
- * @param bool|array $override Data to compute whether to override the preset.
- * @return boolean
- */
- protected static function should_override_preset( $theme_json, $path, $override ) {
- if ( is_bool( $override ) ) {
- return $override;
- }
-
- // The relationship between whether to override the defaults
- // and whether the defaults are enabled is inverse:
- //
- // - If defaults are enabled => theme presets should not be overriden
- // - If defaults are disabled => theme presets should be overriden
- //
- // For example, a theme sets defaultPalette to false,
- // making the default palette hidden from the user.
- // In that case, we want all the theme presets to be present,
- // so they should override the defaults.
- if ( is_array( $override ) ) {
- $value = _wp_array_get( $theme_json, array_merge( $path, $override ) );
- if ( isset( $value ) ) {
- return ! $value;
- }
-
- // Search the top-level key if none was found for this node.
- $value = _wp_array_get( $theme_json, array_merge( array( 'settings' ), $override ) );
- if ( isset( $value ) ) {
- return ! $value;
- }
-
- return true;
- }
- }
-
- /**
- * Returns the default slugs for all the presets in an associative array
- * whose keys are the preset paths and the leafs is the list of slugs.
- *
- * For example:
- *
- * array(
- * 'color' => array(
- * 'palette' => array( 'slug-1', 'slug-2' ),
- * 'gradients' => array( 'slug-3', 'slug-4' ),
- * ),
- * )
- *
- * @param array $data A theme.json like structure.
- * @param array $node_path The path to inspect. It's 'settings' by default.
- *
- * @return array
- */
- protected static function get_default_slugs( $data, $node_path ) {
- $slugs = array();
-
- foreach ( static::PRESETS_METADATA as $metadata ) {
- $path = array_merge( $node_path, $metadata['path'], array( 'default' ) );
- $preset = _wp_array_get( $data, $path, null );
- if ( ! isset( $preset ) ) {
- continue;
- }
-
- $slugs_for_preset = array();
- $slugs_for_preset = array_map(
- function( $value ) {
- return isset( $value['slug'] ) ? $value['slug'] : null;
- },
- $preset
- );
- _wp_array_set( $slugs, $metadata['path'], $slugs_for_preset );
- }
-
- return $slugs;
- }
-
- /**
- * Get a `default`'s preset name by a provided slug.
- *
- * @param string $slug The slug we want to find a match from default presets.
- * @param array $base_path The path to inspect. It's 'settings' by default.
- *
- * @return string|null
- */
- protected function get_name_from_defaults( $slug, $base_path ) {
- $path = array_merge( $base_path, array( 'default' ) );
- $default_content = _wp_array_get( $this->theme_json, $path, null );
- if ( ! $default_content ) {
- return null;
- }
- foreach ( $default_content as $item ) {
- if ( $slug === $item['slug'] ) {
- return $item['name'];
- }
- }
- return null;
- }
-
- /**
- * Removes the preset values whose slug is equal to any of given slugs.
- *
- * @param array $node The node with the presets to validate.
- * @param array $slugs The slugs that should not be overriden.
- *
- * @return array The new node
- */
- protected static function filter_slugs( $node, $slugs ) {
- if ( empty( $slugs ) ) {
- return $node;
- }
-
- $new_node = array();
- foreach ( $node as $value ) {
- if ( isset( $value['slug'] ) && ! in_array( $value['slug'], $slugs, true ) ) {
- $new_node[] = $value;
- }
- }
-
- return $new_node;
- }
-
- /**
- * Removes insecure data from theme.json.
- *
- * @param array $theme_json Structure to sanitize.
- * @return array Sanitized structure.
- */
- public static function remove_insecure_properties( $theme_json ) {
- $sanitized = array();
-
- $theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json );
-
- $valid_block_names = array_keys( static::get_blocks_metadata() );
- $valid_element_names = array_keys( static::ELEMENTS );
- $theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names );
-
- $blocks_metadata = static::get_blocks_metadata();
- $style_nodes = static::get_style_nodes( $theme_json, $blocks_metadata );
- foreach ( $style_nodes as $metadata ) {
- $input = _wp_array_get( $theme_json, $metadata['path'], array() );
- if ( empty( $input ) ) {
- continue;
- }
-
- $output = static::remove_insecure_styles( $input );
- if ( ! empty( $output ) ) {
- _wp_array_set( $sanitized, $metadata['path'], $output );
- }
- }
-
- $setting_nodes = static::get_setting_nodes( $theme_json );
- foreach ( $setting_nodes as $metadata ) {
- $input = _wp_array_get( $theme_json, $metadata['path'], array() );
- if ( empty( $input ) ) {
- continue;
- }
-
- $output = static::remove_insecure_settings( $input );
- if ( ! empty( $output ) ) {
- _wp_array_set( $sanitized, $metadata['path'], $output );
- }
- }
-
- if ( empty( $sanitized['styles'] ) ) {
- unset( $theme_json['styles'] );
- } else {
- $theme_json['styles'] = $sanitized['styles'];
- }
-
- if ( empty( $sanitized['settings'] ) ) {
- unset( $theme_json['settings'] );
- } else {
- $theme_json['settings'] = $sanitized['settings'];
- }
-
- return $theme_json;
- }
-
- /**
- * Processes a setting node and returns the same node
- * without the insecure settings.
- *
- * @param array $input Node to process.
- * @return array
- */
- protected static function remove_insecure_settings( $input ) {
- $output = array();
- foreach ( static::PRESETS_METADATA as $preset_metadata ) {
- foreach ( static::VALID_ORIGINS as $origin ) {
- $path_with_origin = array_merge( $preset_metadata['path'], array( $origin ) );
- $presets = _wp_array_get( $input, $path_with_origin, null );
- if ( null === $presets ) {
- continue;
- }
-
- $escaped_preset = array();
- foreach ( $presets as $preset ) {
- if (
- esc_attr( esc_html( $preset['name'] ) ) === $preset['name'] &&
- sanitize_html_class( $preset['slug'] ) === $preset['slug']
- ) {
- $value = null;
- if ( isset( $preset_metadata['value_key'], $preset[ $preset_metadata['value_key'] ] ) ) {
- $value = $preset[ $preset_metadata['value_key'] ];
- } elseif (
- isset( $preset_metadata['value_func'] ) &&
- is_callable( $preset_metadata['value_func'] )
- ) {
- $value = call_user_func( $preset_metadata['value_func'], $preset );
- }
-
- $preset_is_valid = true;
- foreach ( $preset_metadata['properties'] as $property ) {
- if ( ! static::is_safe_css_declaration( $property, $value ) ) {
- $preset_is_valid = false;
- break;
- }
- }
-
- if ( $preset_is_valid ) {
- $escaped_preset[] = $preset;
- }
- }
- }
-
- if ( ! empty( $escaped_preset ) ) {
- _wp_array_set( $output, $path_with_origin, $escaped_preset );
- }
- }
- }
- return $output;
- }
-
- /**
- * Processes a style node and returns the same node
- * without the insecure styles.
- *
- * @param array $input Node to process.
- * @return array
- */
- protected static function remove_insecure_styles( $input ) {
- $output = array();
- $declarations = static::compute_style_properties( $input );
-
- foreach ( $declarations as $declaration ) {
- if ( static::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) {
- $path = static::PROPERTIES_METADATA[ $declaration['name'] ];
-
- // Check the value isn't an array before adding so as to not
- // double up shorthand and longhand styles.
- $value = _wp_array_get( $input, $path, array() );
- if ( ! is_array( $value ) ) {
- _wp_array_set( $output, $path, $value );
- }
- }
- }
- return $output;
- }
-
- /**
- * Checks that a declaration provided by the user is safe.
- *
- * @param string $property_name Property name in a CSS declaration, i.e. the `color` in `color: red`.
- * @param string $property_value Value in a CSS declaration, i.e. the `red` in `color: red`.
- * @return boolean
- */
- protected static function is_safe_css_declaration( $property_name, $property_value ) {
- $style_to_validate = $property_name . ': ' . $property_value;
- $filtered = esc_html( safecss_filter_attr( $style_to_validate ) );
- return ! empty( trim( $filtered ) );
- }
-
- /**
- * Returns the raw data.
- *
- * @return array Raw data.
- */
- public function get_raw_data() {
- return $this->theme_json;
- }
-
- /**
- * Transforms the given editor settings according the
- * add_theme_support format to the theme.json format.
- *
- * @param array $settings Existing editor settings.
- * @return array Config that adheres to the theme.json schema.
- */
- public static function get_from_editor_settings( $settings ) {
- $theme_settings = array(
- 'version' => static::LATEST_SCHEMA,
- 'settings' => array(),
- );
-
- // Deprecated theme supports.
- if ( isset( $settings['disableCustomColors'] ) ) {
- if ( ! isset( $theme_settings['settings']['color'] ) ) {
- $theme_settings['settings']['color'] = array();
- }
- $theme_settings['settings']['color']['custom'] = ! $settings['disableCustomColors'];
- }
-
- if ( isset( $settings['disableCustomGradients'] ) ) {
- if ( ! isset( $theme_settings['settings']['color'] ) ) {
- $theme_settings['settings']['color'] = array();
- }
- $theme_settings['settings']['color']['customGradient'] = ! $settings['disableCustomGradients'];
- }
-
- if ( isset( $settings['disableCustomFontSizes'] ) ) {
- if ( ! isset( $theme_settings['settings']['typography'] ) ) {
- $theme_settings['settings']['typography'] = array();
- }
- $theme_settings['settings']['typography']['customFontSize'] = ! $settings['disableCustomFontSizes'];
- }
-
- if ( isset( $settings['enableCustomLineHeight'] ) ) {
- if ( ! isset( $theme_settings['settings']['typography'] ) ) {
- $theme_settings['settings']['typography'] = array();
- }
- $theme_settings['settings']['typography']['lineHeight'] = $settings['enableCustomLineHeight'];
- }
-
- if ( isset( $settings['enableCustomUnits'] ) ) {
- if ( ! isset( $theme_settings['settings']['spacing'] ) ) {
- $theme_settings['settings']['spacing'] = array();
- }
- $theme_settings['settings']['spacing']['units'] = ( true === $settings['enableCustomUnits'] ) ?
- array( 'px', 'em', 'rem', 'vh', 'vw', '%' ) :
- $settings['enableCustomUnits'];
- }
-
- if ( isset( $settings['colors'] ) ) {
- if ( ! isset( $theme_settings['settings']['color'] ) ) {
- $theme_settings['settings']['color'] = array();
- }
- $theme_settings['settings']['color']['palette'] = $settings['colors'];
- }
-
- if ( isset( $settings['gradients'] ) ) {
- if ( ! isset( $theme_settings['settings']['color'] ) ) {
- $theme_settings['settings']['color'] = array();
- }
- $theme_settings['settings']['color']['gradients'] = $settings['gradients'];
- }
-
- if ( isset( $settings['fontSizes'] ) ) {
- $font_sizes = $settings['fontSizes'];
- // Back-compatibility for presets without units.
- foreach ( $font_sizes as $key => $font_size ) {
- if ( is_numeric( $font_size['size'] ) ) {
- $font_sizes[ $key ]['size'] = $font_size['size'] . 'px';
- }
- }
- if ( ! isset( $theme_settings['settings']['typography'] ) ) {
- $theme_settings['settings']['typography'] = array();
- }
- $theme_settings['settings']['typography']['fontSizes'] = $font_sizes;
- }
-
- // This allows to make the plugin work with WordPress 5.7 beta
- // as well as lower versions. The second check can be removed
- // as soon as the minimum WordPress version for the plugin
- // is bumped to 5.7.
- if ( isset( $settings['enableCustomSpacing'] ) ) {
- if ( ! isset( $theme_settings['settings']['spacing'] ) ) {
- $theme_settings['settings']['spacing'] = array();
- }
- $theme_settings['settings']['spacing']['padding'] = $settings['enableCustomSpacing'];
- }
-
- // Things that didn't land in core yet, so didn't have a setting assigned.
- // This should be removed when the plugin minimum WordPress version
- // is bumped to 5.9.
- //
- // Since WordPress 5.9, the CSS Custom Properties are enqueued for all themes,
- // so we no longer need to detect theme support for this.
- // Removing this code will have the effect of making link color
- // not visible for classic themes that opted-in and depended on the plugin
- // to show the UI control for link color to users.
- //
- // Do not port this to WordPress core.
- if ( current( (array) get_theme_support( 'experimental-link-color' ) ) ) {
- if ( ! isset( $theme_settings['settings']['color'] ) ) {
- $theme_settings['settings']['color'] = array();
- }
- $theme_settings['settings']['color']['link'] = true;
- }
-
- return $theme_settings;
- }
-
-}
diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php b/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php
deleted file mode 100644
index b00c6e708c763..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php
+++ /dev/null
@@ -1,416 +0,0 @@
- true ) );
- if ( is_array( $decoded_file ) ) {
- $config = $decoded_file;
- }
- }
- return $config;
- }
-
- /**
- * Returns a data structure used in theme.json translation.
- *
- * @deprecated
- * @return array An array of theme.json fields that are translatable and the keys that are translatable
- */
- public static function get_fields_to_translate() {
- _deprecated_function( __METHOD__, '5.9.0' );
- return array();
- }
-
- /**
- * Given a theme.json structure modifies it in place
- * to update certain values by its translated strings
- * according to the language set by the user.
- *
- * @param array $theme_json The theme.json to translate.
- * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
- * Default 'default'.
- * @return array Returns the modified $theme_json_structure.
- */
- protected static function translate( $theme_json, $domain = 'default' ) {
- if ( null === static::$i18n_schema ) {
- $i18n_schema = wp_json_file_decode( __DIR__ . '/theme-i18n.json' );
- static::$i18n_schema = null === $i18n_schema ? array() : $i18n_schema;
- }
-
- return translate_settings_using_i18n_schema( static::$i18n_schema, $theme_json, $domain );
- }
-
- /**
- * Return core's origin config.
- *
- * @return WP_Theme_JSON_Gutenberg Entity that holds core data.
- */
- public static function get_core_data() {
- if ( null !== static::$core ) {
- return static::$core;
- }
-
- $config = static::read_json_file( __DIR__ . '/theme.json' );
- $config = static::translate( $config );
- static::$core = new WP_Theme_JSON_Gutenberg( $config, 'default' );
-
- return static::$core;
- }
-
- /**
- * Returns the theme's data.
- *
- * Data from theme.json will be backfilled from existing
- * theme supports, if any. Note that if the same data
- * is present in theme.json and in theme supports,
- * the theme.json takes precedence.
- *
- * @param array $deprecated Deprecated argument.
- * @return WP_Theme_JSON_Gutenberg Entity that holds theme data.
- */
- public static function get_theme_data( $deprecated = array() ) {
- if ( ! empty( $deprecated ) ) {
- _deprecated_argument( __METHOD__, '5.9' );
- }
- if ( null === static::$theme ) {
- $theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
- $theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
- static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data );
-
- if ( wp_get_theme()->parent() ) {
- // Get parent theme.json.
- $parent_theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json', true ) );
- $parent_theme_json_data = static::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) );
- $parent_theme = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data );
-
- // Merge the child theme.json into the parent theme.json.
- // The child theme takes precedence over the parent.
- $parent_theme->merge( static::$theme );
- static::$theme = $parent_theme;
- }
- }
-
- /*
- * We want the presets and settings declared in theme.json
- * to override the ones declared via theme supports.
- * So we take theme supports, transform it to theme.json shape
- * and merge the static::$theme upon that.
- */
- $theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_default_block_editor_settings() );
- if ( ! static::theme_has_support() ) {
- if ( ! isset( $theme_support_data['settings']['color'] ) ) {
- $theme_support_data['settings']['color'] = array();
- }
-
- $default_palette = false;
- if ( current_theme_supports( 'default-color-palette' ) ) {
- $default_palette = true;
- }
- if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) {
- // If the theme does not have any palette, we still want to show the core one.
- $default_palette = true;
- }
- $theme_support_data['settings']['color']['defaultPalette'] = $default_palette;
-
- $default_gradients = false;
- if ( current_theme_supports( 'default-gradient-presets' ) ) {
- $default_gradients = true;
- }
- if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) {
- // If the theme does not have any gradients, we still want to show the core ones.
- $default_gradients = true;
- }
- $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients;
- }
- $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data );
- $with_theme_supports->merge( static::$theme );
-
- return $with_theme_supports;
- }
-
- /**
- * Returns the custom post type that contains the user's origin config
- * for the current theme or a void array if none are found.
- *
- * This can also create and return a new draft custom post type.
- *
- * @param WP_Theme $theme The theme object. If empty, it
- * defaults to the current theme.
- * @param bool $create_post Optional. Whether a new custom post
- * type should be created if none are
- * found. False by default.
- * @param array $post_status_filter Filter Optional. custom post type by
- * post status. ['publish'] by default,
- * so it only fetches published posts.
- * @return array Custom Post Type for the user's origin config.
- */
- public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) {
- if ( ! $theme instanceof WP_Theme ) {
- $theme = wp_get_theme();
- }
- $user_cpt = array();
- $post_type_filter = 'wp_global_styles';
- $args = array(
- 'numberposts' => 1,
- 'orderby' => 'date',
- 'order' => 'desc',
- 'post_type' => $post_type_filter,
- 'post_status' => $post_status_filter,
- 'tax_query' => array(
- array(
- 'taxonomy' => 'wp_theme',
- 'field' => 'name',
- 'terms' => $theme->get_stylesheet(),
- ),
- ),
- );
-
- $cache_key = sprintf( 'wp_global_styles_%s', md5( serialize( $args ) ) );
- $post_id = wp_cache_get( $cache_key );
-
- if ( (int) $post_id > 0 ) {
- return get_post( $post_id, ARRAY_A );
- }
-
- // Special case: '-1' is a results not found.
- if ( -1 === $post_id && ! $create_post ) {
- return $user_cpt;
- }
-
- $recent_posts = wp_get_recent_posts( $args );
- if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
- $user_cpt = $recent_posts[0];
- } elseif ( $create_post ) {
- $cpt_post_id = wp_insert_post(
- array(
- 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
- 'post_status' => 'publish',
- 'post_title' => __( 'Custom Styles', 'default' ),
- 'post_type' => $post_type_filter,
- 'post_name' => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ),
- 'tax_input' => array(
- 'wp_theme' => array( wp_get_theme()->get_stylesheet() ),
- ),
- ),
- true
- );
- $user_cpt = get_post( $cpt_post_id, ARRAY_A );
- }
- $cache_expiration = $user_cpt ? DAY_IN_SECONDS : HOUR_IN_SECONDS;
- wp_cache_set( $cache_key, $user_cpt ? $user_cpt['ID'] : -1, '', $cache_expiration );
-
- return $user_cpt;
- }
-
- /**
- * Returns the user's origin config.
- *
- * @return WP_Theme_JSON_Gutenberg Entity that holds styles for user data.
- */
- public static function get_user_data() {
- if ( null !== static::$user ) {
- return static::$user;
- }
-
- $config = array();
- $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
-
- 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 ) {
- trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() );
- return new WP_Theme_JSON_Gutenberg( $config, 'custom' );
- }
-
- // Very important to verify if the flag isGlobalStylesUserThemeJSON is true.
- // If is not true the content was not escaped and is not safe.
- if (
- is_array( $decoded_data ) &&
- isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
- $decoded_data['isGlobalStylesUserThemeJSON']
- ) {
- unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
- $config = $decoded_data;
- }
- }
- static::$user = new WP_Theme_JSON_Gutenberg( $config, 'custom' );
-
- return static::$user;
- }
-
- /**
- * There are three sources of data (origins) for a site:
- * default, theme, and custom. The custom's has higher priority
- * than the theme's, and the theme's higher than defaults's.
- *
- * Unlike the getters {@link get_core_data},
- * {@link get_theme_data}, and {@link get_user_data},
- * this method returns data after it has been merged
- * with the previous origins. This means that if the same piece of data
- * is declared in different origins (user, theme, and core),
- * the last origin overrides the previous.
- *
- * For example, if the user has set a background color
- * for the paragraph block, and the theme has done it as well,
- * the user preference wins.
- *
- * @param string $origin Optional. To what level should we merge data.
- * Valid values are 'theme' or 'custom'.
- * Default is 'custom'.
- * @return WP_Theme_JSON_Gutenberg
- */
- public static function get_merged_data( $origin = 'custom' ) {
- if ( is_array( $origin ) ) {
- _deprecated_argument( __FUNCTION__, '5.9' );
- }
-
- $result = new WP_Theme_JSON_Gutenberg();
- $result->merge( static::get_core_data() );
- $result->merge( static::get_theme_data() );
-
- if ( 'custom' === $origin ) {
- $result->merge( static::get_user_data() );
- }
-
- return $result;
- }
-
- /**
- * Returns the ID of the custom post type
- * that stores user data.
- *
- * @return integer|null
- */
- public static function get_user_global_styles_post_id() {
- if ( null !== static::$user_custom_post_type_id ) {
- return static::$user_custom_post_type_id;
- }
-
- $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme(), true );
-
- if ( array_key_exists( 'ID', $user_cpt ) ) {
- static::$user_custom_post_type_id = $user_cpt['ID'];
- }
-
- return static::$user_custom_post_type_id;
- }
-
- /**
- * Whether the current theme has a theme.json file.
- *
- * @return bool
- */
- public static function theme_has_support() {
- if ( ! isset( static::$theme_has_support ) ) {
- static::$theme_has_support = (
- is_readable( static::get_file_path_from_theme( 'theme.json' ) ) ||
- is_readable( static::get_file_path_from_theme( 'theme.json', true ) )
- );
- }
-
- return static::$theme_has_support;
- }
-
- /**
- * Builds the path to the given file and checks that it is readable.
- *
- * If it isn't, returns an empty string, otherwise returns the whole file path.
- *
- * @param string $file_name Name of the file.
- * @param bool $template Optional. Use template theme directory. Default false.
- * @return string The whole file path or empty if the file doesn't exist.
- */
- protected static function get_file_path_from_theme( $file_name, $template = false ) {
- $path = $template ? get_template_directory() : get_stylesheet_directory();
- $candidate = $path . '/' . $file_name;
-
- return is_readable( $candidate ) ? $candidate : '';
- }
-
- /**
- * Cleans the cached data so it can be recalculated.
- */
- public static function clean_cached_data() {
- static::$core = null;
- static::$theme = null;
- static::$user = null;
- static::$user_custom_post_type_id = null;
- static::$theme_has_support = null;
- static::$i18n_schema = null;
- }
-
-}
-
-add_action( 'switch_theme', array( 'WP_Theme_JSON_Resolver_Gutenberg', 'clean_cached_data' ) );
-add_action( 'start_previewing_theme', array( 'WP_Theme_JSON_Resolver_Gutenberg', 'clean_cached_data' ) );
diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php b/lib/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php
deleted file mode 100644
index 0fa833c68072c..0000000000000
--- a/lib/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php
+++ /dev/null
@@ -1,489 +0,0 @@
- 'border.color',
- 'border.customStyle' => 'border.style',
- 'border.customWidth' => 'border.width',
- 'typography.customFontStyle' => 'typography.fontStyle',
- 'typography.customFontWeight' => 'typography.fontWeight',
- 'typography.customLetterSpacing' => 'typography.letterSpacing',
- 'typography.customTextDecorations' => 'typography.textDecoration',
- 'typography.customTextTransforms' => 'typography.textTransform',
- );
-
- /**
- * Maps old properties to their new location within the schema's settings.
- * This will be applied at both the defaults and individual block levels.
- */
- const V1_TO_V2_RENAMED_PATHS = array(
- 'border.customRadius' => 'border.radius',
- 'spacing.customMargin' => 'spacing.margin',
- 'spacing.customPadding' => 'spacing.padding',
- 'typography.customLineHeight' => 'typography.lineHeight',
- );
-
- /**
- * Function that migrates a given theme.json structure to the last version.
- *
- * @param array $theme_json The structure to migrate.
- *
- * @return array The structure in the last version.
- */
- public static function migrate( $theme_json ) {
- // Can be removed when the plugin minimum required version is WordPress 5.8.
- // This doesn't need to land in WordPress core.
- if ( ! isset( $theme_json['version'] ) || 0 === $theme_json['version'] ) {
- $theme_json = self::migrate_v0_to_v1( $theme_json );
- }
-
- // Provide backwards compatibility for settings that did not land in 5.8
- // and have had their `custom` prefixed removed since.
- // Can be removed when the plugin minimum required version is WordPress 5.9.
- // This doesn't need to land in WordPress core.
- if ( 1 === $theme_json['version'] ) {
- $theme_json = self::migrate_v1_remove_custom_prefixes( $theme_json );
- }
-
- if ( 1 === $theme_json['version'] ) {
- $theme_json = self::migrate_v1_to_v2( $theme_json );
- }
-
- return $theme_json;
- }
-
- /**
- * Converts a v0 data structure into a v1 one.
- *
- * It expects input data to come in v0 form:
- *
- * {
- * 'root': {
- * 'settings': { ... },
- * 'styles': { ... }
- * }
- * 'core/paragraph': {
- * 'styles': { ... },
- * 'settings': { ... }
- * },
- * 'core/heading/h1': {
- * 'settings': { ... }
- * 'styles': { ... }
- * },
- * 'core/heading/h2': {
- * 'settings': { ... }
- * 'styles': { ... }
- * },
- * }
- *
- * And it will return v1 form:
- *
- * {
- * 'settings': {
- * 'border': { ... }
- * 'color': { ... },
- * 'typography': { ... },
- * 'spacing': { ... },
- * 'custom': { ... },
- * 'blocks': {
- * 'core/paragraph': { ... }
- * }
- * },
- * styles: {
- * border: { ... }
- * color: { ... },
- * typography: { ... },
- * spacing: { ... },
- * custom: { ... },
- * blocks: {
- * core/paragraph: { ... }
- * core/heading: {
- * elements: {
- * h1: { ... },
- * h2: { ... }
- * }
- * }
- * }
- * }
- * }
- *
- * @param array $old Data in v0 schema.
- *
- * @return array Data in v1 schema.
- */
- private static function migrate_v0_to_v1( $old ) {
- // Copy everything.
- $new = $old;
-
- // Overwrite the things that change.
- if ( isset( $old['settings'] ) ) {
- $new['settings'] = self::migrate_v0_to_v1_process_settings( $old['settings'] );
- }
- if ( isset( $old['styles'] ) ) {
- $new['styles'] = self::migrate_v0_to_v1_process_styles( $old['styles'] );
- }
-
- $new['version'] = 1;
-
- return $new;
- }
-
- /**
- * Processes the settings subtree.
- *
- * @param array $settings Array to process.
- *
- * @return array The settings in the new format.
- */
- private static function migrate_v0_to_v1_process_settings( $settings ) {
- $new = array();
- $blocks_to_consolidate = array(
- 'core/heading/h1' => 'core/heading',
- 'core/heading/h2' => 'core/heading',
- 'core/heading/h3' => 'core/heading',
- 'core/heading/h4' => 'core/heading',
- 'core/heading/h5' => 'core/heading',
- 'core/heading/h6' => 'core/heading',
- 'core/post-title/h1' => 'core/post-title',
- 'core/post-title/h2' => 'core/post-title',
- 'core/post-title/h3' => 'core/post-title',
- 'core/post-title/h4' => 'core/post-title',
- 'core/post-title/h5' => 'core/post-title',
- 'core/post-title/h6' => 'core/post-title',
- 'core/query-title/h1' => 'core/query-title',
- 'core/query-title/h2' => 'core/query-title',
- 'core/query-title/h3' => 'core/query-title',
- 'core/query-title/h4' => 'core/query-title',
- 'core/query-title/h5' => 'core/query-title',
- 'core/query-title/h6' => 'core/query-title',
- );
-
- $paths_to_override = array(
- array( 'color', 'palette' ),
- array( 'color', 'gradients' ),
- array( 'spacing', 'units' ),
- array( 'typography', 'fontSizes' ),
- array( 'typography', 'fontFamilies' ),
- array( 'custom' ),
- );
-
- // 'defaults' settings become top-level.
- if ( isset( $settings[ self::V0_ALL_BLOCKS_NAME ] ) ) {
- $new = $settings[ self::V0_ALL_BLOCKS_NAME ];
- unset( $settings[ self::V0_ALL_BLOCKS_NAME ] );
- }
-
- // 'root' settings override 'defaults'.
- if ( isset( $settings[ self::V0_ROOT_BLOCK_NAME ] ) ) {
- $new = array_replace_recursive( $new, $settings[ self::V0_ROOT_BLOCK_NAME ] );
-
- // The array_replace_recursive algorithm merges at the leaf level.
- // This means that when a leaf value is an array,
- // the incoming array won't replace the existing,
- // but the numeric indexes are used for replacement.
- //
- // These cases we hold into $paths_to_override
- // and need to replace them with the new data.
- foreach ( $paths_to_override as $path ) {
- $root_value = _wp_array_get(
- $settings,
- array_merge( array( self::V0_ROOT_BLOCK_NAME ), $path ),
- null
- );
- if ( null !== $root_value ) {
- _wp_array_set( $new, $path, $root_value );
- }
- }
-
- unset( $settings[ self::V0_ROOT_BLOCK_NAME ] );
- }
-
- if ( empty( $settings ) ) {
- return $new;
- }
-
- /*
- * At this point, it only contains block's data.
- * However, some block data we need to consolidate
- * into a different section, as it's the case for:
- *
- * - core/heading/h1, core/heading/h2, ... => core/heading
- * - core/post-title/h1, core/post-title/h2, ... => core/post-title
- * - core/query-title/h1, core/query-title/h2, ... => core/query-title
- *
- */
- $new['blocks'] = $settings;
- foreach ( $blocks_to_consolidate as $old_name => $new_name ) {
- // Unset the $old_name.
- unset( $new[ $old_name ] );
-
- // Consolidate the $new value.
- $block_settings = _wp_array_get( $settings, array( $old_name ), null );
- if ( null !== $block_settings ) {
- $new_path = array( 'blocks', $new_name );
- $new_settings = array();
- _wp_array_set( $new_settings, $new_path, $block_settings );
-
- $new = array_replace_recursive( $new, $new_settings );
- foreach ( $paths_to_override as $path ) {
- $block_value = _wp_array_get( $block_settings, $path, null );
- if ( null !== $block_value ) {
- _wp_array_set( $new, array_merge( $new_path, $path ), $block_value );
- }
- }
- }
- }
-
- return $new;
- }
-
- /**
- * Processes the styles subtree.
- *
- * @param array $styles Array to process.
- *
- * @return array The styles in the new format.
- */
- private static function migrate_v0_to_v1_process_styles( $styles ) {
- $new = array();
- $block_heading = array(
- 'core/heading/h1' => 'h1',
- 'core/heading/h2' => 'h2',
- 'core/heading/h3' => 'h3',
- 'core/heading/h4' => 'h4',
- 'core/heading/h5' => 'h5',
- 'core/heading/h6' => 'h6',
- );
- $blocks_to_consolidate = array(
- 'core/post-title/h1' => 'core/post-title',
- 'core/post-title/h2' => 'core/post-title',
- 'core/post-title/h3' => 'core/post-title',
- 'core/post-title/h4' => 'core/post-title',
- 'core/post-title/h5' => 'core/post-title',
- 'core/post-title/h6' => 'core/post-title',
- 'core/query-title/h1' => 'core/query-title',
- 'core/query-title/h2' => 'core/query-title',
- 'core/query-title/h3' => 'core/query-title',
- 'core/query-title/h4' => 'core/query-title',
- 'core/query-title/h5' => 'core/query-title',
- 'core/query-title/h6' => 'core/query-title',
- );
-
- // Styles within root become top-level.
- if ( isset( $styles[ self::V0_ROOT_BLOCK_NAME ] ) ) {
- $new = $styles[ self::V0_ROOT_BLOCK_NAME ];
- unset( $styles[ self::V0_ROOT_BLOCK_NAME ] );
-
- // Transform root.styles.color.link into elements.link.color.text.
- if ( isset( $new['color']['link'] ) ) {
- $new['elements']['link']['color']['text'] = $new['color']['link'];
- unset( $new['color']['link'] );
- }
- }
-
- if ( empty( $styles ) ) {
- return $new;
- }
-
- /*
- * At this point, it only contains block's data.
- * However, we still need to consolidate a few things:
- *
- * - link element => transform from link color property
- * - heading elements => consolidate multiple blocks (core/heading/h1, core/heading/h2)
- * into a single one (core/heading).
- */
- $new['blocks'] = $styles;
-
- // link elements.
- foreach ( $new['blocks'] as $block_name => $metadata ) {
- if ( isset( $metadata['color']['link'] ) ) {
- $new['blocks'][ $block_name ]['elements']['link']['color']['text'] = $metadata['color']['link'];
- unset( $new['blocks'][ $block_name ]['color']['link'] );
- }
- }
-
- /*
- * The heading block needs a special treatment:
- *
- * - if it has a link color => it needs to be moved to the blocks.core/heading
- * - the rest is consolidated into the corresponding element
- *
- */
- foreach ( $block_heading as $old_name => $new_name ) {
- if ( ! isset( $new['blocks'][ $old_name ] ) ) {
- continue;
- }
-
- _wp_array_set( $new, array( 'elements', $new_name ), $new['blocks'][ $old_name ] );
-
- if ( isset( $new['blocks'][ $old_name ]['elements'] ) ) {
- _wp_array_set( $new, array( 'blocks', 'core/heading', 'elements' ), $new['blocks'][ $old_name ]['elements'] );
- }
-
- unset( $new['blocks'][ $old_name ] );
-
- }
-
- /*
- * Port the styles from the old blocks to the new,
- * overriding the previous values.
- */
- foreach ( $blocks_to_consolidate as $old_name => $new_name ) {
- if ( ! isset( $new['blocks'][ $old_name ] ) ) {
- continue;
- }
-
- _wp_array_set( $new, array( 'blocks', $new_name ), $new['blocks'][ $old_name ] );
- unset( $new['blocks'][ $old_name ] );
-
- }
-
- return $new;
- }
-
- /**
- * Removes the custom prefixes for a few properties that only worked in the plugin:
- *
- * 'border.customColor' => 'border.color',
- * 'border.customStyle' => 'border.style',
- * 'border.customWidth' => 'border.width',
- * 'typography.customFontStyle' => 'typography.fontStyle',
- * 'typography.customFontWeight' => 'typography.fontWeight',
- * 'typography.customLetterSpacing' => 'typography.letterSpacing',
- * 'typography.customTextDecorations' => 'typography.textDecoration',
- * 'typography.customTextTransforms' => 'typography.textTransform',
- *
- * @param array $old Data to migrate.
- *
- * @return array Data without the custom prefixes.
- */
- private static function migrate_v1_remove_custom_prefixes( $old ) {
- // Copy everything.
- $new = $old;
-
- // Overwrite the things that change.
- if ( isset( $old['settings'] ) ) {
- $new['settings'] = self::rename_paths( $old['settings'], self::V1_RENAMED_PATHS );
- }
-
- return $new;
- }
-
- /**
- * Removes the custom prefixes for a few properties
- * that were part of v1:
- *
- * 'border.customRadius' => 'border.radius',
- * 'spacing.customMargin' => 'spacing.margin',
- * 'spacing.customPadding' => 'spacing.padding',
- * 'typography.customLineHeight' => 'typography.lineHeight',
- *
- * @param array $old Data to migrate.
- *
- * @return array Data without the custom prefixes.
- */
- private static function migrate_v1_to_v2( $old ) {
- // Copy everything.
- $new = $old;
-
- // Overwrite the things that changed.
- if ( isset( $old['settings'] ) ) {
- $new['settings'] = self::rename_paths( $old['settings'], self::V1_TO_V2_RENAMED_PATHS );
- }
-
- // Set the new version.
- $new['version'] = 2;
-
- return $new;
- }
-
- /**
- * Processes the settings subtree.
- *
- * @param array $settings Array to process.
- * @param array $paths_to_rename Paths to rename.
- *
- * @return array The settings in the new format.
- */
- private static function rename_paths( $settings, $paths_to_rename ) {
- $new_settings = $settings;
-
- // Process any renamed/moved paths within default settings.
- self::rename_settings( $new_settings, $paths_to_rename );
-
- // Process individual block settings.
- if ( isset( $new_settings['blocks'] ) && is_array( $new_settings['blocks'] ) ) {
- foreach ( $new_settings['blocks'] as &$block_settings ) {
- self::rename_settings( $block_settings, $paths_to_rename );
- }
- }
-
- return $new_settings;
- }
-
- /**
- * Processes a settings array, renaming or moving properties.
- *
- * @param array $settings Reference to settings either defaults or an individual block's.
- * @param array $paths_to_rename Paths to rename.
- */
- private static function rename_settings( &$settings, $paths_to_rename ) {
- foreach ( $paths_to_rename as $original => $renamed ) {
- $original_path = explode( '.', $original );
- $renamed_path = explode( '.', $renamed );
- $current_value = _wp_array_get( $settings, $original_path, null );
-
- if ( null !== $current_value ) {
- _wp_array_set( $settings, $renamed_path, $current_value );
- self::unset_setting_by_path( $settings, $original_path );
- }
- }
- }
-
- /**
- * Removes a property from within the provided settings by its path.
- *
- * @param array $settings Reference to the current settings array.
- * @param array $path Path to the property to be removed.
- *
- * @return void
- */
- private static function unset_setting_by_path( &$settings, $path ) {
- $tmp_settings = &$settings; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
- $last_key = array_pop( $path );
- foreach ( $path as $key ) {
- $tmp_settings = &$tmp_settings[ $key ];
- }
-
- unset( $tmp_settings[ $last_key ] );
- }
-}
diff --git a/lib/compat/wordpress-5.9/default-editor-styles.php b/lib/compat/wordpress-5.9/default-editor-styles.php
deleted file mode 100644
index ff796b53c02a7..0000000000000
--- a/lib/compat/wordpress-5.9/default-editor-styles.php
+++ /dev/null
@@ -1,41 +0,0 @@
- file_get_contents( $default_editor_styles_file ),
- ),
- );
-
- // Remove the default font addition from Core Code.
- $styles_without_core_styles = array();
- if ( isset( $settings['styles'] ) ) {
- foreach ( $settings['styles'] as $style ) {
- if (
- ! isset( $style['__unstableType'] ) ||
- 'core' !== $style['__unstableType']
- ) {
- $styles_without_core_styles[] = $style;
- }
- }
- }
- $settings['styles'] = $styles_without_core_styles;
-
- return $settings;
-}
-add_filter( 'block_editor_settings_all', 'gutenberg_extend_block_editor_settings_with_default_editor_styles' );
diff --git a/lib/compat/wordpress-5.9/default-theme-supports.php b/lib/compat/wordpress-5.9/default-theme-supports.php
deleted file mode 100644
index 0c204b46d0a5e..0000000000000
--- a/lib/compat/wordpress-5.9/default-theme-supports.php
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- wp_remote_retrieve_body( $response ),
- );
- }
- } else {
- $file = get_theme_file_path( $style );
- if ( is_file( $file ) ) {
- $styles[] = array(
- 'css' => file_get_contents( $file ),
- 'baseURL' => get_theme_file_uri( $style ),
- );
- }
- }
- }
- }
-
- return $styles;
-}
-
-/**
- * Initialize the Gutenberg Site Editor.
- *
- * @since 7.2.0
- *
- * @param string $hook Page.
- */
-function gutenberg_edit_site_init( $hook ) {
- global $current_screen, $post, $editor_styles;
-
- if ( ! gutenberg_is_edit_site_page( $hook ) ) {
- return;
- }
-
- if ( gutenberg_is_edit_site_list_page() ) {
- $post_type = get_post_type_object( $_GET['postType'] );
-
- if ( ! $post_type ) {
- wp_die( __( 'Invalid post type.', 'gutenberg' ) );
- }
- }
-
- // Default to is-fullscreen-mode to avoid rendering wp-admin navigation menu while loading and
- // having jumps in the UI.
- add_filter(
- 'admin_body_class',
- static function( $classes ) {
- return "$classes is-fullscreen-mode";
- }
- );
-
- $indexed_template_types = array();
- foreach ( get_default_block_template_types() as $slug => $template_type ) {
- $template_type['slug'] = (string) $slug;
- $indexed_template_types[] = $template_type;
- }
-
- $custom_settings = array(
- 'siteUrl' => site_url(),
- 'postsPerPage' => get_option( 'posts_per_page' ),
- 'styles' => gutenberg_get_editor_styles(),
- 'defaultTemplateTypes' => $indexed_template_types,
- 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
- '__unstableHomeTemplate' => gutenberg_resolve_home_template(),
- );
-
- // Add additional back-compat patterns registered by `current_screen` et al.
- $custom_settings['__experimentalAdditionalBlockPatterns'] = WP_Block_Patterns_Registry::get_instance()->get_all_registered( true );
- $custom_settings['__experimentalAdditionalBlockPatternCategories'] = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered( true );
-
- /**
- * Make the WP Screen object aware that this is a block editor page.
- * Since custom blocks check whether the screen is_block_editor,
- * this is required for custom blocks to be loaded.
- * See wp_enqueue_registered_block_scripts_and_styles in wp-includes/script-loader.php
- */
- $current_screen->is_block_editor( true );
-
- $site_editor_context = new WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) );
- $settings = get_block_editor_settings( $custom_settings, $site_editor_context );
- $active_global_styles_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id();
- $active_theme = wp_get_theme()->get_stylesheet();
- gutenberg_initialize_editor(
- 'edit_site_editor',
- 'edit-site',
- array(
- 'preload_paths' => array_merge(
- array(
- array( '/wp/v2/media', 'OPTIONS' ),
- '/wp/v2/types?context=view',
- '/wp/v2/types/wp_template?context=edit',
- '/wp/v2/types/wp_template-part?context=edit',
- '/wp/v2/taxonomies?context=view',
- '/wp/v2/pages?context=edit',
- '/wp/v2/categories?context=edit',
- '/wp/v2/posts?context=edit',
- '/wp/v2/tags?context=edit',
- '/wp/v2/templates?context=edit&per_page=-1',
- '/wp/v2/template-parts?context=edit&per_page=-1',
- '/wp/v2/settings',
- '/wp/v2/themes?context=edit&status=active',
- '/wp/v2/global-styles/' . $active_global_styles_id . '?context=edit',
- '/wp/v2/global-styles/' . $active_global_styles_id,
- '/wp/v2/global-styles/themes/' . $active_theme,
- )
- ),
- 'initializer_name' => 'initializeEditor',
- 'editor_settings' => $settings,
- )
- );
-
- wp_add_inline_script(
- 'wp-blocks',
- sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $post ) ) ),
- 'after'
- );
-
- wp_enqueue_script( 'wp-edit-site' );
- wp_enqueue_script( 'wp-format-library' );
- wp_enqueue_style( 'wp-edit-site' );
- wp_enqueue_style( 'wp-format-library' );
- wp_enqueue_media();
-
- if (
- current_theme_supports( 'wp-block-styles' ) ||
- ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 )
- ) {
- wp_enqueue_style( 'wp-block-library-theme' );
- }
-
- /**
- * Fires after block assets have been enqueued for the editing interface.
- *
- * Call `add_action` on any hook before 'admin_enqueue_scripts'.
- *
- * In the function call you supply, simply use `wp_enqueue_script` and
- * `wp_enqueue_style` to add your functionality to the block editor.
- *
- * @since 5.0.0
- */
-
- do_action( 'enqueue_block_editor_assets' );
-
-}
-add_action( 'admin_enqueue_scripts', 'gutenberg_edit_site_init' );
-
-/**
- * Tells the script loader to load the scripts and styles of custom block on site editor screen.
- *
- * @param bool $is_block_editor_screen Current decision about loading block assets.
- * @return bool Filtered decision about loading block assets.
- */
-function gutenberg_site_editor_load_block_editor_scripts_and_styles( $is_block_editor_screen ) {
- return ( is_callable( 'get_current_screen' ) && get_current_screen() && 'appearance_page_gutenberg-edit-site' === get_current_screen()->base )
- ? true
- : $is_block_editor_screen;
-}
-add_filter( 'should_load_block_editor_scripts_and_styles', 'gutenberg_site_editor_load_block_editor_scripts_and_styles' );
-
-/**
- * Do a server-side redirection if missing `postType` and `postId`
- * query args when visiting site editor.
- *
- * Note: The `site-editor.php` should handle redirection when migrated into the WP core.
- *
- * @return void
- */
-function gutenberg_maybe_redirect_to_homepage() {
- if ( empty( $_GET['postType'] ) && empty( $_GET['postId'] ) ) {
- $template = gutenberg_resolve_home_template();
- if ( ! $template ) {
- return;
- }
-
- $redirect_url = add_query_arg(
- $template,
- admin_url( 'themes.php?page=gutenberg-edit-site' )
- );
- wp_safe_redirect( $redirect_url );
- }
-}
-add_action( 'load-appearance_page_gutenberg-edit-site', 'gutenberg_maybe_redirect_to_homepage' );
diff --git a/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php b/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php
deleted file mode 100644
index bcc30c1411700..0000000000000
--- a/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php
+++ /dev/null
@@ -1,20 +0,0 @@
- false ) );
- $decoded_file = json_decode( file_get_contents( $filename ), $options['associative'] );
-
- if ( JSON_ERROR_NONE !== json_last_error() ) {
- trigger_error(
- sprintf(
- /* translators: 1: Path to the JSON file, 2: Error message. */
- __( 'Error when decoding a JSON file at path %1$s: %2$s', 'gutenberg' ),
- $filename,
- json_last_error_msg()
- )
- );
- return $result;
- }
-
- return $decoded_file;
- }
-}
diff --git a/lib/compat/wordpress-5.9/kses.php b/lib/compat/wordpress-5.9/kses.php
deleted file mode 100644
index 648718d6facd2..0000000000000
--- a/lib/compat/wordpress-5.9/kses.php
+++ /dev/null
@@ -1,82 +0,0 @@
- __( 'Navigation Menus', 'gutenberg' ),
- 'singular_name' => __( 'Navigation Menu', 'gutenberg' ),
- 'menu_name' => _x( 'Navigation Menus', 'Admin Menu text', 'gutenberg' ),
- 'add_new' => _x( 'Add New', 'Navigation Menu', 'gutenberg' ),
- 'add_new_item' => __( 'Add New Navigation Menu', 'gutenberg' ),
- 'new_item' => __( 'New Navigation Menu', 'gutenberg' ),
- 'edit_item' => __( 'Edit Navigation Menu', 'gutenberg' ),
- 'view_item' => __( 'View Navigation Menu', 'gutenberg' ),
- 'all_items' => __( 'All Navigation Menus', 'gutenberg' ),
- 'search_items' => __( 'Search Navigation Menus', 'gutenberg' ),
- 'parent_item_colon' => __( 'Parent Navigation Menu:', 'gutenberg' ),
- 'not_found' => __( 'No Navigation Menu found.', 'gutenberg' ),
- 'not_found_in_trash' => __( 'No Navigation Menu found in Trash.', 'gutenberg' ),
- 'archives' => __( 'Navigation Menu archives', 'gutenberg' ),
- 'insert_into_item' => __( 'Insert into Navigation Menu', 'gutenberg' ),
- 'uploaded_to_this_item' => __( 'Uploaded to this Navigation Menu', 'gutenberg' ),
- // Some of these are a bit weird, what are they for?
- 'filter_items_list' => __( 'Filter Navigation Menu list', 'gutenberg' ),
- 'items_list_navigation' => __( 'Navigation Menus list navigation', 'gutenberg' ),
- 'items_list' => __( 'Navigation Menus list', 'gutenberg' ),
- );
-
- $args = array(
- 'labels' => $labels,
- 'description' => __( 'Navigation menus.', 'gutenberg' ),
- 'public' => false,
- 'has_archive' => false,
- 'show_ui' => true,
- 'show_in_menu' => false,
- 'show_in_admin_bar' => false,
- 'show_in_rest' => true,
- 'map_meta_cap' => true,
- 'rest_base' => 'navigation',
- 'rest_controller_class' => WP_REST_Posts_Controller::class,
- 'supports' => array(
- 'title',
- 'editor',
- 'revisions',
- ),
- 'capabilities' => array(
- 'edit_others_posts' => 'edit_theme_options',
- 'delete_posts' => 'edit_theme_options',
- 'publish_posts' => 'edit_theme_options',
- 'create_posts' => 'edit_theme_options',
- 'read_private_posts' => 'edit_theme_options',
- 'delete_private_posts' => 'edit_theme_options',
- 'delete_published_posts' => 'edit_theme_options',
- 'delete_others_posts' => 'edit_theme_options',
- 'edit_private_posts' => 'edit_theme_options',
- 'edit_published_posts' => 'edit_theme_options',
- ),
- );
-
- register_post_type( 'wp_navigation', $args );
-}
-add_action( 'init', 'gutenberg_register_navigation_post_type' );
-
-/**
- * Disable "Post Attributes" for wp_navigation post type.
- *
- * The attributes are also conditionally enabled when a site has custom templates.
- * Block Theme templates can be available for every post type.
- */
-add_filter( 'theme_wp_navigation_templates', '__return_empty_array' );
-
-/**
- * Disable block editor for wp_navigation type posts so they can be managed via the UI.
- *
- * @param bool $value Whether the CPT supports block editor or not.
- * @param string $post_type Post type.
- *
- * @return bool
- */
-function gutenberg_disable_block_editor_for_navigation_post_type( $value, $post_type ) {
- if ( 'wp_navigation' === $post_type ) {
- return false;
- }
-
- return $value;
-}
-
-add_filter( 'use_block_editor_for_post_type', 'gutenberg_disable_block_editor_for_navigation_post_type', 10, 2 );
-
-/**
- * This callback disables the content editor for wp_navigation type posts.
- * Content editor cannot handle wp_navigation type posts correctly.
- * We cannot disable the "editor" feature in the wp_navigation's CPT definition
- * because it disables the ability to save navigation blocks via REST API.
- *
- * @param WP_Post $post An instance of WP_Post class.
- */
-function gutenberg_disable_content_editor_for_navigation_post_type( $post ) {
- $post_type = get_post_type( $post );
- if ( 'wp_navigation' !== $post_type ) {
- return;
- }
-
- remove_post_type_support( $post_type, 'editor' );
-}
-
-add_action( 'edit_form_after_title', 'gutenberg_disable_content_editor_for_navigation_post_type', 10, 1 );
-
-/**
- * This callback enables content editor for wp_navigation type posts.
- * We need to enable it back because we disable it to hide
- * the content editor for wp_navigation type posts.
- *
- * @see gutenberg_disable_content_editor_for_navigation_post_type
- *
- * @param WP_Post $post An instance of WP_Post class.
- */
-function gutenberg_enable_content_editor_for_navigation_post_type( $post ) {
- $post_type = get_post_type( $post );
- if ( 'wp_navigation' !== $post_type ) {
- return;
- }
-
- add_post_type_support( $post_type, 'editor' );
-}
-
-add_action( 'edit_form_after_editor', 'gutenberg_enable_content_editor_for_navigation_post_type', 10, 1 );
-
-/**
- * Rename the menu title from "All Navigation Menus" to "Navigation Menus".
- */
-function gutenberg_rename_navigation_post_type_admin_menu_entry() {
- global $submenu;
- if ( ! isset( $submenu['themes.php'] ) ) {
- return;
- }
-
- $post_type = get_post_type_object( 'wp_navigation' );
- if ( ! $post_type ) {
- return;
- }
-
- $menu_title_index = 0;
- foreach ( $submenu['themes.php'] as $key => $menu_item ) {
- if ( $post_type->labels->all_items === $menu_item[ $menu_title_index ] ) {
- $submenu['themes.php'][ $key ][ $menu_title_index ] = $post_type->labels->menu_name; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
- return;
- }
- }
-}
-
-add_action( 'admin_menu', 'gutenberg_rename_navigation_post_type_admin_menu_entry' );
-
-/**
- * Registers the navigation areas supported by the current theme. The expected
- * shape of the argument is:
- * array(
- * 'primary' => 'Primary',
- * 'secondary' => 'Secondary',
- * 'tertiary' => 'Tertiary',
- * )
- *
- * @param array $new_areas Supported navigation areas.
- */
-function gutenberg_register_navigation_areas( $new_areas ) {
- global $gutenberg_navigation_areas;
- $gutenberg_navigation_areas = $new_areas;
-}
-
-// Register the default navigation areas.
-gutenberg_register_navigation_areas(
- array(
- 'primary' => 'Primary',
- 'secondary' => 'Secondary',
- 'tertiary' => 'Tertiary',
- )
-);
-
-/**
- * Returns the available navigation areas.
- *
- * @return array Registered navigation areas.
- */
-function gutenberg_get_navigation_areas() {
- global $gutenberg_navigation_areas;
- return $gutenberg_navigation_areas;
-}
-
-/**
- * Retrieves navigation areas.
- *
- * @return array Navigation areas.
- */
-function gutenberg_get_navigation_areas_menus() {
- $areas = get_option( 'wp_navigation_areas', array() );
- if ( ! $areas ) {
- // Original key used `fse` prefix but Core options should use `wp`.
- // We fallback to the legacy option to catch sites with values in the
- // original location.
- $legacy_option_key = 'fse_navigation_areas';
- $areas = get_option( $legacy_option_key, array() );
- }
- return $areas;
-}
-
-/**
- * Shim that hides ability to edit visibility and status for wp_navigation type posts.
- * When merged to Core, the CSS below should be moved to wp-admin/css/edit.css.
- *
- * This shim can be removed when the Gutenberg plugin requires a WordPress
- * version that has the ticket below.
- *
- * @see https://core.trac.wordpress.org/ticket/54407
- *
- * @param string $hook The current admin page.
- */
-function gutenberg_hide_visibility_and_status_for_navigation_posts( $hook ) {
- $allowed_hooks = array( 'post.php', 'post-new.php' );
- if ( ! in_array( $hook, $allowed_hooks, true ) ) {
- return;
- }
-
- /**
- * HACK: We're hiding the description field using CSS because this
- * cannot be done using a filter or an action.
- */
-
- $css = << __( 'Global Styles', 'gutenberg' ),
- 'description' => 'Global styles to include in themes.',
- 'public' => false,
- 'show_ui' => false,
- 'show_in_rest' => false,
- 'rewrite' => false,
- '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(
- 'title',
- 'editor',
- 'revisions',
- ),
- );
- register_post_type( 'wp_global_styles', $args );
- }
-
- add_action( 'init', 'register_global_styles_custom_post_type' );
-}
diff --git a/lib/compat/wordpress-5.9/rest-active-global-styles.php b/lib/compat/wordpress-5.9/rest-active-global-styles.php
deleted file mode 100644
index af7f789110f66..0000000000000
--- a/lib/compat/wordpress-5.9/rest-active-global-styles.php
+++ /dev/null
@@ -1,33 +0,0 @@
-get_stylesheet() === wp_get_theme()->get_stylesheet() ) {
- // This creates a record for the current theme if not existent.
- $id = WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id();
- } else {
- $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
- $id = isset( $user_cpt['ID'] ) ? $user_cpt['ID'] : null;
- }
-
- if ( $id ) {
- $response->add_link(
- 'https://api.w.org/user-global-styles',
- rest_url( 'wp/v2/global-styles/' . $id )
- );
- }
-
- return $response;
-}
-
-add_filter( 'rest_prepare_theme', 'gutenberg_add_active_global_styles_link', 10, 2 );
diff --git a/lib/compat/wordpress-5.9/rest-api.php b/lib/compat/wordpress-5.9/rest-api.php
deleted file mode 100644
index 8414f9e514d15..0000000000000
--- a/lib/compat/wordpress-5.9/rest-api.php
+++ /dev/null
@@ -1,148 +0,0 @@
-register_routes();
-}
-add_action( 'rest_api_init', 'gutenberg_register_url_details_routes' );
-
-/**
- * Registers the menu locations REST API routes.
- */
-function gutenberg_register_rest_menu_location() {
- $nav_menu_location = new WP_REST_Menu_Locations_Controller();
- $nav_menu_location->register_routes();
-}
-add_action( 'rest_api_init', 'gutenberg_register_rest_menu_location' );
-
-/**
- * Hook in to the nav menu item post type and enable a post type rest endpoint.
- *
- * @param array $args Current registered post type args.
- * @param string $post_type Name of post type.
- *
- * @return array
- */
-function gutenberg_api_nav_menus_post_type_args( $args, $post_type ) {
- if ( 'nav_menu_item' === $post_type ) {
- $args['show_in_rest'] = true;
- $args['rest_base'] = 'menu-items';
- $args['rest_controller_class'] = 'WP_REST_Menu_Items_Controller';
- }
-
- return $args;
-}
-add_filter( 'register_post_type_args', 'gutenberg_api_nav_menus_post_type_args', 10, 2 );
-
-/**
- * Hook in to the nav_menu taxonomy and enable a taxonomy rest endpoint.
- *
- * @param array $args Current registered taxonomy args.
- * @param string $taxonomy Name of taxonomy.
- *
- * @return array
- */
-function gutenberg_api_nav_menus_taxonomy_args( $args, $taxonomy ) {
- if ( 'nav_menu' === $taxonomy ) {
- $args['show_in_rest'] = true;
- $args['rest_base'] = 'menus';
- $args['rest_controller_class'] = 'WP_REST_Menus_Controller';
- }
-
- return $args;
-}
-add_filter( 'register_taxonomy_args', 'gutenberg_api_nav_menus_taxonomy_args', 10, 2 );
-
-/**
- * Exposes the site icon url to the Gutenberg editor through the WordPress REST
- * API. The site icon url should instead be fetched from the wp/v2/settings
- * endpoint when https://github.com/WordPress/gutenberg/pull/19967 is complete.
- *
- * @param WP_REST_Response $response Response data served by the WordPress REST index endpoint.
- * @return WP_REST_Response
- */
-function gutenberg_register_site_icon_url( $response ) {
- $data = $response->data;
- $data['site_icon_url'] = get_site_icon_url();
- $response->set_data( $data );
- return $response;
-}
-
-add_filter( 'rest_index', 'gutenberg_register_site_icon_url' );
-
-/**
- * Exposes the site logo to the Gutenberg editor through the WordPress REST
- * API. This is used for fetching this information when user has no rights
- * to update settings.
- *
- * @param WP_REST_Response $response Response data served by the WordPress REST index endpoint.
- * @return WP_REST_Response
- */
-function gutenberg_register_site_logo_to_rest_index( $response ) {
- $site_logo_id = get_theme_mod( 'custom_logo' );
- $response->data['site_logo'] = $site_logo_id;
- if ( $site_logo_id ) {
- $response->add_link(
- 'https://api.w.org/featuredmedia',
- rest_url( 'wp/v2/media/' . $site_logo_id ),
- array(
- 'embeddable' => true,
- )
- );
- }
- return $response;
-}
-
-add_filter( 'rest_index', 'gutenberg_register_site_logo_to_rest_index' );
-
-/**
- * Filters WP_User_Query arguments when querying users via the REST API.
- *
- * Allow using the has_published_post argument.
- *
- * @param array $prepared_args Array of arguments for WP_User_Query.
- * @param WP_REST_Request $request The REST API request.
- *
- * @return array Returns modified $prepared_args.
- */
-function gutenberg_rest_user_query_has_published_posts( $prepared_args, $request ) {
- if ( ! empty( $request['has_published_posts'] ) ) {
- $prepared_args['has_published_posts'] = ( true === $request['has_published_posts'] )
- ? get_post_types( array( 'show_in_rest' => true ), 'names' )
- : (array) $request['has_published_posts'];
- }
- return $prepared_args;
-}
-add_filter( 'rest_user_query', 'gutenberg_rest_user_query_has_published_posts', 10, 2 );
-
-/**
- * Filters REST API collection parameters for the users controller.
- *
- * @param array $query_params JSON Schema-formatted collection parameters.
- *
- * @return array Returns the $query_params with "has_published_posts".
- */
-function gutenberg_rest_user_collection_params_has_published_posts( $query_params ) {
- $query_params['has_published_posts'] = array(
- 'description' => __( 'Limit result set to users who have published posts.', 'gutenberg' ),
- 'type' => array( 'boolean', 'array' ),
- 'items' => array(
- 'type' => 'string',
- 'enum' => get_post_types( array( 'show_in_rest' => true ), 'names' ),
- ),
- );
- return $query_params;
-}
-add_filter( 'rest_user_collection_params', 'gutenberg_rest_user_collection_params_has_published_posts' );
diff --git a/lib/compat/wordpress-5.9/script-loader.php b/lib/compat/wordpress-5.9/script-loader.php
deleted file mode 100644
index 7f407ec1b2900..0000000000000
--- a/lib/compat/wordpress-5.9/script-loader.php
+++ /dev/null
@@ -1,48 +0,0 @@
-registered['global-styles'] ) ) {
- // There's a GS stylesheet (theme has theme.json), so we overwrite it.
- wp_styles()->registered['global-styles']->extra['after'][0] = $stylesheet;
- } else {
- // No GS stylesheet (theme has no theme.json), so we enqueue a new one.
- wp_register_style( 'global-styles', false, array(), true, true );
- wp_add_inline_style( 'global-styles', $stylesheet );
- wp_enqueue_style( 'global-styles' );
- }
-}
-add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_global_styles_assets' );
-add_action( 'wp_footer', 'gutenberg_enqueue_global_styles_assets' );
diff --git a/lib/compat/wordpress-5.9/template-canvas.php b/lib/compat/wordpress-5.9/template-canvas.php
deleted file mode 100644
index 66805be797dcd..0000000000000
--- a/lib/compat/wordpress-5.9/template-canvas.php
+++ /dev/null
@@ -1,27 +0,0 @@
- so that blocks can add scripts and styles in wp_head().
- */
-$template_html = gutenberg_get_the_template_html();
-?>
->
-
-
-
-
-
->
-
-
-
-
-
-
-
diff --git a/lib/compat/wordpress-5.9/template-parts.php b/lib/compat/wordpress-5.9/template-parts.php
deleted file mode 100644
index 102c9275be408..0000000000000
--- a/lib/compat/wordpress-5.9/template-parts.php
+++ /dev/null
@@ -1,129 +0,0 @@
- __( 'Template Parts', 'gutenberg' ),
- 'singular_name' => __( 'Template Part', 'gutenberg' ),
- 'menu_name' => _x( 'Template Parts', 'Admin Menu text', 'gutenberg' ),
- 'add_new' => _x( 'Add New', 'Template Part', 'gutenberg' ),
- 'add_new_item' => __( 'Add New Template Part', 'gutenberg' ),
- 'new_item' => __( 'New Template Part', 'gutenberg' ),
- 'edit_item' => __( 'Edit Template Part', 'gutenberg' ),
- 'view_item' => __( 'View Template Part', 'gutenberg' ),
- 'view_items' => __( 'View Template Parts', 'gutenberg' ),
- 'all_items' => __( 'All Template Parts', 'gutenberg' ),
- 'search_items' => __( 'Search Template Parts', 'gutenberg' ),
- 'parent_item_colon' => __( 'Parent Template Part:', 'gutenberg' ),
- 'not_found' => __( 'No template parts found.', 'gutenberg' ),
- 'not_found_in_trash' => __( 'No template parts found in Trash.', 'gutenberg' ),
- 'archives' => __( 'Template part archives', 'gutenberg' ),
- 'insert_into_item' => __( 'Insert into template part', 'gutenberg' ),
- 'uploaded_to_this_item' => __( 'Uploaded to this template part', 'gutenberg' ),
- 'filter_items_list' => __( 'Filter template parts list', 'gutenberg' ),
- 'items_list_navigation' => __( 'Template parts list navigation', 'gutenberg' ),
- 'items_list' => __( 'Template parts list', 'gutenberg' ),
- );
-
- $args = array(
- 'labels' => $labels,
- 'description' => __( 'Template parts to include in your templates.', 'gutenberg' ),
- 'public' => false,
- 'has_archive' => false,
- 'show_ui' => true,
- 'show_in_menu' => false,
- 'show_in_admin_bar' => false,
- 'show_in_rest' => true,
- 'rest_base' => 'template-parts',
- 'rest_controller_class' => 'Gutenberg_REST_Templates_Controller',
- 'map_meta_cap' => true,
- 'supports' => array(
- 'title',
- 'slug',
- 'excerpt',
- 'editor',
- 'revisions',
- 'author',
- ),
- );
-
- register_post_type( 'wp_template_part', $args );
- }
- add_action( 'init', 'gutenberg_register_template_part_post_type' );
-
- /**
- * Registers the 'wp_template_part_area' taxonomy.
- */
- function gutenberg_register_wp_template_part_area_taxonomy() {
- register_taxonomy(
- 'wp_template_part_area',
- array( 'wp_template_part' ),
- array(
- 'public' => false,
- 'hierarchical' => false,
- 'labels' => array(
- 'name' => __( 'Template Part Areas', 'gutenberg' ),
- 'singular_name' => __( 'Template Part Area', 'gutenberg' ),
- ),
- 'query_var' => false,
- 'rewrite' => false,
- 'show_ui' => false,
- '_builtin' => true,
- 'show_in_nav_menus' => false,
- 'show_in_rest' => false,
- )
- );
- }
- add_action( 'init', 'gutenberg_register_wp_template_part_area_taxonomy' );
-
- /**
- * Sets a custom slug when creating auto-draft template parts.
- * This is only needed for auto-drafts created by the regular WP editor.
- * If this page is to be removed, this won't be necessary.
- *
- * @param int $post_id Post ID.
- */
- function gutenberg_set_unique_slug_on_create_template_part( $post_id ) {
- // This is the core function with the same functionality.
- if ( function_exists( 'wp_set_unique_slug_on_create_template_part' ) ) {
- return;
- }
-
- $post = get_post( $post_id );
- if ( 'auto-draft' !== $post->post_status ) {
- return;
- }
-
- if ( ! $post->post_name ) {
- wp_update_post(
- array(
- 'ID' => $post_id,
- 'post_name' => 'custom_slug_' . uniqid(),
- )
- );
- }
-
- $terms = get_the_terms( $post_id, 'wp_theme' );
- if ( ! $terms || ! count( $terms ) ) {
- wp_set_post_terms( $post_id, wp_get_theme()->get_stylesheet(), 'wp_theme' );
- }
- }
-
- add_action( 'save_post_wp_template_part', 'gutenberg_set_unique_slug_on_create_template_part' );
-}
diff --git a/lib/compat/wordpress-5.9/templates.php b/lib/compat/wordpress-5.9/templates.php
deleted file mode 100644
index d5bdf1c55c486..0000000000000
--- a/lib/compat/wordpress-5.9/templates.php
+++ /dev/null
@@ -1,255 +0,0 @@
- __( 'Templates', 'gutenberg' ),
- 'singular_name' => __( 'Template', 'gutenberg' ),
- 'menu_name' => _x( 'Templates', 'Admin Menu text', 'gutenberg' ),
- 'add_new' => _x( 'Add New', 'Template', 'gutenberg' ),
- 'add_new_item' => __( 'Add New Template', 'gutenberg' ),
- 'new_item' => __( 'New Template', 'gutenberg' ),
- 'edit_item' => __( 'Edit Template', 'gutenberg' ),
- 'view_item' => __( 'View Template', 'gutenberg' ),
- 'view_items' => __( 'View Templates', 'gutenberg' ),
- 'all_items' => __( 'All Templates', 'gutenberg' ),
- 'search_items' => __( 'Search Templates', 'gutenberg' ),
- 'parent_item_colon' => __( 'Parent Template:', 'gutenberg' ),
- 'not_found' => __( 'No templates found.', 'gutenberg' ),
- 'not_found_in_trash' => __( 'No templates found in Trash.', 'gutenberg' ),
- 'archives' => __( 'Template archives', 'gutenberg' ),
- 'insert_into_item' => __( 'Insert into template', 'gutenberg' ),
- 'uploaded_to_this_item' => __( 'Uploaded to this template', 'gutenberg' ),
- 'filter_items_list' => __( 'Filter templates list', 'gutenberg' ),
- 'items_list_navigation' => __( 'Templates list navigation', 'gutenberg' ),
- 'items_list' => __( 'Templates list', 'gutenberg' ),
- );
-
- $args = array(
- 'labels' => $labels,
- 'description' => __( 'Templates to include in your theme.', 'gutenberg' ),
- 'public' => false,
- 'has_archive' => false,
- 'show_ui' => true,
- 'show_in_menu' => false,
- 'show_in_admin_bar' => false,
- 'show_in_rest' => true,
- 'rest_base' => 'templates',
- 'rest_controller_class' => 'Gutenberg_REST_Templates_Controller',
- 'capability_type' => array( 'template', 'templates' ),
- 'map_meta_cap' => true,
- 'supports' => array(
- 'title',
- 'slug',
- 'excerpt',
- 'editor',
- 'revisions',
- 'author',
- ),
- );
-
- register_post_type( 'wp_template', $args );
- }
- add_action( 'init', 'gutenberg_register_template_post_type' );
-
- /**
- * Registers block editor 'wp_theme' taxonomy.
- */
- function gutenberg_register_wp_theme_taxonomy() {
- register_taxonomy(
- 'wp_theme',
- array( 'wp_template', 'wp_template_part', 'wp_global_styles' ),
- array(
- 'public' => false,
- 'hierarchical' => false,
- 'labels' => array(
- 'name' => __( 'Themes', 'gutenberg' ),
- 'singular_name' => __( 'Theme', 'gutenberg' ),
- ),
- 'query_var' => false,
- 'rewrite' => false,
- 'show_ui' => false,
- '_builtin' => true,
- 'show_in_nav_menus' => false,
- 'show_in_rest' => false,
- )
- );
- }
- add_action( 'init', 'gutenberg_register_wp_theme_taxonomy' );
-
- /**
- * Filters the capabilities of a user to conditionally grant them capabilities for managing 'wp_template' posts.
- *
- * Any user who can 'edit_theme_options' will have access.
- *
- * @param array $allcaps A user's capabilities.
- * @return array Filtered $allcaps.
- */
- function gutenberg_grant_template_caps( array $allcaps ) {
- if ( isset( $allcaps['edit_theme_options'] ) ) {
- $allcaps['edit_templates'] = $allcaps['edit_theme_options'];
- $allcaps['edit_others_templates'] = $allcaps['edit_theme_options'];
- $allcaps['edit_published_templates'] = $allcaps['edit_theme_options'];
- $allcaps['edit_private_templates'] = $allcaps['edit_theme_options'];
- $allcaps['delete_templates'] = $allcaps['edit_theme_options'];
- $allcaps['delete_others_templates'] = $allcaps['edit_theme_options'];
- $allcaps['delete_published_templates'] = $allcaps['edit_theme_options'];
- $allcaps['delete_private_templates'] = $allcaps['edit_theme_options'];
- $allcaps['publish_templates'] = $allcaps['edit_theme_options'];
- $allcaps['read_private_templates'] = $allcaps['edit_theme_options'];
- }
-
- return $allcaps;
- }
- add_filter( 'user_has_cap', 'gutenberg_grant_template_caps' );
-
- /**
- * Sets a custom slug when creating auto-draft templates.
- * This is only needed for auto-drafts created by the regular WP editor.
- * If this page is to be removed, this won't be necessary.
- *
- * @param int $post_id Post ID.
- */
- function set_unique_slug_on_create_template( $post_id ) {
- $post = get_post( $post_id );
- if ( 'auto-draft' !== $post->post_status ) {
- return;
- }
-
- if ( ! $post->post_name ) {
- wp_update_post(
- array(
- 'ID' => $post_id,
- 'post_name' => 'custom_slug_' . uniqid(),
- )
- );
- }
-
- $terms = get_the_terms( $post_id, 'wp_theme' );
- if ( ! $terms || ! count( $terms ) ) {
- wp_set_post_terms( $post_id, wp_get_theme()->get_stylesheet(), 'wp_theme' );
- }
- }
- add_action( 'save_post_wp_template', 'set_unique_slug_on_create_template' );
-
- /**
- * Print the skip-link script & styles.
- *
- * @todo Remove this when WP 5.8 is the minimum required version.
- *
- * @return void
- */
- function gutenberg_the_skip_link() {
- // Early exit if not a block theme.
- if ( ! current_theme_supports( 'block-templates' ) ) {
- return;
- }
-
- // Early exit if not a block template.
- global $_wp_current_template_content;
- if ( ! $_wp_current_template_content ) {
- return;
- }
- ?>
-
-
-
-
-
- get_stylesheet();
- $terms = get_the_terms( $post_ID, 'wp_theme' );
- if ( $terms && ! is_wp_error( $terms ) ) {
- $theme = $terms[0]->name;
- }
-
- $check_query_args = array(
- 'post_name__in' => array( $override_slug ),
- 'post_type' => $post_type,
- 'posts_per_page' => 1,
- 'no_found_rows' => true,
- 'post__not_in' => array( $post_ID ),
- 'tax_query' => array(
- array(
- 'taxonomy' => 'wp_theme',
- 'field' => 'name',
- 'terms' => $theme,
- ),
- ),
- );
- $check_query = new WP_Query( $check_query_args );
- $posts = $check_query->posts;
-
- if ( count( $posts ) > 0 ) {
- $suffix = 2;
- do {
- $query_args = $check_query_args;
- $alt_post_name = _truncate_post_slug( $override_slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
- $query_args['post_name__in'] = array( $alt_post_name );
- $query = new WP_Query( $query_args );
- $suffix++;
- } while ( count( $query->posts ) > 0 );
- $override_slug = $alt_post_name;
- }
-
- return $override_slug;
-}
-
-// Remove 5.8 filter if it exists.
-remove_filter( 'pre_wp_unique_post_slug', 'wp_filter_wp_template_unique_post_slug' );
-add_filter( 'pre_wp_unique_post_slug', 'gutenberg_filter_wp_template_unique_post_slug', 10, 5 );
-
-/**
- * Enable block templates (editor mode) for themes with theme.json.
- */
-function gutenberg_enable_block_templates() {
- if ( wp_is_block_theme() || WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
- add_theme_support( 'block-templates' );
- }
-}
-
-// Remove 5.8 filter if it exists.
-remove_action( 'setup_theme', 'wp_enable_block_templates' );
-add_action( 'setup_theme', 'gutenberg_enable_block_templates' );
diff --git a/lib/compat/wordpress-5.9/theme.php b/lib/compat/wordpress-5.9/theme.php
deleted file mode 100644
index 7bfe11f604c5b..0000000000000
--- a/lib/compat/wordpress-5.9/theme.php
+++ /dev/null
@@ -1,28 +0,0 @@
- $value ) {
- if ( isset( $i18n_schema->$key ) ) {
- $translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $value, $textdomain );
- } elseif ( isset( $i18n_schema->$group_key ) ) {
- $translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$group_key, $value, $textdomain );
- } else {
- $translated_settings[ $key ] = $value;
- }
- }
- return $translated_settings;
- }
- return $settings;
- }
-}
diff --git a/lib/compat/wordpress-5.9/widget-render-api-endpoint/class-gb-rest-widget-render-endpoint-polyfill.php b/lib/compat/wordpress-5.9/widget-render-api-endpoint/class-gb-rest-widget-render-endpoint-polyfill.php
deleted file mode 100644
index 6c8f2ee2a51e3..0000000000000
--- a/lib/compat/wordpress-5.9/widget-render-api-endpoint/class-gb-rest-widget-render-endpoint-polyfill.php
+++ /dev/null
@@ -1,121 +0,0 @@
-rest_base . '/(?P[a-zA-Z0-9_-]+)/render';
-
- // Don't override if already registered.
- $registered_routes = rest_get_server()->get_routes( 'wp/v2' );
- if ( array_key_exists( $route, $registered_routes ) ) {
- return;
- }
-
- register_rest_route(
- $this->namespace,
- $route,
- array(
- array(
- 'methods' => WP_REST_Server::CREATABLE,
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'callback' => array( $this, 'render' ),
- 'args' => array(
- 'id' => array(
- 'description' => __( 'The widget type id.', 'default' ),
- 'type' => 'string',
- 'required' => true,
- ),
- 'instance' => array(
- 'description' => __( 'Current instance settings of the widget.', 'default' ),
- 'type' => 'object',
- ),
- ),
- ),
- )
- );
- }
-
- /**
- * Renders a single Legacy Widget and wraps it in a JSON-encodable array.
- *
- * @param WP_REST_Request $request Full details about the request.
- *
- * @return array An array with rendered Legacy Widget HTML.
- */
- public function render( $request ) {
- return array(
- 'preview' => $this->render_legacy_widget_preview_iframe(
- $request['id'],
- isset( $request['instance'] ) ? $request['instance'] : null
- ),
- );
- }
-
- /**
- * Renders a page containing a preview of the requested Legacy Widget block.
- *
- * @param string $id_base The id base of the requested widget.
- * @param array $instance The widget instance attributes.
- *
- * @return string Rendered Legacy Widget block preview.
- */
- private function render_legacy_widget_preview_iframe( $id_base, $instance ) {
- if ( ! defined( 'IFRAME_REQUEST' ) ) {
- define( 'IFRAME_REQUEST', true );
- }
-
- ob_start();
- ?>
-
- >
-
-
-
-
-
-
-
- >
-
-
- get_registered( 'core/legacy-widget' );
- echo $block->render(
- array(
- 'idBase' => $id_base,
- 'instance' => $instance,
- )
- );
- ?>
-
-
-
-
-
- /render endpoint in WP versions where it's missing
- *
- * @package gutenberg
- */
-
-// Load the polyfill class.
-require_once __DIR__ . '/class-gb-rest-widget-render-endpoint-polyfill.php';
-
-/**
- * Registers routes from the GB_REST_Widget_Render_Endpoint_Polyfill class.
- */
-function setup_widget_render_api_endpoint_polyfill() {
- $polyfill = new GB_REST_Widget_Render_Endpoint_Polyfill();
- $polyfill->register_routes();
-}
-
-// Priority should be larger than 99 which is the one used for registering the core routes.
-add_action( 'rest_api_init', 'setup_widget_render_api_endpoint_polyfill', 100 );
-
-
diff --git a/lib/compat/wordpress-6.0/block-editor-settings.php b/lib/compat/wordpress-6.0/block-editor-settings.php
index 21f1f05bb45f1..8aae673bff334 100644
--- a/lib/compat/wordpress-6.0/block-editor-settings.php
+++ b/lib/compat/wordpress-6.0/block-editor-settings.php
@@ -5,20 +5,6 @@
* @package gutenberg
*/
-/**
- * Returns true if the style is coming from global styles.
- *
- * @param array $style Array containing a '__unstableType' key.
- * @return boolean
- */
-function gutenberg_is_global_styles_in_5_8( $style ) {
- if ( isset( $style['__unstableType'] ) && ( 'globalStyles' === $style['__unstableType'] ) ) {
- return true;
- }
-
- return false;
-}
-
/**
* Returns true if the style is coming from global styles.
*
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php
index 77d731b9d4544..878ac30c1b2c7 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php
@@ -14,7 +14,7 @@
*
* @access private
*/
-class WP_Theme_JSON_6_0 extends WP_Theme_JSON_5_9 {
+class WP_Theme_JSON_6_0 extends WP_Theme_JSON {
/**
* Metadata for style properties.
*
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
index cc6aa688493de..1de501b7b98c3 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
@@ -15,7 +15,7 @@
*
* @access private
*/
-class WP_Theme_JSON_Resolver_6_0 extends WP_Theme_JSON_Resolver_5_9 {
+class WP_Theme_JSON_Resolver_6_0 extends WP_Theme_JSON_Resolver {
/**
* Given a theme.json structure modifies it in place
* to update certain values by its translated strings
diff --git a/lib/load.php b/lib/load.php
index d16096e03dabc..61d7bedba5b86 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -31,25 +31,9 @@ function gutenberg_is_experiment_enabled( $name ) {
// These files only need to be loaded if within a rest server instance
// which this class will exist if that is the case.
if ( class_exists( 'WP_REST_Controller' ) ) {
- // WordPress 5.9 compat.
- require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-global-styles-controller.php';
- require_once __DIR__ . '/compat/wordpress-5.9/rest-active-global-styles.php';
- if ( ! class_exists( 'WP_REST_Menus_Controller' ) ) {
- require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-menus-controller.php';
- }
- if ( ! class_exists( 'WP_REST_Menu_Items_Controller' ) ) {
- require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-menu-items-controller.php';
- }
- if ( ! class_exists( 'WP_REST_Menu_Locations_Controller' ) ) {
- require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-menu-locations-controller.php';
- }
if ( ! class_exists( 'WP_REST_Block_Editor_Settings_Controller' ) ) {
require_once __DIR__ . '/experimental/class-wp-rest-block-editor-settings-controller.php';
}
- if ( ! class_exists( 'WP_REST_URL_Details_Controller' ) ) {
- require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-url-details-controller.php';
- }
- require_once __DIR__ . '/compat/wordpress-5.9/rest-api.php';
// WordPress 6.0 compat.
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php';
@@ -74,36 +58,6 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/editor-settings.php';
-// WordPress 5.9 compat.
-require __DIR__ . '/compat/wordpress-5.9/polyfills.php';
-require __DIR__ . '/compat/wordpress-5.9/block-gallery.php';
-require __DIR__ . '/compat/wordpress-5.9/widget-render-api-endpoint/index.php';
-require __DIR__ . '/compat/wordpress-5.9/blocks.php';
-require __DIR__ . '/compat/wordpress-5.9/block-editor-settings.php';
-require __DIR__ . '/compat/wordpress-5.9/block-patterns.php';
-require __DIR__ . '/compat/wordpress-5.9/block-template-utils.php';
-require __DIR__ . '/compat/wordpress-5.9/default-editor-styles.php';
-require __DIR__ . '/compat/wordpress-5.9/register-global-styles-cpt.php';
-require __DIR__ . '/compat/wordpress-5.9/script-loader.php';
-require __DIR__ . '/compat/wordpress-5.9/json-file-decode.php';
-require __DIR__ . '/compat/wordpress-5.9/translate-settings-using-i18n-schema.php';
-require __DIR__ . '/compat/wordpress-5.9/global-styles-css-custom-properties.php';
-require __DIR__ . '/compat/wordpress-5.9/class-gutenberg-block-template.php';
-require __DIR__ . '/compat/wordpress-5.9/templates.php';
-require __DIR__ . '/compat/wordpress-5.9/template-parts.php';
-require __DIR__ . '/compat/wordpress-5.9/theme-templates.php';
-require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php';
-require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-5-9.php';
-require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php';
-require __DIR__ . '/compat/wordpress-5.9/theme.php';
-require __DIR__ . '/compat/wordpress-5.9/admin-menu.php';
-require __DIR__ . '/compat/wordpress-5.9/edit-site-page.php';
-require __DIR__ . '/compat/wordpress-5.9/block-template.php';
-require __DIR__ . '/compat/wordpress-5.9/default-theme-supports.php';
-require __DIR__ . '/compat/wordpress-5.9/move-theme-editor-menu-item.php';
-require __DIR__ . '/compat/wordpress-5.9/navigation.php';
-require __DIR__ . '/compat/wordpress-5.9/kses.php';
-
// WordPress 6.0 compat.
require __DIR__ . '/compat/wordpress-6.0/block-editor-settings.php';
require __DIR__ . '/compat/wordpress-6.0/get-global-styles-and-settings.php';
diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index e71b87d2ab791..1ac1034cb9b60 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
-import { map, some, omit } from 'lodash';
+import { some, omit } from 'lodash';
/**
* WordPress dependencies
@@ -19,7 +19,6 @@ import {
LINK_DESTINATION_MEDIA,
LINK_DESTINATION_NONE,
} from './constants';
-import { isGalleryV2Enabled } from './shared';
const DEPRECATED_LINK_DESTINATION_MEDIA = 'file';
const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post';
@@ -282,11 +281,7 @@ const v6 = {
);
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
-
- return attributes;
+ return runV2Migration( attributes );
},
};
const v5 = {
@@ -372,23 +367,7 @@ const v5 = {
return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
-
- let linkTo = attributes.linkTo;
-
- if ( ! attributes.linkTo ) {
- linkTo = 'none';
- } else if ( attributes.linkTo === 'attachment' ) {
- linkTo = 'post';
- } else if ( attributes.linkTo === 'media' ) {
- linkTo = 'file';
- }
- return {
- ...attributes,
- linkTo,
- };
+ return runV2Migration( attributes );
},
save( { attributes } ) {
const {
@@ -535,17 +514,7 @@ const v4 = {
return ids && ids.some( ( id ) => typeof id === 'string' );
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
-
- return {
- ...attributes,
- ids: map( attributes.ids, ( id ) => {
- const parsedId = parseInt( id, 10 );
- return Number.isInteger( parsedId ) ? parsedId : null;
- } ),
- };
+ return runV2Migration( attributes );
},
save( { attributes } ) {
const {
@@ -741,10 +710,7 @@ const v3 = {
);
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
- return attributes;
+ return runV2Migration( attributes );
},
};
const v2 = {
@@ -810,18 +776,7 @@ const v2 = {
);
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
- return {
- ...attributes,
- ids: map( attributes.images, ( { id } ) => {
- if ( ! id ) {
- return null;
- }
- return parseInt( id, 10 );
- } ),
- };
+ return runV2Migration( attributes );
},
supports: {
align: true,
@@ -974,11 +929,7 @@ const v1 = {
);
},
migrate( attributes ) {
- if ( isGalleryV2Enabled() ) {
- return runV2Migration( attributes );
- }
-
- return attributes;
+ return runV2Migration( attributes );
},
};
diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
deleted file mode 100644
index 2c81271902d31..0000000000000
--- a/packages/block-library/src/gallery/edit-wrapper.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { compose } from '@wordpress/compose';
-import { withNotices } from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import EditWithInnerBlocks from './edit';
-import EditWithoutInnerBlocks from './v1/edit';
-import { isGalleryV2Enabled } from './shared';
-
-/*
- * Using a wrapper around the logic to load the edit for v1 of Gallery block
- * or the refactored version with InnerBlocks. This is to prevent conditional
- * use of hooks lint errors if adding this logic to the top of the edit component.
- */
-function GalleryEditWrapper( props ) {
- if ( ! isGalleryV2Enabled() ) {
- return ;
- }
-
- return ;
-}
-
-export default compose( [ withNotices ] )( GalleryEditWrapper );
diff --git a/packages/block-library/src/gallery/gallery-styles.native.scss b/packages/block-library/src/gallery/gallery-styles.native.scss
index a3073592291b9..f9b4d63c55b81 100644
--- a/packages/block-library/src/gallery/gallery-styles.native.scss
+++ b/packages/block-library/src/gallery/gallery-styles.native.scss
@@ -1,5 +1,3 @@
-@import "./v1/gallery-styles.native.scss";
-
.galleryAppender {
padding-top: $grid-unit-20;
}
diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js
index c9b82278a507a..ca74aec021b28 100644
--- a/packages/block-library/src/gallery/index.js
+++ b/packages/block-library/src/gallery/index.js
@@ -7,7 +7,7 @@ import { gallery as icon } from '@wordpress/icons';
* Internal dependencies
*/
import deprecated from './deprecated';
-import edit from './edit-wrapper';
+import edit from './edit';
import metadata from './block.json';
import save from './save';
import transforms from './transforms';
diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index 66253001e6418..acb689e4427ba 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -13,17 +13,7 @@ import {
__experimentalGetElementClassName,
} from '@wordpress/block-editor';
-/**
- * Internal dependencies
- */
-import saveWithoutInnerBlocks from './v1/save';
-import { isGalleryV2Enabled } from './shared';
-
export default function saveWithInnerBlocks( { attributes } ) {
- if ( ! isGalleryV2Enabled() ) {
- return saveWithoutInnerBlocks( { attributes } );
- }
-
const { caption, columns, imageCrop } = attributes;
const className = classnames( 'has-nested-images', {
diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index 53a08bd590e71..74d5e1149f908 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -18,11 +18,6 @@ import {
LINK_DESTINATION_NONE,
LINK_DESTINATION_MEDIA,
} from './constants';
-import {
- LINK_DESTINATION_ATTACHMENT as DEPRECATED_LINK_DESTINATION_ATTACHMENT,
- LINK_DESTINATION_MEDIA as DEPRECATED_LINK_DESTINATION_MEDIA,
-} from './v1/constants';
-import { pickRelevantMediaFiles, isGalleryV2Enabled } from './shared';
const parseShortcodeIds = ( ids ) => {
if ( ! ids ) {
@@ -48,7 +43,6 @@ const parseShortcodeIds = ( ids ) => {
*/
function updateThirdPartyTransformToGallery( block ) {
if (
- isGalleryV2Enabled() &&
block.name === 'core/gallery' &&
block.attributes?.images.length > 0
) {
@@ -142,34 +136,18 @@ const transforms = {
const validImages = filter( attributes, ( { url } ) => url );
- if ( isGalleryV2Enabled() ) {
- const innerBlocks = validImages.map( ( image ) => {
- return createBlock( 'core/image', image );
- } );
-
- return createBlock(
- 'core/gallery',
- {
- align,
- sizeSlug,
- },
- innerBlocks
- );
- }
-
- return createBlock( 'core/gallery', {
- images: validImages.map(
- ( { id, url, alt, caption } ) => ( {
- id: id.toString(),
- url,
- alt,
- caption,
- } )
- ),
- ids: validImages.map( ( { id } ) => parseInt( id, 10 ) ),
- align,
- sizeSlug,
+ const innerBlocks = validImages.map( ( image ) => {
+ return createBlock( 'core/image', image );
} );
+
+ return createBlock(
+ 'core/gallery',
+ {
+ align,
+ sizeSlug,
+ },
+ innerBlocks
+ );
},
},
{
@@ -177,32 +155,12 @@ const transforms = {
tag: 'gallery',
attributes: {
- images: {
- type: 'array',
- shortcode: ( { named: { ids } } ) => {
- if ( ! isGalleryV2Enabled() ) {
- return parseShortcodeIds( ids ).map( ( id ) => ( {
- id: id.toString(),
- } ) );
- }
- },
- },
- ids: {
- type: 'array',
- shortcode: ( { named: { ids } } ) => {
- if ( ! isGalleryV2Enabled() ) {
- return parseShortcodeIds( ids );
- }
- },
- },
shortCodeTransforms: {
type: 'array',
shortcode: ( { named: { ids } } ) => {
- if ( isGalleryV2Enabled() ) {
- return parseShortcodeIds( ids ).map( ( id ) => ( {
- id: parseInt( id ),
- } ) );
- }
+ return parseShortcodeIds( ids ).map( ( id ) => ( {
+ id: parseInt( id ),
+ } ) );
},
},
columns: {
@@ -214,16 +172,6 @@ const transforms = {
linkTo: {
type: 'string',
shortcode: ( { named: { link } } ) => {
- if ( ! isGalleryV2Enabled() ) {
- switch ( link ) {
- case 'post':
- return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
- case 'file':
- return DEPRECATED_LINK_DESTINATION_MEDIA;
- default:
- return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
- }
- }
switch ( link ) {
case 'post':
return LINK_DESTINATION_ATTACHMENT;
@@ -257,23 +205,13 @@ const transforms = {
);
},
transform( files ) {
- if ( isGalleryV2Enabled() ) {
- const innerBlocks = files.map( ( file ) =>
- createBlock( 'core/image', {
- url: createBlobURL( file ),
- } )
- );
+ const innerBlocks = files.map( ( file ) =>
+ createBlock( 'core/image', {
+ url: createBlobURL( file ),
+ } )
+ );
- return createBlock( 'core/gallery', {}, innerBlocks );
- }
- const block = createBlock( 'core/gallery', {
- images: files.map( ( file ) =>
- pickRelevantMediaFiles( {
- url: createBlobURL( file ),
- } )
- ),
- } );
- return block;
+ return createBlock( 'core/gallery', {}, innerBlocks );
},
},
],
@@ -281,47 +219,32 @@ const transforms = {
{
type: 'block',
blocks: [ 'core/image' ],
- transform: ( { align, images, ids, sizeSlug }, innerBlocks ) => {
- if ( isGalleryV2Enabled() ) {
- if ( innerBlocks.length > 0 ) {
- return innerBlocks.map(
- ( {
- attributes: {
- id,
- url,
- alt,
- caption,
- sizeSlug: imageSizeSlug,
- linkDestination,
- href,
- linkTarget,
- },
- } ) =>
- createBlock( 'core/image', {
- id,
- url,
- alt,
- caption,
- sizeSlug: imageSizeSlug,
- align,
- linkDestination,
- href,
- linkTarget,
- } )
- );
- }
- return createBlock( 'core/image', { align } );
- }
- if ( images.length > 0 ) {
- return images.map( ( { url, alt, caption }, index ) =>
- createBlock( 'core/image', {
- id: ids[ index ],
- url,
- alt,
- caption,
- align,
- sizeSlug,
- } )
+ transform: ( { align }, innerBlocks ) => {
+ if ( innerBlocks.length > 0 ) {
+ return innerBlocks.map(
+ ( {
+ attributes: {
+ id,
+ url,
+ alt,
+ caption,
+ sizeSlug: imageSizeSlug,
+ linkDestination,
+ href,
+ linkTarget,
+ },
+ } ) =>
+ createBlock( 'core/image', {
+ id,
+ url,
+ alt,
+ caption,
+ sizeSlug: imageSizeSlug,
+ align,
+ linkDestination,
+ href,
+ linkTarget,
+ } )
);
}
return createBlock( 'core/image', { align } );
diff --git a/packages/block-library/src/gallery/v1/constants.js b/packages/block-library/src/gallery/v1/constants.js
deleted file mode 100644
index f4b6e7af56d47..0000000000000
--- a/packages/block-library/src/gallery/v1/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const LINK_DESTINATION_NONE = 'none';
-export const LINK_DESTINATION_MEDIA = 'file';
-export const LINK_DESTINATION_ATTACHMENT = 'post';
diff --git a/packages/block-library/src/gallery/v1/gallery-button.native.js b/packages/block-library/src/gallery/v1/gallery-button.native.js
deleted file mode 100644
index 8804e99cf2e7e..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery-button.native.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * External dependencies
- */
-import { StyleSheet, TouchableOpacity } from 'react-native';
-
-/**
- * WordPress dependencies
- */
-import { Icon } from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import style from './gallery-image-style.scss';
-
-export function Button( props ) {
- const {
- icon,
- iconSize = 24,
- onClick,
- disabled,
- 'aria-disabled': ariaDisabled,
- accessibilityLabel = 'button',
- style: customStyle,
- } = props;
-
- const buttonStyle = StyleSheet.compose( style.buttonActive, customStyle );
-
- const isDisabled = disabled || ariaDisabled;
-
- const { fill } = isDisabled ? style.buttonDisabled : style.button;
-
- return (
-
-
-
- );
-}
-
-export default Button;
diff --git a/packages/block-library/src/gallery/v1/gallery-image-style.native.scss b/packages/block-library/src/gallery/v1/gallery-image-style.native.scss
deleted file mode 100644
index 9b221a56981f6..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery-image-style.native.scss
+++ /dev/null
@@ -1,109 +0,0 @@
-$gallery-image-container-height: 150px;
-$overlay-border-width: 2px;
-$caption-background-color: rgba(0, 0, 0, 0.4);
-
-.galleryImageContainer {
- flex: 1;
- height: $gallery-image-container-height;
- overflow: hidden;
- background-color: $gray-lighten-30;
-}
-
-.galleryImageContainerDark {
- background-color: $gray-90;
-}
-
-.image {
- height: 100%;
-}
-
-.button {
- fill: $gray-0;
- width: 30px;
-}
-
-.buttonDisabled {
- fill: $gray-30;
-}
-
-.buttonActive {
- flex-direction: row;
- justify-content: center;
- align-items: center;
- border-radius: 6px;
- border-color: $gray-70;
- background-color: $gray-70;
-}
-
-.moverButtonContainer {
- flex-direction: row;
- align-items: center;
- border-radius: 3px;
- background-color: $gray-70;
-}
-
-.separator {
- border-right-color: $gray-30;
- border-right-width: 1px;
- height: 20px;
-}
-
-.toolbarContainer {
- position: absolute;
-}
-
-.toolbar {
- padding: 5px;
- flex-direction: row;
- justify-content: space-between;
-}
-
-.captionContainer {
- flex: 1;
- flex-direction: row;
- align-items: flex-end;
- position: absolute;
- bottom: $overlay-border-width;
- left: $overlay-border-width;
- right: $overlay-border-width;
- top: 45;
-}
-
-@mixin caption-shared {
- font-size: 12px;
- background-color: #0000;
- color: #fff;
- font-family: $default-regular-font;
- text-align: center;
-}
-
-.caption {
- @include caption-shared;
- background-color: $caption-background-color;
- padding-top: $grid-unit;
- padding-bottom: $grid-unit;
-}
-
-.captionPlaceholder {
- color: #ccc;
-}
-
-// expand caption container to compensate for overlay border
-.captionExpandedContainer {
- // constrain height to gallery image height for caption scroll
- max-height: $gallery-image-container-height;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- padding-top: $grid-unit;
- padding-bottom: $overlay-border-width + $grid-unit;
- padding-left: $overlay-border-width;
- padding-right: $overlay-border-width;
- // use caption background color on container when expanded
- background-color: $caption-background-color;
-}
-
-.captionExpanded {
- @include caption-shared;
-}
diff --git a/packages/block-library/src/gallery/v1/gallery-image.js b/packages/block-library/src/gallery/v1/gallery-image.js
deleted file mode 100644
index 9520a5ba76349..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery-image.js
+++ /dev/null
@@ -1,282 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-import { get, omit } from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import { Component } from '@wordpress/element';
-import { Button, Spinner, ButtonGroup } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
-import { BACKSPACE, DELETE } from '@wordpress/keycodes';
-import { withSelect, withDispatch } from '@wordpress/data';
-import {
- RichText,
- MediaPlaceholder,
- store as blockEditorStore,
- __experimentalGetElementClassName,
-} from '@wordpress/block-editor';
-import { isBlobURL } from '@wordpress/blob';
-import { compose } from '@wordpress/compose';
-import {
- closeSmall,
- chevronLeft,
- chevronRight,
- edit,
- image as imageIcon,
-} from '@wordpress/icons';
-import { store as coreStore } from '@wordpress/core-data';
-
-/**
- * Internal dependencies
- */
-import { pickRelevantMediaFiles } from './shared';
-import {
- LINK_DESTINATION_ATTACHMENT,
- LINK_DESTINATION_MEDIA,
-} from './constants';
-
-const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url );
-
-class GalleryImage extends Component {
- constructor() {
- super( ...arguments );
-
- this.onSelectImage = this.onSelectImage.bind( this );
- this.onRemoveImage = this.onRemoveImage.bind( this );
- this.bindContainer = this.bindContainer.bind( this );
- this.onEdit = this.onEdit.bind( this );
- this.onSelectImageFromLibrary =
- this.onSelectImageFromLibrary.bind( this );
- this.onSelectCustomURL = this.onSelectCustomURL.bind( this );
- this.state = {
- isEditing: false,
- };
- }
-
- bindContainer( ref ) {
- this.container = ref;
- }
-
- onSelectImage() {
- if ( ! this.props.isSelected ) {
- this.props.onSelect();
- }
- }
-
- onRemoveImage( event ) {
- if (
- this.container === this.container.ownerDocument.activeElement &&
- this.props.isSelected &&
- [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1
- ) {
- event.preventDefault();
- this.props.onRemove();
- }
- }
-
- onEdit() {
- this.setState( {
- isEditing: true,
- } );
- }
-
- componentDidUpdate() {
- const { image, url, __unstableMarkNextChangeAsNotPersistent } =
- this.props;
- if ( image && ! url ) {
- __unstableMarkNextChangeAsNotPersistent();
- this.props.setAttributes( {
- url: image.source_url,
- alt: image.alt_text,
- } );
- }
- }
-
- deselectOnBlur() {
- this.props.onDeselect();
- }
-
- onSelectImageFromLibrary( media ) {
- const { setAttributes, id, url, alt, caption, sizeSlug } = this.props;
- if ( ! media || ! media.url ) {
- return;
- }
-
- let mediaAttributes = pickRelevantMediaFiles( media, sizeSlug );
-
- // If the current image is temporary but an alt text was meanwhile
- // written by the user, make sure the text is not overwritten.
- if ( isTemporaryImage( id, url ) ) {
- if ( alt ) {
- mediaAttributes = omit( mediaAttributes, [ 'alt' ] );
- }
- }
-
- // If a caption text was meanwhile written by the user,
- // make sure the text is not overwritten by empty captions.
- if ( caption && ! get( mediaAttributes, [ 'caption' ] ) ) {
- mediaAttributes = omit( mediaAttributes, [ 'caption' ] );
- }
-
- setAttributes( mediaAttributes );
- this.setState( {
- isEditing: false,
- } );
- }
-
- onSelectCustomURL( newURL ) {
- const { setAttributes, url } = this.props;
- if ( newURL !== url ) {
- setAttributes( {
- url: newURL,
- id: undefined,
- } );
- this.setState( {
- isEditing: false,
- } );
- }
- }
-
- render() {
- const {
- url,
- alt,
- id,
- linkTo,
- link,
- isFirstItem,
- isLastItem,
- isSelected,
- caption,
- onRemove,
- onMoveForward,
- onMoveBackward,
- setAttributes,
- 'aria-label': ariaLabel,
- } = this.props;
- const { isEditing } = this.state;
-
- let href;
-
- switch ( linkTo ) {
- case LINK_DESTINATION_MEDIA:
- href = url;
- break;
- case LINK_DESTINATION_ATTACHMENT:
- href = link;
- break;
- }
-
- const img = (
- // Disable reason: Image itself is not meant to be interactive, but should
- // direct image selection and unfocus caption fields.
- /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
- <>
-
- { isBlobURL( url ) && }
- >
- /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
- );
-
- const className = classnames( {
- 'is-selected': isSelected,
- 'is-transient': isBlobURL( url ),
- } );
-
- return (
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
-
- { ! isEditing && ( href ? { img } : img ) }
- { isEditing && (
-
- ) }
-
-
-
-
-
-
-
-
- { ! isEditing && ( isSelected || caption ) && (
-
- setAttributes( { caption: newCaption } )
- }
- inlineToolbar
- />
- ) }
-
- );
- }
-}
-
-export default compose( [
- withSelect( ( select, ownProps ) => {
- const { getMedia } = select( coreStore );
- const { id } = ownProps;
-
- return {
- image: id ? getMedia( parseInt( id, 10 ) ) : null,
- };
- } ),
- withDispatch( ( dispatch ) => {
- const { __unstableMarkNextChangeAsNotPersistent } =
- dispatch( blockEditorStore );
- return {
- __unstableMarkNextChangeAsNotPersistent,
- };
- } ),
-] )( GalleryImage );
diff --git a/packages/block-library/src/gallery/v1/gallery-styles.native.scss b/packages/block-library/src/gallery/v1/gallery-styles.native.scss
deleted file mode 100644
index 9b3169da048b2..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery-styles.native.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.galleryTilesContainerSelected {
- margin-bottom: 16px;
-}
-
-.fullWidth {
- margin-left: $block-edge-to-content;
- margin-right: $block-edge-to-content;
-}
diff --git a/packages/block-library/src/gallery/v1/gallery.js b/packages/block-library/src/gallery/v1/gallery.js
deleted file mode 100644
index 7fc587f27911a..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import {
- RichText,
- __experimentalGetElementClassName,
-} from '@wordpress/block-editor';
-import { VisuallyHidden } from '@wordpress/components';
-import { __, sprintf } from '@wordpress/i18n';
-import { createBlock, getDefaultBlockName } from '@wordpress/blocks';
-
-/**
- * Internal dependencies
- */
-import GalleryImage from './gallery-image';
-import { defaultColumnsNumberV1 } from '../deprecated';
-
-export const Gallery = ( props ) => {
- const {
- attributes,
- isSelected,
- setAttributes,
- selectedImage,
- mediaPlaceholder,
- onMoveBackward,
- onMoveForward,
- onRemoveImage,
- onSelectImage,
- onDeselectImage,
- onSetImageAttributes,
- insertBlocksAfter,
- blockProps,
- } = props;
-
- const {
- align,
- columns = defaultColumnsNumberV1( attributes ),
- caption,
- imageCrop,
- images,
- } = attributes;
-
- return (
-
-
- { images.map( ( img, index ) => {
- const ariaLabel = sprintf(
- /* translators: 1: the order number of the image. 2: the total number of images. */
- __( 'image %1$d of %2$d in gallery' ),
- index + 1,
- images.length
- );
-
- return (
-
-
- onSetImageAttributes( index, attrs )
- }
- caption={ img.caption }
- aria-label={ ariaLabel }
- sizeSlug={ attributes.sizeSlug }
- />
-
- );
- } ) }
-
- { mediaPlaceholder }
- setAttributes( { caption: value } ) }
- inlineToolbar
- __unstableOnSplitAtEnd={ () =>
- insertBlocksAfter( createBlock( getDefaultBlockName() ) )
- }
- />
-
- );
-};
-
-function RichTextVisibilityHelper( { isHidden, ...richTextProps } ) {
- return isHidden ? (
-
- ) : (
-
- );
-}
-
-export default Gallery;
diff --git a/packages/block-library/src/gallery/v1/gallery.native.js b/packages/block-library/src/gallery/v1/gallery.native.js
deleted file mode 100644
index 7908d17988a1a..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery.native.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * External dependencies
- */
-import { View } from 'react-native';
-import { isEmpty } from 'lodash';
-
-/**
- * Internal dependencies
- */
-import GalleryImage from './gallery-image';
-import { defaultColumnsNumberV1 } from '../deprecated';
-import styles from './gallery-styles.scss';
-import Tiles from './tiles';
-
-/**
- * WordPress dependencies
- */
-import { __, sprintf } from '@wordpress/i18n';
-import {
- BlockCaption,
- store as blockEditorStore,
-} from '@wordpress/block-editor';
-import { useState, useEffect } from '@wordpress/element';
-import { mediaUploadSync } from '@wordpress/react-native-bridge';
-import { useSelect } from '@wordpress/data';
-import { alignmentHelpers } from '@wordpress/components';
-
-const TILE_SPACING = 15;
-
-// we must limit displayed columns since readable content max-width is 580px
-const MAX_DISPLAYED_COLUMNS = 4;
-const MAX_DISPLAYED_COLUMNS_NARROW = 2;
-
-const { isFullWidth } = alignmentHelpers;
-
-export const Gallery = ( props ) => {
- const [ isCaptionSelected, setIsCaptionSelected ] = useState( false );
- useEffect( mediaUploadSync, [] );
-
- const isRTL = useSelect( ( select ) => {
- return !! select( blockEditorStore ).getSettings().isRTL;
- }, [] );
-
- const {
- clientId,
- selectedImage,
- mediaPlaceholder,
- onBlur,
- onMoveBackward,
- onMoveForward,
- onRemoveImage,
- onSelectImage,
- onSetImageAttributes,
- onFocusGalleryCaption,
- attributes,
- isSelected,
- isNarrow,
- onFocus,
- insertBlocksAfter,
- } = props;
-
- const {
- align,
- columns = defaultColumnsNumberV1( attributes ),
- imageCrop,
- images,
- } = attributes;
-
- // limit displayed columns when isNarrow is true (i.e. when viewport width is
- // less than "small", where small = 600)
- const displayedColumns = isNarrow
- ? Math.min( columns, MAX_DISPLAYED_COLUMNS_NARROW )
- : Math.min( columns, MAX_DISPLAYED_COLUMNS );
-
- const selectImage = ( index ) => {
- return () => {
- if ( isCaptionSelected ) {
- setIsCaptionSelected( false );
- }
- // We need to fully invoke the curried function here.
- onSelectImage( index )();
- };
- };
-
- const focusGalleryCaption = () => {
- if ( ! isCaptionSelected ) {
- setIsCaptionSelected( true );
- }
- onFocusGalleryCaption();
- };
-
- return (
-
-
- { images.map( ( img, index ) => {
- const ariaLabel = sprintf(
- /* translators: 1: the order number of the image. 2: the total number of images. */
- __( 'image %1$d of %2$d in gallery' ),
- index + 1,
- images.length
- );
-
- return (
-
- onSetImageAttributes( index, attrs )
- }
- caption={ img.caption }
- aria-label={ ariaLabel }
- isRTL={ isRTL }
- />
- );
- } ) }
-
-
- { mediaPlaceholder }
-
-
- isEmpty( caption )
- ? /* translators: accessibility text. Empty gallery caption. */
- 'Gallery caption. Empty'
- : sprintf(
- /* translators: accessibility text. %s: gallery caption. */
- __( 'Gallery caption. %s' ),
- caption
- )
- }
- onFocus={ focusGalleryCaption }
- onBlur={ onBlur } // Always assign onBlur as props.
- insertBlocksAfter={ insertBlocksAfter }
- />
-
- );
-};
-
-export default Gallery;
diff --git a/packages/block-library/src/gallery/v1/save.js b/packages/block-library/src/gallery/v1/save.js
deleted file mode 100644
index bf8be9ba413b4..0000000000000
--- a/packages/block-library/src/gallery/v1/save.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import {
- RichText,
- useBlockProps,
- __experimentalGetElementClassName,
-} from '@wordpress/block-editor';
-
-/**
- * Internal dependencies
- */
-import { defaultColumnsNumberV1 } from '../deprecated';
-import {
- LINK_DESTINATION_ATTACHMENT,
- LINK_DESTINATION_MEDIA,
-} from './constants';
-
-export default function saveV1( { attributes } ) {
- const {
- images,
- columns = defaultColumnsNumberV1( attributes ),
- imageCrop,
- caption,
- linkTo,
- } = attributes;
- const className = `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }`;
-
- return (
-
-
- { images.map( ( image ) => {
- let href;
-
- switch ( linkTo ) {
- case LINK_DESTINATION_MEDIA:
- href = image.fullUrl || image.url;
- break;
- case LINK_DESTINATION_ATTACHMENT:
- href = image.link;
- break;
- }
-
- const img = (
-
- );
-
- return (
-
-
- { href ? { img } : img }
- { ! RichText.isEmpty( image.caption ) && (
-
- ) }
-
-
- );
- } ) }
-
- { ! RichText.isEmpty( caption ) && (
-
- ) }
-
- );
-}
diff --git a/packages/block-library/src/gallery/v1/shared.js b/packages/block-library/src/gallery/v1/shared.js
deleted file mode 100644
index 484020cb9d58c..0000000000000
--- a/packages/block-library/src/gallery/v1/shared.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * External dependencies
- */
-import { get, pick } from 'lodash';
-
-export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
- const imageProps = pick( image, [ 'alt', 'id', 'link', 'caption' ] );
- imageProps.url =
- get( image, [ 'sizes', sizeSlug, 'url' ] ) ||
- get( image, [ 'media_details', 'sizes', sizeSlug, 'source_url' ] ) ||
- image.url;
- const fullUrl =
- get( image, [ 'sizes', 'full', 'url' ] ) ||
- get( image, [ 'media_details', 'sizes', 'full', 'source_url' ] );
- if ( fullUrl ) {
- imageProps.fullUrl = fullUrl;
- }
- return imageProps;
-};
diff --git a/packages/block-library/src/gallery/v1/tiles-styles.native.scss b/packages/block-library/src/gallery/v1/tiles-styles.native.scss
deleted file mode 100644
index c2e3d4bad7979..0000000000000
--- a/packages/block-library/src/gallery/v1/tiles-styles.native.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.containerStyle {
- flex-direction: row;
- flex-wrap: wrap;
-}
-
-.tileStyle {
- overflow: hidden;
- flex-direction: row;
- align-items: center;
- border-color: transparent;
-}
diff --git a/packages/block-library/src/gallery/v1/tiles.native.js b/packages/block-library/src/gallery/v1/tiles.native.js
deleted file mode 100644
index 30126ce872593..0000000000000
--- a/packages/block-library/src/gallery/v1/tiles.native.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * External dependencies
- */
-import { View, StyleSheet } from 'react-native';
-
-/**
- * WordPress dependencies
- */
-import { Children } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import styles from './tiles-styles.scss';
-
-function Tiles( props ) {
- const { columns, children, spacing = 10, style } = props;
-
- const { compose } = StyleSheet;
-
- const tileCount = Children.count( children );
- const lastTile = tileCount - 1;
- const lastRow = Math.floor( lastTile / columns );
-
- const wrappedChildren = Children.map( children, ( child, index ) => {
- /**
- * Since we don't have `calc()`, we must calculate our spacings here in
- * order to preserve even spacing between tiles and equal width for tiles
- * in a given row.
- *
- * In order to ensure equal sizing of tile contents, we distribute the
- * spacing such that each tile has an equal "share" of the fixed spacing. To
- * keep the tiles properly aligned within their rows, we calculate the left
- * and right paddings based on the tile's relative position within the row.
- *
- * Note: we use padding instead of margins so that the fixed spacing is
- * included within the relative spacing (i.e. width percentage), and
- * wrapping behavior is preserved.
- *
- * - The left most tile in a row must have left padding of zero.
- * - The right most tile in a row must have a right padding of zero.
- *
- * The values of these left and right paddings are interpolated for tiles in
- * between. The right padding is complementary with the left padding of the
- * next tile (i.e. the right padding of [tile n] + the left padding of
- * [tile n + 1] will be equal for all tiles except the last one in a given
- * row).
- */
-
- const row = Math.floor( index / columns );
- const rowLength =
- row === lastRow ? ( lastTile % columns ) + 1 : columns;
- const indexInRow = index % columns;
-
- return (
-
- { child }
-
- );
- } );
-
- const containerStyle = compose( styles.containerStyle, style );
-
- return { wrappedChildren } ;
-}
-
-export default Tiles;
From 7121691c209ca4c99ec6533ab02e6597e6e77ce0 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Wed, 25 May 2022 10:24:53 +0100
Subject: [PATCH 02/13] Use the code version of wp_enqueue_block_support_styles
---
lib/block-supports/layout.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index e239ed023bd62..b1ba529baae75 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -219,7 +219,7 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
1
);
- gutenberg_enqueue_block_support_styles( $style );
+ wp_enqueue_block_support_styles( $style );
return $content;
}
From df5e9f3bedb9b1d11654376dc4c33989afe54864 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Wed, 25 May 2022 10:39:38 +0100
Subject: [PATCH 03/13] More tweaks
---
.../class-wp-theme-json-resolver-6-0.php | 152 +++++
...ss-wp-theme-json-schema-gutenberg-test.php | 522 ------------------
2 files changed, 152 insertions(+), 522 deletions(-)
delete mode 100644 phpunit/class-wp-theme-json-schema-gutenberg-test.php
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
index 1de501b7b98c3..13e00171ce082 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
@@ -16,6 +16,9 @@
* @access private
*/
class WP_Theme_JSON_Resolver_6_0 extends WP_Theme_JSON_Resolver {
+
+
+
/**
* Given a theme.json structure modifies it in place
* to update certain values by its translated strings
@@ -159,4 +162,153 @@ public static function get_style_variations() {
}
return $variations;
}
+
+ /**
+ * Returns the custom post type that contains the user's origin config
+ * for the current theme or a void array if none are found.
+ *
+ * This can also create and return a new draft custom post type.
+ *
+ * @param WP_Theme $theme The theme object. If empty, it
+ * defaults to the current theme.
+ * @param bool $create_post Optional. Whether a new custom post
+ * type should be created if none are
+ * found. False by default.
+ * @param array $post_status_filter Filter Optional. custom post type by
+ * post status. ['publish'] by default,
+ * so it only fetches published posts.
+ * @return array Custom Post Type for the user's origin config.
+ */
+ public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) {
+ if ( ! $theme instanceof WP_Theme ) {
+ $theme = wp_get_theme();
+ }
+ $user_cpt = array();
+ $post_type_filter = 'wp_global_styles';
+ $args = array(
+ 'numberposts' => 1,
+ 'orderby' => 'date',
+ 'order' => 'desc',
+ 'post_type' => $post_type_filter,
+ 'post_status' => $post_status_filter,
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => 'wp_theme',
+ 'field' => 'name',
+ 'terms' => $theme->get_stylesheet(),
+ ),
+ ),
+ );
+
+ $cache_key = sprintf( 'wp_global_styles_%s', md5( serialize( $args ) ) );
+ $post_id = wp_cache_get( $cache_key );
+
+ if ( (int) $post_id > 0 ) {
+ return get_post( $post_id, ARRAY_A );
+ }
+
+ // Special case: '-1' is a results not found.
+ if ( -1 === $post_id && ! $create_post ) {
+ return $user_cpt;
+ }
+
+ $recent_posts = wp_get_recent_posts( $args );
+ if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) {
+ $user_cpt = $recent_posts[0];
+ } elseif ( $create_post ) {
+ $cpt_post_id = wp_insert_post(
+ array(
+ 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
+ 'post_status' => 'publish',
+ 'post_title' => __( 'Custom Styles', 'default' ),
+ 'post_type' => $post_type_filter,
+ 'post_name' => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ),
+ 'tax_input' => array(
+ 'wp_theme' => array( wp_get_theme()->get_stylesheet() ),
+ ),
+ ),
+ true
+ );
+ $user_cpt = get_post( $cpt_post_id, ARRAY_A );
+ }
+ $cache_expiration = $user_cpt ? DAY_IN_SECONDS : HOUR_IN_SECONDS;
+ wp_cache_set( $cache_key, $user_cpt ? $user_cpt['ID'] : -1, '', $cache_expiration );
+
+ return $user_cpt;
+ }
+
+ /**
+ * Returns the user's origin config.
+ *
+ * @return WP_Theme_JSON_Gutenberg Entity that holds styles for user data.
+ */
+ public static function get_user_data() {
+ if ( null !== static::$user ) {
+ return static::$user;
+ }
+
+ $config = array();
+ $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
+
+ 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 ) {
+ trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() );
+ return new WP_Theme_JSON_Gutenberg( $config, 'custom' );
+ }
+
+ // Very important to verify if the flag isGlobalStylesUserThemeJSON is true.
+ // If is not true the content was not escaped and is not safe.
+ if (
+ is_array( $decoded_data ) &&
+ isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
+ $decoded_data['isGlobalStylesUserThemeJSON']
+ ) {
+ unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
+ $config = $decoded_data;
+ }
+ }
+ static::$user = new WP_Theme_JSON_Gutenberg( $config, 'custom' );
+
+ return static::$user;
+ }
+
+ /**
+ * There are three sources of data (origins) for a site:
+ * default, theme, and custom. The custom's has higher priority
+ * than the theme's, and the theme's higher than defaults's.
+ *
+ * Unlike the getters {@link get_core_data},
+ * {@link get_theme_data}, and {@link get_user_data},
+ * this method returns data after it has been merged
+ * with the previous origins. This means that if the same piece of data
+ * is declared in different origins (user, theme, and core),
+ * the last origin overrides the previous.
+ *
+ * For example, if the user has set a background color
+ * for the paragraph block, and the theme has done it as well,
+ * the user preference wins.
+ *
+ * @param string $origin Optional. To what level should we merge data.
+ * Valid values are 'theme' or 'custom'.
+ * Default is 'custom'.
+ * @return WP_Theme_JSON_Gutenberg
+ */
+ public static function get_merged_data( $origin = 'custom' ) {
+ if ( is_array( $origin ) ) {
+ _deprecated_argument( __FUNCTION__, '5.9' );
+ }
+
+ $result = new WP_Theme_JSON_Gutenberg();
+ $result->merge( static::get_core_data() );
+ $result->merge( static::get_theme_data() );
+
+ if ( 'custom' === $origin ) {
+ $result->merge( static::get_user_data() );
+ }
+
+ return $result;
+ }
}
diff --git a/phpunit/class-wp-theme-json-schema-gutenberg-test.php b/phpunit/class-wp-theme-json-schema-gutenberg-test.php
deleted file mode 100644
index 6f6c2a323409c..0000000000000
--- a/phpunit/class-wp-theme-json-schema-gutenberg-test.php
+++ /dev/null
@@ -1,522 +0,0 @@
- array(
- 'defaults' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Black',
- 'slug' => 'black',
- 'color' => '#00000',
- ),
- array(
- 'name' => 'White',
- 'slug' => 'white',
- 'color' => '#ffffff',
- ),
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2',
- ),
- ),
- 'custom' => false,
- 'link' => false,
- ),
- 'typography' => array(
- 'customFontStyle' => false,
- 'customFontWeight' => false,
- 'customTextDecorations' => false,
- 'customTextTransforms' => false,
- ),
- ),
- 'root' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'link' => true,
- ),
- 'border' => array(
- 'customColor' => false,
- 'customRadius' => false,
- 'customStyle' => false,
- 'customWidth' => false,
- ),
- ),
- 'core/paragraph' => array(
- 'typography' => array(
- 'dropCap' => false,
- ),
- ),
- ),
- 'styles' => array(
- 'root' => array(
- 'color' => array(
- 'background' => 'purple',
- 'link' => 'red',
- ),
- ),
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- 'link' => 'yellow',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- ),
- ),
- );
-
- $actual = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json_v0 );
-
- $expected = array(
- 'version' => self::LATEST_SCHEMA_VERSION,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'custom' => false,
- 'link' => true,
- ),
- 'border' => array(
- 'color' => false,
- 'radius' => false,
- 'style' => false,
- 'width' => false,
- ),
- 'typography' => array(
- 'fontStyle' => false,
- 'fontWeight' => false,
- 'textDecoration' => false,
- 'textTransform' => false,
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'typography' => array(
- 'dropCap' => false,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'background' => 'purple',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'yellow',
- ),
- ),
- ),
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_migrate_v1_remove_custom_prefixes() {
- $theme_json_v1 = array(
- 'version' => 1,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'custom' => false,
- 'link' => true,
- ),
- 'border' => array(
- 'customColor' => false,
- 'customRadius' => false,
- 'customStyle' => false,
- 'customWidth' => false,
- ),
- 'typography' => array(
- 'customFontStyle' => false,
- 'customFontWeight' => false,
- 'customLetterSpacing' => false,
- 'customTextDecorations' => false,
- 'customTextTransforms' => false,
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'customColor' => true,
- 'customRadius' => true,
- 'customStyle' => true,
- 'customWidth' => true,
- ),
- 'typography' => array(
- 'customFontStyle' => true,
- 'customFontWeight' => true,
- 'customLetterSpacing' => true,
- 'customTextDecorations' => true,
- 'customTextTransforms' => true,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'background' => 'purple',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'yellow',
- ),
- ),
- ),
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- );
-
- $actual = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json_v1 );
-
- $expected = array(
- 'version' => self::LATEST_SCHEMA_VERSION,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'custom' => false,
- 'link' => true,
- ),
- 'border' => array(
- 'color' => false,
- 'radius' => false,
- 'style' => false,
- 'width' => false,
- ),
- 'typography' => array(
- 'fontStyle' => false,
- 'fontWeight' => false,
- 'letterSpacing' => false,
- 'textDecoration' => false,
- 'textTransform' => false,
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'color' => true,
- 'radius' => true,
- 'style' => true,
- 'width' => true,
- ),
- 'typography' => array(
- 'fontStyle' => true,
- 'fontWeight' => true,
- 'letterSpacing' => true,
- 'textDecoration' => true,
- 'textTransform' => true,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'background' => 'purple',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'yellow',
- ),
- ),
- ),
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_migrate_v1_to_v2() {
- $theme_json_v1 = array(
- 'version' => 1,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'custom' => false,
- 'link' => true,
- ),
- 'border' => array(
- 'customColor' => false,
- 'customRadius' => false,
- 'customStyle' => false,
- 'customWidth' => false,
- ),
- 'typography' => array(
- 'customFontStyle' => false,
- 'customFontWeight' => false,
- 'customLetterSpacing' => false,
- 'customTextDecorations' => false,
- 'customTextTransforms' => false,
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'customColor' => true,
- 'customRadius' => true,
- 'customStyle' => true,
- 'customWidth' => true,
- ),
- 'typography' => array(
- 'customFontStyle' => true,
- 'customFontWeight' => true,
- 'customLetterSpacing' => true,
- 'customTextDecorations' => true,
- 'customTextTransforms' => true,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'background' => 'purple',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'yellow',
- ),
- ),
- ),
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- );
-
- $actual = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json_v1 );
-
- $expected = array(
- 'version' => self::LATEST_SCHEMA_VERSION,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'name' => 'Pale Pink',
- 'slug' => 'pale-pink',
- 'color' => '#f78da7',
- ),
- array(
- 'name' => 'Vivid Red',
- 'slug' => 'vivid-red',
- 'color' => '#cf2e2e',
- ),
- ),
- 'custom' => false,
- 'link' => true,
- ),
- 'border' => array(
- 'color' => false,
- 'radius' => false,
- 'style' => false,
- 'width' => false,
- ),
- 'typography' => array(
- 'fontStyle' => false,
- 'fontWeight' => false,
- 'letterSpacing' => false,
- 'textDecoration' => false,
- 'textTransform' => false,
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'color' => true,
- 'radius' => true,
- 'style' => true,
- 'width' => true,
- ),
- 'typography' => array(
- 'fontStyle' => true,
- 'fontWeight' => true,
- 'letterSpacing' => true,
- 'textDecoration' => true,
- 'textTransform' => true,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'background' => 'purple',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'background' => 'red',
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '10px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'yellow',
- ),
- ),
- ),
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-}
From 5a76cb1422f0741bd1e0d453c750b419a8dd3190 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Wed, 25 May 2022 17:18:32 +0300
Subject: [PATCH 04/13] more tweaks
---
gutenberg.php | 4 ++--
lib/init.php | 24 -------------------
.../src/admin/visit-site-editor.ts | 3 +--
packages/e2e-test-utils/src/site-editor.js | 3 +--
4 files changed, 4 insertions(+), 30 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index 3488ca7f516c2..a965592e1baf6 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -26,7 +26,7 @@
function gutenberg_wordpress_version_notice() {
echo '';
/* translators: %s: Minimum required version */
- printf( __( 'Gutenberg requires WordPress %s or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ), '5.8' );
+ printf( __( 'Gutenberg requires WordPress %s or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ), '5.9' );
echo '
';
deactivate_plugins( array( 'gutenberg/gutenberg.php' ) );
@@ -64,7 +64,7 @@ function gutenberg_pre_init() {
// Compare against major release versions (X.Y) rather than minor (X.Y.Z)
// unless a minor release is the actual minimum requirement. WordPress reports
// X.Y for its major releases.
- if ( version_compare( $version, '5.8', '<' ) ) {
+ if ( version_compare( $version, '5.9', '<' ) ) {
add_action( 'admin_notices', 'gutenberg_wordpress_version_notice' );
return;
}
diff --git a/lib/init.php b/lib/init.php
index 9ba46a1f41fa2..b867dd472552f 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -58,30 +58,6 @@ function gutenberg_menu() {
}
add_action( 'admin_menu', 'gutenberg_menu', 9 );
-/**
- * Site editor's Menu.
- *
- * Adds a new wp-admin menu item for the Site editor.
- *
- * @since 9.4.0
- */
-function gutenberg_site_editor_menu() {
- if ( wp_is_block_theme() ) {
- add_theme_page(
- __( 'Editor (beta)', 'gutenberg' ),
- sprintf(
- /* translators: %s: "beta" label. */
- __( 'Editor %s', 'gutenberg' ),
- '' . __( 'beta', 'gutenberg' ) . ' '
- ),
- 'edit_theme_options',
- 'gutenberg-edit-site',
- 'gutenberg_edit_site_page'
- );
- }
-}
-add_action( 'admin_menu', 'gutenberg_site_editor_menu', 9 );
-
/**
* Outputs a WP REST API nonce.
*/
diff --git a/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts b/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts
index 0c374f2fe404b..ab66de030feb2 100644
--- a/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts
+++ b/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts
@@ -30,11 +30,10 @@ export async function visitSiteEditor(
skipWelcomeGuide = true
) {
const path = addQueryArgs( '', {
- page: 'gutenberg-edit-site',
...query,
} ).slice( 1 );
- await this.visitAdminPage( 'themes.php', path );
+ await this.visitAdminPage( 'site-editor.php', path );
await this.page.waitForSelector( CANVAS_SELECTOR );
if ( skipWelcomeGuide ) {
diff --git a/packages/e2e-test-utils/src/site-editor.js b/packages/e2e-test-utils/src/site-editor.js
index e32ad01cc396e..a30d8b5455037 100644
--- a/packages/e2e-test-utils/src/site-editor.js
+++ b/packages/e2e-test-utils/src/site-editor.js
@@ -248,11 +248,10 @@ export async function siteEditorNavigateSequence( labels ) {
*/
export async function visitSiteEditor( query, skipWelcomeGuide = true ) {
query = addQueryArgs( '', {
- page: 'gutenberg-edit-site',
...query,
} ).slice( 1 );
- await visitAdminPage( 'themes.php', query );
+ await visitAdminPage( 'site-editor.php', query );
await page.waitForSelector( SELECTORS.visualEditor );
if ( skipWelcomeGuide ) {
From 45249060ba1f0f76be7387a5453ef7ec29b72675 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Tue, 21 Jun 2022 09:53:27 +0300
Subject: [PATCH 05/13] remove v1 gallery files
---
packages/block-library/src/gallery/v1/edit.js | 475 ------------------
.../src/gallery/v1/gallery-image.native.js | 349 -------------
2 files changed, 824 deletions(-)
delete mode 100644 packages/block-library/src/gallery/v1/edit.js
delete mode 100644 packages/block-library/src/gallery/v1/gallery-image.native.js
diff --git a/packages/block-library/src/gallery/v1/edit.js b/packages/block-library/src/gallery/v1/edit.js
deleted file mode 100644
index e86fb49d4ad83..0000000000000
--- a/packages/block-library/src/gallery/v1/edit.js
+++ /dev/null
@@ -1,475 +0,0 @@
-/**
- * External dependencies
- */
-import {
- every,
- filter,
- find,
- forEach,
- get,
- isEmpty,
- map,
- reduce,
- some,
-} from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import { compose } from '@wordpress/compose';
-import {
- PanelBody,
- SelectControl,
- ToggleControl,
- withNotices,
- RangeControl,
-} from '@wordpress/components';
-import {
- MediaPlaceholder,
- InspectorControls,
- useBlockProps,
- store as blockEditorStore,
-} from '@wordpress/block-editor';
-import { Platform, useEffect, useState, useMemo } from '@wordpress/element';
-import { __ } from '@wordpress/i18n';
-import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
-import { useDispatch, useSelect } from '@wordpress/data';
-import { withViewportMatch } from '@wordpress/viewport';
-import { View } from '@wordpress/primitives';
-import { store as coreStore } from '@wordpress/core-data';
-
-/**
- * Internal dependencies
- */
-import { sharedIcon } from '../shared-icon';
-import { pickRelevantMediaFiles } from './shared';
-import { defaultColumnsNumberV1 } from '../deprecated';
-import Gallery from './gallery';
-import {
- LINK_DESTINATION_ATTACHMENT,
- LINK_DESTINATION_MEDIA,
- LINK_DESTINATION_NONE,
-} from './constants';
-
-const MAX_COLUMNS = 8;
-const linkOptions = [
- { value: LINK_DESTINATION_ATTACHMENT, label: __( 'Attachment Page' ) },
- { value: LINK_DESTINATION_MEDIA, label: __( 'Media File' ) },
- { value: LINK_DESTINATION_NONE, label: __( 'None' ) },
-];
-const ALLOWED_MEDIA_TYPES = [ 'image' ];
-
-const PLACEHOLDER_TEXT = Platform.select( {
- web: __(
- 'Drag images, upload new ones or select files from your library.'
- ),
- native: __( 'ADD MEDIA' ),
-} );
-
-const MOBILE_CONTROL_PROPS_RANGE_CONTROL = Platform.select( {
- web: {},
- native: { type: 'stepper' },
-} );
-
-function GalleryEdit( props ) {
- const {
- attributes,
- clientId,
- isSelected,
- noticeUI,
- noticeOperations,
- onFocus,
- } = props;
- const {
- columns = defaultColumnsNumberV1( attributes ),
- imageCrop,
- images,
- linkTo,
- sizeSlug,
- } = attributes;
- const [ selectedImage, setSelectedImage ] = useState();
- const [ attachmentCaptions, setAttachmentCaptions ] = useState();
- const { __unstableMarkNextChangeAsNotPersistent } =
- useDispatch( blockEditorStore );
-
- const { imageSizes, mediaUpload, getMedia, wasBlockJustInserted } =
- useSelect( ( select ) => {
- const settings = select( blockEditorStore ).getSettings();
-
- return {
- imageSizes: settings.imageSizes,
- mediaUpload: settings.mediaUpload,
- getMedia: select( coreStore ).getMedia,
- wasBlockJustInserted: select(
- blockEditorStore
- ).wasBlockJustInserted( clientId, 'inserter_menu' ),
- };
- } );
-
- const resizedImages = useMemo( () => {
- if ( isSelected ) {
- return reduce(
- attributes.ids,
- ( currentResizedImages, id ) => {
- if ( ! id ) {
- return currentResizedImages;
- }
- const image = getMedia( id );
- const sizes = reduce(
- imageSizes,
- ( currentSizes, size ) => {
- const defaultUrl = get( image, [
- 'sizes',
- size.slug,
- 'url',
- ] );
- const mediaDetailsUrl = get( image, [
- 'media_details',
- 'sizes',
- size.slug,
- 'source_url',
- ] );
- return {
- ...currentSizes,
- [ size.slug ]: defaultUrl || mediaDetailsUrl,
- };
- },
- {}
- );
- return {
- ...currentResizedImages,
- [ parseInt( id, 10 ) ]: sizes,
- };
- },
- {}
- );
- }
- return {};
- }, [ isSelected, attributes.ids, imageSizes ] );
-
- function onFocusGalleryCaption() {
- setSelectedImage();
- }
-
- function setAttributes( newAttrs ) {
- if ( newAttrs.ids ) {
- throw new Error(
- 'The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes'
- );
- }
-
- if ( newAttrs.images ) {
- newAttrs = {
- ...newAttrs,
- // Unlike images[ n ].id which is a string, always ensure the
- // ids array contains numbers as per its attribute type.
- ids: map( newAttrs.images, ( { id } ) => parseInt( id, 10 ) ),
- };
- }
-
- props.setAttributes( newAttrs );
- }
-
- function onSelectImage( index ) {
- return () => {
- setSelectedImage( index );
- };
- }
-
- function onDeselectImage() {
- return () => {
- setSelectedImage();
- };
- }
-
- function onMove( oldIndex, newIndex ) {
- const newImages = [ ...images ];
- newImages.splice( newIndex, 1, images[ oldIndex ] );
- newImages.splice( oldIndex, 1, images[ newIndex ] );
- setSelectedImage( newIndex );
- setAttributes( { images: newImages } );
- }
-
- function onMoveForward( oldIndex ) {
- return () => {
- if ( oldIndex === images.length - 1 ) {
- return;
- }
- onMove( oldIndex, oldIndex + 1 );
- };
- }
-
- function onMoveBackward( oldIndex ) {
- return () => {
- if ( oldIndex === 0 ) {
- return;
- }
- onMove( oldIndex, oldIndex - 1 );
- };
- }
-
- function onRemoveImage( index ) {
- return () => {
- const newImages = filter( images, ( img, i ) => index !== i );
- setSelectedImage();
- setAttributes( {
- images: newImages,
- columns: attributes.columns
- ? Math.min( newImages.length, attributes.columns )
- : attributes.columns,
- } );
- };
- }
-
- function selectCaption( newImage ) {
- // The image id in both the images and attachmentCaptions arrays is a
- // string, so ensure comparison works correctly by converting the
- // newImage.id to a string.
- const newImageId = newImage.id.toString();
- const currentImage = find( images, { id: newImageId } );
- const currentImageCaption = currentImage
- ? currentImage.caption
- : newImage.caption;
-
- if ( ! attachmentCaptions ) {
- return currentImageCaption;
- }
-
- const attachment = find( attachmentCaptions, {
- id: newImageId,
- } );
-
- // If the attachment caption is updated.
- if ( attachment && attachment.caption !== newImage.caption ) {
- return newImage.caption;
- }
-
- return currentImageCaption;
- }
-
- function onSelectImages( newImages ) {
- setAttachmentCaptions(
- newImages.map( ( newImage ) => ( {
- // Store the attachmentCaption id as a string for consistency
- // with the type of the id in the images attribute.
- id: newImage.id.toString(),
- caption: newImage.caption,
- } ) )
- );
- setAttributes( {
- images: newImages.map( ( newImage ) => ( {
- ...pickRelevantMediaFiles( newImage, sizeSlug ),
- caption: selectCaption( newImage, images, attachmentCaptions ),
- // The id value is stored in a data attribute, so when the
- // block is parsed it's converted to a string. Converting
- // to a string here ensures it's type is consistent.
- id: newImage.id.toString(),
- } ) ),
- columns: attributes.columns
- ? Math.min( newImages.length, attributes.columns )
- : attributes.columns,
- } );
- }
-
- function onUploadError( message ) {
- noticeOperations.removeAllNotices();
- noticeOperations.createErrorNotice( message );
- }
-
- function setLinkTo( value ) {
- setAttributes( { linkTo: value } );
- }
-
- function setColumnsNumber( value ) {
- setAttributes( { columns: value } );
- }
-
- function toggleImageCrop() {
- setAttributes( { imageCrop: ! imageCrop } );
- }
-
- function getImageCropHelp( checked ) {
- return checked
- ? __( 'Thumbnails are cropped to align.' )
- : __( 'Thumbnails are not cropped.' );
- }
-
- function setImageAttributes( index, newAttributes ) {
- if ( ! images[ index ] ) {
- return;
- }
-
- setAttributes( {
- images: [
- ...images.slice( 0, index ),
- {
- ...images[ index ],
- ...newAttributes,
- },
- ...images.slice( index + 1 ),
- ],
- } );
- }
-
- function getImagesSizeOptions() {
- return map(
- filter( imageSizes, ( { slug } ) =>
- some( resizedImages, ( sizes ) => sizes[ slug ] )
- ),
- ( { name, slug } ) => ( { value: slug, label: name } )
- );
- }
-
- function updateImagesSize( newSizeSlug ) {
- const updatedImages = map( images, ( image ) => {
- if ( ! image.id ) {
- return image;
- }
- const url = get( resizedImages, [
- parseInt( image.id, 10 ),
- newSizeSlug,
- ] );
- return {
- ...image,
- ...( url && { url } ),
- };
- } );
-
- setAttributes( { images: updatedImages, sizeSlug: newSizeSlug } );
- }
-
- useEffect( () => {
- if (
- Platform.OS === 'web' &&
- images &&
- images.length > 0 &&
- every( images, ( { url } ) => isBlobURL( url ) )
- ) {
- const filesList = map( images, ( { url } ) => getBlobByURL( url ) );
- forEach( images, ( { url } ) => revokeBlobURL( url ) );
- mediaUpload( {
- filesList,
- onFileChange: onSelectImages,
- allowedTypes: [ 'image' ],
- } );
- }
- }, [] );
-
- useEffect( () => {
- // Deselect images when deselecting the block.
- if ( ! isSelected ) {
- setSelectedImage();
- }
- }, [ isSelected ] );
-
- useEffect( () => {
- // linkTo attribute must be saved so blocks don't break when changing
- // image_default_link_type in options.php.
- if ( ! linkTo ) {
- __unstableMarkNextChangeAsNotPersistent();
- setAttributes( {
- linkTo:
- window?.wp?.media?.view?.settings?.defaultProps?.link ||
- LINK_DESTINATION_NONE,
- } );
- }
- }, [ linkTo ] );
-
- const hasImages = !! images.length;
- const hasImageIds = hasImages && images.some( ( image ) => !! image.id );
-
- const mediaPlaceholder = (
-
- );
-
- const blockProps = useBlockProps();
-
- if ( ! hasImages ) {
- return { mediaPlaceholder } ;
- }
-
- const imageSizeOptions = getImagesSizeOptions();
- const shouldShowSizeOptions = hasImages && ! isEmpty( imageSizeOptions );
-
- return (
- <>
-
-
- { images.length > 1 && (
-
- ) }
-
-
- { shouldShowSizeOptions && (
-
- ) }
-
-
- { noticeUI }
-
- >
- );
-}
-
-export default compose( [
- withNotices,
- withViewportMatch( { isNarrow: '< small' } ),
-] )( GalleryEdit );
diff --git a/packages/block-library/src/gallery/v1/gallery-image.native.js b/packages/block-library/src/gallery/v1/gallery-image.native.js
deleted file mode 100644
index 7fa6ab4ab7ea7..0000000000000
--- a/packages/block-library/src/gallery/v1/gallery-image.native.js
+++ /dev/null
@@ -1,349 +0,0 @@
-/**
- * External dependencies
- */
-import {
- StyleSheet,
- View,
- ScrollView,
- TouchableWithoutFeedback,
-} from 'react-native';
-import { isEmpty } from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import {
- requestImageFailedRetryDialog,
- requestImageUploadCancelDialog,
- requestImageFullscreenPreview,
-} from '@wordpress/react-native-bridge';
-import { Component } from '@wordpress/element';
-import { Image } from '@wordpress/components';
-import { __, sprintf } from '@wordpress/i18n';
-import { Caption, MediaUploadProgress } from '@wordpress/block-editor';
-import { getProtocol } from '@wordpress/url';
-import { withPreferredColorScheme } from '@wordpress/compose';
-import { arrowLeft, arrowRight, warning } from '@wordpress/icons';
-
-/**
- * Internal dependencies
- */
-import Button from './gallery-button';
-import style from './gallery-image-style.scss';
-
-const { compose } = StyleSheet;
-
-const separatorStyle = compose( style.separator, {
- borderRightWidth: StyleSheet.hairlineWidth,
-} );
-const buttonStyle = compose( style.button, { aspectRatio: 1 } );
-const ICON_SIZE_ARROW = 15;
-
-class GalleryImage extends Component {
- constructor() {
- super( ...arguments );
-
- this.onSelectImage = this.onSelectImage.bind( this );
- this.onSelectCaption = this.onSelectCaption.bind( this );
- this.onMediaPressed = this.onMediaPressed.bind( this );
- this.onCaptionChange = this.onCaptionChange.bind( this );
- this.onSelectMedia = this.onSelectMedia.bind( this );
-
- this.updateMediaProgress = this.updateMediaProgress.bind( this );
- this.finishMediaUploadWithSuccess =
- this.finishMediaUploadWithSuccess.bind( this );
- this.finishMediaUploadWithFailure =
- this.finishMediaUploadWithFailure.bind( this );
- this.renderContent = this.renderContent.bind( this );
-
- this.state = {
- captionSelected: false,
- isUploadInProgress: false,
- didUploadFail: false,
- };
- }
-
- onSelectCaption() {
- if ( ! this.state.captionSelected ) {
- this.setState( {
- captionSelected: true,
- } );
- }
-
- if ( ! this.props.isSelected ) {
- this.props.onSelect();
- }
- }
-
- onMediaPressed() {
- const { id, url, isSelected } = this.props;
- const { captionSelected, isUploadInProgress, didUploadFail } =
- this.state;
-
- this.onSelectImage();
-
- if ( isUploadInProgress ) {
- requestImageUploadCancelDialog( id );
- } else if (
- didUploadFail ||
- ( id && getProtocol( url ) === 'file:' )
- ) {
- requestImageFailedRetryDialog( id );
- } else if ( isSelected && ! captionSelected ) {
- requestImageFullscreenPreview( url );
- }
- }
-
- onSelectImage() {
- if ( ! this.props.isBlockSelected ) {
- this.props.onSelectBlock();
- }
-
- if ( ! this.props.isSelected ) {
- this.props.onSelect();
- }
-
- if ( this.state.captionSelected ) {
- this.setState( {
- captionSelected: false,
- } );
- }
- }
-
- onSelectMedia( media ) {
- const { setAttributes } = this.props;
- setAttributes( media );
- }
-
- onCaptionChange( caption ) {
- const { setAttributes } = this.props;
- setAttributes( { caption } );
- }
-
- componentDidUpdate( prevProps ) {
- const { isSelected, image, url } = this.props;
- if ( image && ! url ) {
- this.props.setAttributes( {
- url: image.source_url,
- alt: image.alt_text,
- } );
- }
-
- // Unselect the caption so when the user selects other image and comeback
- // the caption is not immediately selected.
- if (
- this.state.captionSelected &&
- ! isSelected &&
- prevProps.isSelected
- ) {
- this.setState( {
- captionSelected: false,
- } );
- }
- }
-
- updateMediaProgress() {
- if ( ! this.state.isUploadInProgress ) {
- this.setState( { isUploadInProgress: true } );
- }
- }
-
- finishMediaUploadWithSuccess( payload ) {
- this.setState( {
- isUploadInProgress: false,
- didUploadFail: false,
- } );
-
- this.props.setAttributes( {
- id: payload.mediaServerId,
- url: payload.mediaUrl,
- } );
- }
-
- finishMediaUploadWithFailure() {
- this.setState( {
- isUploadInProgress: false,
- didUploadFail: true,
- } );
- }
-
- renderContent( params ) {
- const {
- url,
- isFirstItem,
- isLastItem,
- isSelected,
- caption,
- onRemove,
- onMoveForward,
- onMoveBackward,
- 'aria-label': ariaLabel,
- isCropped,
- getStylesFromColorScheme,
- isRTL,
- } = this.props;
-
- const { isUploadInProgress, captionSelected } = this.state;
- const { isUploadFailed, retryMessage } = params;
- const resizeMode = isCropped ? 'cover' : 'contain';
-
- const captionPlaceholderStyle = getStylesFromColorScheme(
- style.captionPlaceholder,
- style.captionPlaceholderDark
- );
-
- const shouldShowCaptionEditable = ! isUploadFailed && isSelected;
- const shouldShowCaptionExpanded =
- ! isUploadFailed && ! isSelected && !! caption;
-
- const captionContainerStyle = shouldShowCaptionExpanded
- ? style.captionExpandedContainer
- : style.captionContainer;
-
- const captionStyle = shouldShowCaptionExpanded
- ? style.captionExpanded
- : style.caption;
-
- const mediaPickerOptions = [
- {
- destructiveButton: true,
- id: 'removeImage',
- label: __( 'Remove' ),
- onPress: onRemove,
- separated: true,
- value: 'removeImage',
- },
- ];
-
- return (
- <>
-
-
- { ! isUploadInProgress && isSelected && (
-
-
-
-
-
-
-
-
-
- ) }
-
- { ! isUploadInProgress &&
- ( shouldShowCaptionEditable ||
- shouldShowCaptionExpanded ) && (
-
-
-
-
-
- ) }
- >
- );
- }
-
- render() {
- const { id, onRemove, getStylesFromColorScheme, isSelected } =
- this.props;
-
- const containerStyle = getStylesFromColorScheme(
- style.galleryImageContainer,
- style.galleryImageContainerDark
- );
-
- return (
-
-
-
-
-
- );
- }
-
- accessibilityLabelImageContainer() {
- const { caption, 'aria-label': ariaLabel } = this.props;
-
- return isEmpty( caption )
- ? ariaLabel
- : ariaLabel +
- '. ' +
- sprintf(
- /* translators: accessibility text. %s: image caption. */
- __( 'Image caption. %s' ),
- caption
- );
- }
-}
-
-export default withPreferredColorScheme( GalleryImage );
From a4b71abb30e608345834c984981a259c31488f63 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Tue, 28 Jun 2022 21:04:58 +0300
Subject: [PATCH 06/13] revert gallery udpates
---
.../block-library/src/gallery/deprecated.js | 63 ++-
.../block-library/src/gallery/edit-wrapper.js | 27 +
.../src/gallery/gallery-styles.native.scss | 2 +
packages/block-library/src/gallery/index.js | 2 +-
packages/block-library/src/gallery/save.js | 10 +
.../block-library/src/gallery/transforms.js | 169 +++++--
.../block-library/src/gallery/v1/constants.js | 3 +
packages/block-library/src/gallery/v1/edit.js | 475 ++++++++++++++++++
.../src/gallery/v1/gallery-button.native.js | 47 ++
.../v1/gallery-image-style.native.scss | 109 ++++
.../src/gallery/v1/gallery-image.js | 282 +++++++++++
.../src/gallery/v1/gallery-image.native.js | 349 +++++++++++++
.../src/gallery/v1/gallery-styles.native.scss | 8 +
.../block-library/src/gallery/v1/gallery.js | 125 +++++
.../src/gallery/v1/gallery.native.js | 162 ++++++
packages/block-library/src/gallery/v1/save.js | 98 ++++
.../block-library/src/gallery/v1/shared.js | 19 +
.../src/gallery/v1/tiles-styles.native.scss | 11 +
.../src/gallery/v1/tiles.native.js | 79 +++
19 files changed, 1986 insertions(+), 54 deletions(-)
create mode 100644 packages/block-library/src/gallery/edit-wrapper.js
create mode 100644 packages/block-library/src/gallery/v1/constants.js
create mode 100644 packages/block-library/src/gallery/v1/edit.js
create mode 100644 packages/block-library/src/gallery/v1/gallery-button.native.js
create mode 100644 packages/block-library/src/gallery/v1/gallery-image-style.native.scss
create mode 100644 packages/block-library/src/gallery/v1/gallery-image.js
create mode 100644 packages/block-library/src/gallery/v1/gallery-image.native.js
create mode 100644 packages/block-library/src/gallery/v1/gallery-styles.native.scss
create mode 100644 packages/block-library/src/gallery/v1/gallery.js
create mode 100644 packages/block-library/src/gallery/v1/gallery.native.js
create mode 100644 packages/block-library/src/gallery/v1/save.js
create mode 100644 packages/block-library/src/gallery/v1/shared.js
create mode 100644 packages/block-library/src/gallery/v1/tiles-styles.native.scss
create mode 100644 packages/block-library/src/gallery/v1/tiles.native.js
diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index 1ac1034cb9b60..e71b87d2ab791 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
-import { some, omit } from 'lodash';
+import { map, some, omit } from 'lodash';
/**
* WordPress dependencies
@@ -19,6 +19,7 @@ import {
LINK_DESTINATION_MEDIA,
LINK_DESTINATION_NONE,
} from './constants';
+import { isGalleryV2Enabled } from './shared';
const DEPRECATED_LINK_DESTINATION_MEDIA = 'file';
const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post';
@@ -281,7 +282,11 @@ const v6 = {
);
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+
+ return attributes;
},
};
const v5 = {
@@ -367,7 +372,23 @@ const v5 = {
return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+
+ let linkTo = attributes.linkTo;
+
+ if ( ! attributes.linkTo ) {
+ linkTo = 'none';
+ } else if ( attributes.linkTo === 'attachment' ) {
+ linkTo = 'post';
+ } else if ( attributes.linkTo === 'media' ) {
+ linkTo = 'file';
+ }
+ return {
+ ...attributes,
+ linkTo,
+ };
},
save( { attributes } ) {
const {
@@ -514,7 +535,17 @@ const v4 = {
return ids && ids.some( ( id ) => typeof id === 'string' );
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+
+ return {
+ ...attributes,
+ ids: map( attributes.ids, ( id ) => {
+ const parsedId = parseInt( id, 10 );
+ return Number.isInteger( parsedId ) ? parsedId : null;
+ } ),
+ };
},
save( { attributes } ) {
const {
@@ -710,7 +741,10 @@ const v3 = {
);
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+ return attributes;
},
};
const v2 = {
@@ -776,7 +810,18 @@ const v2 = {
);
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+ return {
+ ...attributes,
+ ids: map( attributes.images, ( { id } ) => {
+ if ( ! id ) {
+ return null;
+ }
+ return parseInt( id, 10 );
+ } ),
+ };
},
supports: {
align: true,
@@ -929,7 +974,11 @@ const v1 = {
);
},
migrate( attributes ) {
- return runV2Migration( attributes );
+ if ( isGalleryV2Enabled() ) {
+ return runV2Migration( attributes );
+ }
+
+ return attributes;
},
};
diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
new file mode 100644
index 0000000000000..2c81271902d31
--- /dev/null
+++ b/packages/block-library/src/gallery/edit-wrapper.js
@@ -0,0 +1,27 @@
+/**
+ * WordPress dependencies
+ */
+import { compose } from '@wordpress/compose';
+import { withNotices } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import EditWithInnerBlocks from './edit';
+import EditWithoutInnerBlocks from './v1/edit';
+import { isGalleryV2Enabled } from './shared';
+
+/*
+ * Using a wrapper around the logic to load the edit for v1 of Gallery block
+ * or the refactored version with InnerBlocks. This is to prevent conditional
+ * use of hooks lint errors if adding this logic to the top of the edit component.
+ */
+function GalleryEditWrapper( props ) {
+ if ( ! isGalleryV2Enabled() ) {
+ return ;
+ }
+
+ return ;
+}
+
+export default compose( [ withNotices ] )( GalleryEditWrapper );
diff --git a/packages/block-library/src/gallery/gallery-styles.native.scss b/packages/block-library/src/gallery/gallery-styles.native.scss
index f9b4d63c55b81..a3073592291b9 100644
--- a/packages/block-library/src/gallery/gallery-styles.native.scss
+++ b/packages/block-library/src/gallery/gallery-styles.native.scss
@@ -1,3 +1,5 @@
+@import "./v1/gallery-styles.native.scss";
+
.galleryAppender {
padding-top: $grid-unit-20;
}
diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js
index ca74aec021b28..c9b82278a507a 100644
--- a/packages/block-library/src/gallery/index.js
+++ b/packages/block-library/src/gallery/index.js
@@ -7,7 +7,7 @@ import { gallery as icon } from '@wordpress/icons';
* Internal dependencies
*/
import deprecated from './deprecated';
-import edit from './edit';
+import edit from './edit-wrapper';
import metadata from './block.json';
import save from './save';
import transforms from './transforms';
diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index acb689e4427ba..66253001e6418 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -13,7 +13,17 @@ import {
__experimentalGetElementClassName,
} from '@wordpress/block-editor';
+/**
+ * Internal dependencies
+ */
+import saveWithoutInnerBlocks from './v1/save';
+import { isGalleryV2Enabled } from './shared';
+
export default function saveWithInnerBlocks( { attributes } ) {
+ if ( ! isGalleryV2Enabled() ) {
+ return saveWithoutInnerBlocks( { attributes } );
+ }
+
const { caption, columns, imageCrop } = attributes;
const className = classnames( 'has-nested-images', {
diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index 74d5e1149f908..53a08bd590e71 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -18,6 +18,11 @@ import {
LINK_DESTINATION_NONE,
LINK_DESTINATION_MEDIA,
} from './constants';
+import {
+ LINK_DESTINATION_ATTACHMENT as DEPRECATED_LINK_DESTINATION_ATTACHMENT,
+ LINK_DESTINATION_MEDIA as DEPRECATED_LINK_DESTINATION_MEDIA,
+} from './v1/constants';
+import { pickRelevantMediaFiles, isGalleryV2Enabled } from './shared';
const parseShortcodeIds = ( ids ) => {
if ( ! ids ) {
@@ -43,6 +48,7 @@ const parseShortcodeIds = ( ids ) => {
*/
function updateThirdPartyTransformToGallery( block ) {
if (
+ isGalleryV2Enabled() &&
block.name === 'core/gallery' &&
block.attributes?.images.length > 0
) {
@@ -136,18 +142,34 @@ const transforms = {
const validImages = filter( attributes, ( { url } ) => url );
- const innerBlocks = validImages.map( ( image ) => {
- return createBlock( 'core/image', image );
- } );
+ if ( isGalleryV2Enabled() ) {
+ const innerBlocks = validImages.map( ( image ) => {
+ return createBlock( 'core/image', image );
+ } );
- return createBlock(
- 'core/gallery',
- {
- align,
- sizeSlug,
- },
- innerBlocks
- );
+ return createBlock(
+ 'core/gallery',
+ {
+ align,
+ sizeSlug,
+ },
+ innerBlocks
+ );
+ }
+
+ return createBlock( 'core/gallery', {
+ images: validImages.map(
+ ( { id, url, alt, caption } ) => ( {
+ id: id.toString(),
+ url,
+ alt,
+ caption,
+ } )
+ ),
+ ids: validImages.map( ( { id } ) => parseInt( id, 10 ) ),
+ align,
+ sizeSlug,
+ } );
},
},
{
@@ -155,12 +177,32 @@ const transforms = {
tag: 'gallery',
attributes: {
+ images: {
+ type: 'array',
+ shortcode: ( { named: { ids } } ) => {
+ if ( ! isGalleryV2Enabled() ) {
+ return parseShortcodeIds( ids ).map( ( id ) => ( {
+ id: id.toString(),
+ } ) );
+ }
+ },
+ },
+ ids: {
+ type: 'array',
+ shortcode: ( { named: { ids } } ) => {
+ if ( ! isGalleryV2Enabled() ) {
+ return parseShortcodeIds( ids );
+ }
+ },
+ },
shortCodeTransforms: {
type: 'array',
shortcode: ( { named: { ids } } ) => {
- return parseShortcodeIds( ids ).map( ( id ) => ( {
- id: parseInt( id ),
- } ) );
+ if ( isGalleryV2Enabled() ) {
+ return parseShortcodeIds( ids ).map( ( id ) => ( {
+ id: parseInt( id ),
+ } ) );
+ }
},
},
columns: {
@@ -172,6 +214,16 @@ const transforms = {
linkTo: {
type: 'string',
shortcode: ( { named: { link } } ) => {
+ if ( ! isGalleryV2Enabled() ) {
+ switch ( link ) {
+ case 'post':
+ return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
+ case 'file':
+ return DEPRECATED_LINK_DESTINATION_MEDIA;
+ default:
+ return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
+ }
+ }
switch ( link ) {
case 'post':
return LINK_DESTINATION_ATTACHMENT;
@@ -205,13 +257,23 @@ const transforms = {
);
},
transform( files ) {
- const innerBlocks = files.map( ( file ) =>
- createBlock( 'core/image', {
- url: createBlobURL( file ),
- } )
- );
+ if ( isGalleryV2Enabled() ) {
+ const innerBlocks = files.map( ( file ) =>
+ createBlock( 'core/image', {
+ url: createBlobURL( file ),
+ } )
+ );
- return createBlock( 'core/gallery', {}, innerBlocks );
+ return createBlock( 'core/gallery', {}, innerBlocks );
+ }
+ const block = createBlock( 'core/gallery', {
+ images: files.map( ( file ) =>
+ pickRelevantMediaFiles( {
+ url: createBlobURL( file ),
+ } )
+ ),
+ } );
+ return block;
},
},
],
@@ -219,32 +281,47 @@ const transforms = {
{
type: 'block',
blocks: [ 'core/image' ],
- transform: ( { align }, innerBlocks ) => {
- if ( innerBlocks.length > 0 ) {
- return innerBlocks.map(
- ( {
- attributes: {
- id,
- url,
- alt,
- caption,
- sizeSlug: imageSizeSlug,
- linkDestination,
- href,
- linkTarget,
- },
- } ) =>
- createBlock( 'core/image', {
- id,
- url,
- alt,
- caption,
- sizeSlug: imageSizeSlug,
- align,
- linkDestination,
- href,
- linkTarget,
- } )
+ transform: ( { align, images, ids, sizeSlug }, innerBlocks ) => {
+ if ( isGalleryV2Enabled() ) {
+ if ( innerBlocks.length > 0 ) {
+ return innerBlocks.map(
+ ( {
+ attributes: {
+ id,
+ url,
+ alt,
+ caption,
+ sizeSlug: imageSizeSlug,
+ linkDestination,
+ href,
+ linkTarget,
+ },
+ } ) =>
+ createBlock( 'core/image', {
+ id,
+ url,
+ alt,
+ caption,
+ sizeSlug: imageSizeSlug,
+ align,
+ linkDestination,
+ href,
+ linkTarget,
+ } )
+ );
+ }
+ return createBlock( 'core/image', { align } );
+ }
+ if ( images.length > 0 ) {
+ return images.map( ( { url, alt, caption }, index ) =>
+ createBlock( 'core/image', {
+ id: ids[ index ],
+ url,
+ alt,
+ caption,
+ align,
+ sizeSlug,
+ } )
);
}
return createBlock( 'core/image', { align } );
diff --git a/packages/block-library/src/gallery/v1/constants.js b/packages/block-library/src/gallery/v1/constants.js
new file mode 100644
index 0000000000000..f4b6e7af56d47
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/constants.js
@@ -0,0 +1,3 @@
+export const LINK_DESTINATION_NONE = 'none';
+export const LINK_DESTINATION_MEDIA = 'file';
+export const LINK_DESTINATION_ATTACHMENT = 'post';
diff --git a/packages/block-library/src/gallery/v1/edit.js b/packages/block-library/src/gallery/v1/edit.js
new file mode 100644
index 0000000000000..e86fb49d4ad83
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/edit.js
@@ -0,0 +1,475 @@
+/**
+ * External dependencies
+ */
+import {
+ every,
+ filter,
+ find,
+ forEach,
+ get,
+ isEmpty,
+ map,
+ reduce,
+ some,
+} from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { compose } from '@wordpress/compose';
+import {
+ PanelBody,
+ SelectControl,
+ ToggleControl,
+ withNotices,
+ RangeControl,
+} from '@wordpress/components';
+import {
+ MediaPlaceholder,
+ InspectorControls,
+ useBlockProps,
+ store as blockEditorStore,
+} from '@wordpress/block-editor';
+import { Platform, useEffect, useState, useMemo } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { withViewportMatch } from '@wordpress/viewport';
+import { View } from '@wordpress/primitives';
+import { store as coreStore } from '@wordpress/core-data';
+
+/**
+ * Internal dependencies
+ */
+import { sharedIcon } from '../shared-icon';
+import { pickRelevantMediaFiles } from './shared';
+import { defaultColumnsNumberV1 } from '../deprecated';
+import Gallery from './gallery';
+import {
+ LINK_DESTINATION_ATTACHMENT,
+ LINK_DESTINATION_MEDIA,
+ LINK_DESTINATION_NONE,
+} from './constants';
+
+const MAX_COLUMNS = 8;
+const linkOptions = [
+ { value: LINK_DESTINATION_ATTACHMENT, label: __( 'Attachment Page' ) },
+ { value: LINK_DESTINATION_MEDIA, label: __( 'Media File' ) },
+ { value: LINK_DESTINATION_NONE, label: __( 'None' ) },
+];
+const ALLOWED_MEDIA_TYPES = [ 'image' ];
+
+const PLACEHOLDER_TEXT = Platform.select( {
+ web: __(
+ 'Drag images, upload new ones or select files from your library.'
+ ),
+ native: __( 'ADD MEDIA' ),
+} );
+
+const MOBILE_CONTROL_PROPS_RANGE_CONTROL = Platform.select( {
+ web: {},
+ native: { type: 'stepper' },
+} );
+
+function GalleryEdit( props ) {
+ const {
+ attributes,
+ clientId,
+ isSelected,
+ noticeUI,
+ noticeOperations,
+ onFocus,
+ } = props;
+ const {
+ columns = defaultColumnsNumberV1( attributes ),
+ imageCrop,
+ images,
+ linkTo,
+ sizeSlug,
+ } = attributes;
+ const [ selectedImage, setSelectedImage ] = useState();
+ const [ attachmentCaptions, setAttachmentCaptions ] = useState();
+ const { __unstableMarkNextChangeAsNotPersistent } =
+ useDispatch( blockEditorStore );
+
+ const { imageSizes, mediaUpload, getMedia, wasBlockJustInserted } =
+ useSelect( ( select ) => {
+ const settings = select( blockEditorStore ).getSettings();
+
+ return {
+ imageSizes: settings.imageSizes,
+ mediaUpload: settings.mediaUpload,
+ getMedia: select( coreStore ).getMedia,
+ wasBlockJustInserted: select(
+ blockEditorStore
+ ).wasBlockJustInserted( clientId, 'inserter_menu' ),
+ };
+ } );
+
+ const resizedImages = useMemo( () => {
+ if ( isSelected ) {
+ return reduce(
+ attributes.ids,
+ ( currentResizedImages, id ) => {
+ if ( ! id ) {
+ return currentResizedImages;
+ }
+ const image = getMedia( id );
+ const sizes = reduce(
+ imageSizes,
+ ( currentSizes, size ) => {
+ const defaultUrl = get( image, [
+ 'sizes',
+ size.slug,
+ 'url',
+ ] );
+ const mediaDetailsUrl = get( image, [
+ 'media_details',
+ 'sizes',
+ size.slug,
+ 'source_url',
+ ] );
+ return {
+ ...currentSizes,
+ [ size.slug ]: defaultUrl || mediaDetailsUrl,
+ };
+ },
+ {}
+ );
+ return {
+ ...currentResizedImages,
+ [ parseInt( id, 10 ) ]: sizes,
+ };
+ },
+ {}
+ );
+ }
+ return {};
+ }, [ isSelected, attributes.ids, imageSizes ] );
+
+ function onFocusGalleryCaption() {
+ setSelectedImage();
+ }
+
+ function setAttributes( newAttrs ) {
+ if ( newAttrs.ids ) {
+ throw new Error(
+ 'The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes'
+ );
+ }
+
+ if ( newAttrs.images ) {
+ newAttrs = {
+ ...newAttrs,
+ // Unlike images[ n ].id which is a string, always ensure the
+ // ids array contains numbers as per its attribute type.
+ ids: map( newAttrs.images, ( { id } ) => parseInt( id, 10 ) ),
+ };
+ }
+
+ props.setAttributes( newAttrs );
+ }
+
+ function onSelectImage( index ) {
+ return () => {
+ setSelectedImage( index );
+ };
+ }
+
+ function onDeselectImage() {
+ return () => {
+ setSelectedImage();
+ };
+ }
+
+ function onMove( oldIndex, newIndex ) {
+ const newImages = [ ...images ];
+ newImages.splice( newIndex, 1, images[ oldIndex ] );
+ newImages.splice( oldIndex, 1, images[ newIndex ] );
+ setSelectedImage( newIndex );
+ setAttributes( { images: newImages } );
+ }
+
+ function onMoveForward( oldIndex ) {
+ return () => {
+ if ( oldIndex === images.length - 1 ) {
+ return;
+ }
+ onMove( oldIndex, oldIndex + 1 );
+ };
+ }
+
+ function onMoveBackward( oldIndex ) {
+ return () => {
+ if ( oldIndex === 0 ) {
+ return;
+ }
+ onMove( oldIndex, oldIndex - 1 );
+ };
+ }
+
+ function onRemoveImage( index ) {
+ return () => {
+ const newImages = filter( images, ( img, i ) => index !== i );
+ setSelectedImage();
+ setAttributes( {
+ images: newImages,
+ columns: attributes.columns
+ ? Math.min( newImages.length, attributes.columns )
+ : attributes.columns,
+ } );
+ };
+ }
+
+ function selectCaption( newImage ) {
+ // The image id in both the images and attachmentCaptions arrays is a
+ // string, so ensure comparison works correctly by converting the
+ // newImage.id to a string.
+ const newImageId = newImage.id.toString();
+ const currentImage = find( images, { id: newImageId } );
+ const currentImageCaption = currentImage
+ ? currentImage.caption
+ : newImage.caption;
+
+ if ( ! attachmentCaptions ) {
+ return currentImageCaption;
+ }
+
+ const attachment = find( attachmentCaptions, {
+ id: newImageId,
+ } );
+
+ // If the attachment caption is updated.
+ if ( attachment && attachment.caption !== newImage.caption ) {
+ return newImage.caption;
+ }
+
+ return currentImageCaption;
+ }
+
+ function onSelectImages( newImages ) {
+ setAttachmentCaptions(
+ newImages.map( ( newImage ) => ( {
+ // Store the attachmentCaption id as a string for consistency
+ // with the type of the id in the images attribute.
+ id: newImage.id.toString(),
+ caption: newImage.caption,
+ } ) )
+ );
+ setAttributes( {
+ images: newImages.map( ( newImage ) => ( {
+ ...pickRelevantMediaFiles( newImage, sizeSlug ),
+ caption: selectCaption( newImage, images, attachmentCaptions ),
+ // The id value is stored in a data attribute, so when the
+ // block is parsed it's converted to a string. Converting
+ // to a string here ensures it's type is consistent.
+ id: newImage.id.toString(),
+ } ) ),
+ columns: attributes.columns
+ ? Math.min( newImages.length, attributes.columns )
+ : attributes.columns,
+ } );
+ }
+
+ function onUploadError( message ) {
+ noticeOperations.removeAllNotices();
+ noticeOperations.createErrorNotice( message );
+ }
+
+ function setLinkTo( value ) {
+ setAttributes( { linkTo: value } );
+ }
+
+ function setColumnsNumber( value ) {
+ setAttributes( { columns: value } );
+ }
+
+ function toggleImageCrop() {
+ setAttributes( { imageCrop: ! imageCrop } );
+ }
+
+ function getImageCropHelp( checked ) {
+ return checked
+ ? __( 'Thumbnails are cropped to align.' )
+ : __( 'Thumbnails are not cropped.' );
+ }
+
+ function setImageAttributes( index, newAttributes ) {
+ if ( ! images[ index ] ) {
+ return;
+ }
+
+ setAttributes( {
+ images: [
+ ...images.slice( 0, index ),
+ {
+ ...images[ index ],
+ ...newAttributes,
+ },
+ ...images.slice( index + 1 ),
+ ],
+ } );
+ }
+
+ function getImagesSizeOptions() {
+ return map(
+ filter( imageSizes, ( { slug } ) =>
+ some( resizedImages, ( sizes ) => sizes[ slug ] )
+ ),
+ ( { name, slug } ) => ( { value: slug, label: name } )
+ );
+ }
+
+ function updateImagesSize( newSizeSlug ) {
+ const updatedImages = map( images, ( image ) => {
+ if ( ! image.id ) {
+ return image;
+ }
+ const url = get( resizedImages, [
+ parseInt( image.id, 10 ),
+ newSizeSlug,
+ ] );
+ return {
+ ...image,
+ ...( url && { url } ),
+ };
+ } );
+
+ setAttributes( { images: updatedImages, sizeSlug: newSizeSlug } );
+ }
+
+ useEffect( () => {
+ if (
+ Platform.OS === 'web' &&
+ images &&
+ images.length > 0 &&
+ every( images, ( { url } ) => isBlobURL( url ) )
+ ) {
+ const filesList = map( images, ( { url } ) => getBlobByURL( url ) );
+ forEach( images, ( { url } ) => revokeBlobURL( url ) );
+ mediaUpload( {
+ filesList,
+ onFileChange: onSelectImages,
+ allowedTypes: [ 'image' ],
+ } );
+ }
+ }, [] );
+
+ useEffect( () => {
+ // Deselect images when deselecting the block.
+ if ( ! isSelected ) {
+ setSelectedImage();
+ }
+ }, [ isSelected ] );
+
+ useEffect( () => {
+ // linkTo attribute must be saved so blocks don't break when changing
+ // image_default_link_type in options.php.
+ if ( ! linkTo ) {
+ __unstableMarkNextChangeAsNotPersistent();
+ setAttributes( {
+ linkTo:
+ window?.wp?.media?.view?.settings?.defaultProps?.link ||
+ LINK_DESTINATION_NONE,
+ } );
+ }
+ }, [ linkTo ] );
+
+ const hasImages = !! images.length;
+ const hasImageIds = hasImages && images.some( ( image ) => !! image.id );
+
+ const mediaPlaceholder = (
+
+ );
+
+ const blockProps = useBlockProps();
+
+ if ( ! hasImages ) {
+ return { mediaPlaceholder } ;
+ }
+
+ const imageSizeOptions = getImagesSizeOptions();
+ const shouldShowSizeOptions = hasImages && ! isEmpty( imageSizeOptions );
+
+ return (
+ <>
+
+
+ { images.length > 1 && (
+
+ ) }
+
+
+ { shouldShowSizeOptions && (
+
+ ) }
+
+
+ { noticeUI }
+
+ >
+ );
+}
+
+export default compose( [
+ withNotices,
+ withViewportMatch( { isNarrow: '< small' } ),
+] )( GalleryEdit );
diff --git a/packages/block-library/src/gallery/v1/gallery-button.native.js b/packages/block-library/src/gallery/v1/gallery-button.native.js
new file mode 100644
index 0000000000000..8804e99cf2e7e
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery-button.native.js
@@ -0,0 +1,47 @@
+/**
+ * External dependencies
+ */
+import { StyleSheet, TouchableOpacity } from 'react-native';
+
+/**
+ * WordPress dependencies
+ */
+import { Icon } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import style from './gallery-image-style.scss';
+
+export function Button( props ) {
+ const {
+ icon,
+ iconSize = 24,
+ onClick,
+ disabled,
+ 'aria-disabled': ariaDisabled,
+ accessibilityLabel = 'button',
+ style: customStyle,
+ } = props;
+
+ const buttonStyle = StyleSheet.compose( style.buttonActive, customStyle );
+
+ const isDisabled = disabled || ariaDisabled;
+
+ const { fill } = isDisabled ? style.buttonDisabled : style.button;
+
+ return (
+
+
+
+ );
+}
+
+export default Button;
diff --git a/packages/block-library/src/gallery/v1/gallery-image-style.native.scss b/packages/block-library/src/gallery/v1/gallery-image-style.native.scss
new file mode 100644
index 0000000000000..9b221a56981f6
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery-image-style.native.scss
@@ -0,0 +1,109 @@
+$gallery-image-container-height: 150px;
+$overlay-border-width: 2px;
+$caption-background-color: rgba(0, 0, 0, 0.4);
+
+.galleryImageContainer {
+ flex: 1;
+ height: $gallery-image-container-height;
+ overflow: hidden;
+ background-color: $gray-lighten-30;
+}
+
+.galleryImageContainerDark {
+ background-color: $gray-90;
+}
+
+.image {
+ height: 100%;
+}
+
+.button {
+ fill: $gray-0;
+ width: 30px;
+}
+
+.buttonDisabled {
+ fill: $gray-30;
+}
+
+.buttonActive {
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 6px;
+ border-color: $gray-70;
+ background-color: $gray-70;
+}
+
+.moverButtonContainer {
+ flex-direction: row;
+ align-items: center;
+ border-radius: 3px;
+ background-color: $gray-70;
+}
+
+.separator {
+ border-right-color: $gray-30;
+ border-right-width: 1px;
+ height: 20px;
+}
+
+.toolbarContainer {
+ position: absolute;
+}
+
+.toolbar {
+ padding: 5px;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.captionContainer {
+ flex: 1;
+ flex-direction: row;
+ align-items: flex-end;
+ position: absolute;
+ bottom: $overlay-border-width;
+ left: $overlay-border-width;
+ right: $overlay-border-width;
+ top: 45;
+}
+
+@mixin caption-shared {
+ font-size: 12px;
+ background-color: #0000;
+ color: #fff;
+ font-family: $default-regular-font;
+ text-align: center;
+}
+
+.caption {
+ @include caption-shared;
+ background-color: $caption-background-color;
+ padding-top: $grid-unit;
+ padding-bottom: $grid-unit;
+}
+
+.captionPlaceholder {
+ color: #ccc;
+}
+
+// expand caption container to compensate for overlay border
+.captionExpandedContainer {
+ // constrain height to gallery image height for caption scroll
+ max-height: $gallery-image-container-height;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding-top: $grid-unit;
+ padding-bottom: $overlay-border-width + $grid-unit;
+ padding-left: $overlay-border-width;
+ padding-right: $overlay-border-width;
+ // use caption background color on container when expanded
+ background-color: $caption-background-color;
+}
+
+.captionExpanded {
+ @include caption-shared;
+}
diff --git a/packages/block-library/src/gallery/v1/gallery-image.js b/packages/block-library/src/gallery/v1/gallery-image.js
new file mode 100644
index 0000000000000..9520a5ba76349
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery-image.js
@@ -0,0 +1,282 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import { get, omit } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { Component } from '@wordpress/element';
+import { Button, Spinner, ButtonGroup } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import { BACKSPACE, DELETE } from '@wordpress/keycodes';
+import { withSelect, withDispatch } from '@wordpress/data';
+import {
+ RichText,
+ MediaPlaceholder,
+ store as blockEditorStore,
+ __experimentalGetElementClassName,
+} from '@wordpress/block-editor';
+import { isBlobURL } from '@wordpress/blob';
+import { compose } from '@wordpress/compose';
+import {
+ closeSmall,
+ chevronLeft,
+ chevronRight,
+ edit,
+ image as imageIcon,
+} from '@wordpress/icons';
+import { store as coreStore } from '@wordpress/core-data';
+
+/**
+ * Internal dependencies
+ */
+import { pickRelevantMediaFiles } from './shared';
+import {
+ LINK_DESTINATION_ATTACHMENT,
+ LINK_DESTINATION_MEDIA,
+} from './constants';
+
+const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url );
+
+class GalleryImage extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.onSelectImage = this.onSelectImage.bind( this );
+ this.onRemoveImage = this.onRemoveImage.bind( this );
+ this.bindContainer = this.bindContainer.bind( this );
+ this.onEdit = this.onEdit.bind( this );
+ this.onSelectImageFromLibrary =
+ this.onSelectImageFromLibrary.bind( this );
+ this.onSelectCustomURL = this.onSelectCustomURL.bind( this );
+ this.state = {
+ isEditing: false,
+ };
+ }
+
+ bindContainer( ref ) {
+ this.container = ref;
+ }
+
+ onSelectImage() {
+ if ( ! this.props.isSelected ) {
+ this.props.onSelect();
+ }
+ }
+
+ onRemoveImage( event ) {
+ if (
+ this.container === this.container.ownerDocument.activeElement &&
+ this.props.isSelected &&
+ [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1
+ ) {
+ event.preventDefault();
+ this.props.onRemove();
+ }
+ }
+
+ onEdit() {
+ this.setState( {
+ isEditing: true,
+ } );
+ }
+
+ componentDidUpdate() {
+ const { image, url, __unstableMarkNextChangeAsNotPersistent } =
+ this.props;
+ if ( image && ! url ) {
+ __unstableMarkNextChangeAsNotPersistent();
+ this.props.setAttributes( {
+ url: image.source_url,
+ alt: image.alt_text,
+ } );
+ }
+ }
+
+ deselectOnBlur() {
+ this.props.onDeselect();
+ }
+
+ onSelectImageFromLibrary( media ) {
+ const { setAttributes, id, url, alt, caption, sizeSlug } = this.props;
+ if ( ! media || ! media.url ) {
+ return;
+ }
+
+ let mediaAttributes = pickRelevantMediaFiles( media, sizeSlug );
+
+ // If the current image is temporary but an alt text was meanwhile
+ // written by the user, make sure the text is not overwritten.
+ if ( isTemporaryImage( id, url ) ) {
+ if ( alt ) {
+ mediaAttributes = omit( mediaAttributes, [ 'alt' ] );
+ }
+ }
+
+ // If a caption text was meanwhile written by the user,
+ // make sure the text is not overwritten by empty captions.
+ if ( caption && ! get( mediaAttributes, [ 'caption' ] ) ) {
+ mediaAttributes = omit( mediaAttributes, [ 'caption' ] );
+ }
+
+ setAttributes( mediaAttributes );
+ this.setState( {
+ isEditing: false,
+ } );
+ }
+
+ onSelectCustomURL( newURL ) {
+ const { setAttributes, url } = this.props;
+ if ( newURL !== url ) {
+ setAttributes( {
+ url: newURL,
+ id: undefined,
+ } );
+ this.setState( {
+ isEditing: false,
+ } );
+ }
+ }
+
+ render() {
+ const {
+ url,
+ alt,
+ id,
+ linkTo,
+ link,
+ isFirstItem,
+ isLastItem,
+ isSelected,
+ caption,
+ onRemove,
+ onMoveForward,
+ onMoveBackward,
+ setAttributes,
+ 'aria-label': ariaLabel,
+ } = this.props;
+ const { isEditing } = this.state;
+
+ let href;
+
+ switch ( linkTo ) {
+ case LINK_DESTINATION_MEDIA:
+ href = url;
+ break;
+ case LINK_DESTINATION_ATTACHMENT:
+ href = link;
+ break;
+ }
+
+ const img = (
+ // Disable reason: Image itself is not meant to be interactive, but should
+ // direct image selection and unfocus caption fields.
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
+ <>
+
+ { isBlobURL( url ) && }
+ >
+ /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
+ );
+
+ const className = classnames( {
+ 'is-selected': isSelected,
+ 'is-transient': isBlobURL( url ),
+ } );
+
+ return (
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
+
+ { ! isEditing && ( href ? { img } : img ) }
+ { isEditing && (
+
+ ) }
+
+
+
+
+
+
+
+
+ { ! isEditing && ( isSelected || caption ) && (
+
+ setAttributes( { caption: newCaption } )
+ }
+ inlineToolbar
+ />
+ ) }
+
+ );
+ }
+}
+
+export default compose( [
+ withSelect( ( select, ownProps ) => {
+ const { getMedia } = select( coreStore );
+ const { id } = ownProps;
+
+ return {
+ image: id ? getMedia( parseInt( id, 10 ) ) : null,
+ };
+ } ),
+ withDispatch( ( dispatch ) => {
+ const { __unstableMarkNextChangeAsNotPersistent } =
+ dispatch( blockEditorStore );
+ return {
+ __unstableMarkNextChangeAsNotPersistent,
+ };
+ } ),
+] )( GalleryImage );
diff --git a/packages/block-library/src/gallery/v1/gallery-image.native.js b/packages/block-library/src/gallery/v1/gallery-image.native.js
new file mode 100644
index 0000000000000..7fa6ab4ab7ea7
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery-image.native.js
@@ -0,0 +1,349 @@
+/**
+ * External dependencies
+ */
+import {
+ StyleSheet,
+ View,
+ ScrollView,
+ TouchableWithoutFeedback,
+} from 'react-native';
+import { isEmpty } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ requestImageFailedRetryDialog,
+ requestImageUploadCancelDialog,
+ requestImageFullscreenPreview,
+} from '@wordpress/react-native-bridge';
+import { Component } from '@wordpress/element';
+import { Image } from '@wordpress/components';
+import { __, sprintf } from '@wordpress/i18n';
+import { Caption, MediaUploadProgress } from '@wordpress/block-editor';
+import { getProtocol } from '@wordpress/url';
+import { withPreferredColorScheme } from '@wordpress/compose';
+import { arrowLeft, arrowRight, warning } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import Button from './gallery-button';
+import style from './gallery-image-style.scss';
+
+const { compose } = StyleSheet;
+
+const separatorStyle = compose( style.separator, {
+ borderRightWidth: StyleSheet.hairlineWidth,
+} );
+const buttonStyle = compose( style.button, { aspectRatio: 1 } );
+const ICON_SIZE_ARROW = 15;
+
+class GalleryImage extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.onSelectImage = this.onSelectImage.bind( this );
+ this.onSelectCaption = this.onSelectCaption.bind( this );
+ this.onMediaPressed = this.onMediaPressed.bind( this );
+ this.onCaptionChange = this.onCaptionChange.bind( this );
+ this.onSelectMedia = this.onSelectMedia.bind( this );
+
+ this.updateMediaProgress = this.updateMediaProgress.bind( this );
+ this.finishMediaUploadWithSuccess =
+ this.finishMediaUploadWithSuccess.bind( this );
+ this.finishMediaUploadWithFailure =
+ this.finishMediaUploadWithFailure.bind( this );
+ this.renderContent = this.renderContent.bind( this );
+
+ this.state = {
+ captionSelected: false,
+ isUploadInProgress: false,
+ didUploadFail: false,
+ };
+ }
+
+ onSelectCaption() {
+ if ( ! this.state.captionSelected ) {
+ this.setState( {
+ captionSelected: true,
+ } );
+ }
+
+ if ( ! this.props.isSelected ) {
+ this.props.onSelect();
+ }
+ }
+
+ onMediaPressed() {
+ const { id, url, isSelected } = this.props;
+ const { captionSelected, isUploadInProgress, didUploadFail } =
+ this.state;
+
+ this.onSelectImage();
+
+ if ( isUploadInProgress ) {
+ requestImageUploadCancelDialog( id );
+ } else if (
+ didUploadFail ||
+ ( id && getProtocol( url ) === 'file:' )
+ ) {
+ requestImageFailedRetryDialog( id );
+ } else if ( isSelected && ! captionSelected ) {
+ requestImageFullscreenPreview( url );
+ }
+ }
+
+ onSelectImage() {
+ if ( ! this.props.isBlockSelected ) {
+ this.props.onSelectBlock();
+ }
+
+ if ( ! this.props.isSelected ) {
+ this.props.onSelect();
+ }
+
+ if ( this.state.captionSelected ) {
+ this.setState( {
+ captionSelected: false,
+ } );
+ }
+ }
+
+ onSelectMedia( media ) {
+ const { setAttributes } = this.props;
+ setAttributes( media );
+ }
+
+ onCaptionChange( caption ) {
+ const { setAttributes } = this.props;
+ setAttributes( { caption } );
+ }
+
+ componentDidUpdate( prevProps ) {
+ const { isSelected, image, url } = this.props;
+ if ( image && ! url ) {
+ this.props.setAttributes( {
+ url: image.source_url,
+ alt: image.alt_text,
+ } );
+ }
+
+ // Unselect the caption so when the user selects other image and comeback
+ // the caption is not immediately selected.
+ if (
+ this.state.captionSelected &&
+ ! isSelected &&
+ prevProps.isSelected
+ ) {
+ this.setState( {
+ captionSelected: false,
+ } );
+ }
+ }
+
+ updateMediaProgress() {
+ if ( ! this.state.isUploadInProgress ) {
+ this.setState( { isUploadInProgress: true } );
+ }
+ }
+
+ finishMediaUploadWithSuccess( payload ) {
+ this.setState( {
+ isUploadInProgress: false,
+ didUploadFail: false,
+ } );
+
+ this.props.setAttributes( {
+ id: payload.mediaServerId,
+ url: payload.mediaUrl,
+ } );
+ }
+
+ finishMediaUploadWithFailure() {
+ this.setState( {
+ isUploadInProgress: false,
+ didUploadFail: true,
+ } );
+ }
+
+ renderContent( params ) {
+ const {
+ url,
+ isFirstItem,
+ isLastItem,
+ isSelected,
+ caption,
+ onRemove,
+ onMoveForward,
+ onMoveBackward,
+ 'aria-label': ariaLabel,
+ isCropped,
+ getStylesFromColorScheme,
+ isRTL,
+ } = this.props;
+
+ const { isUploadInProgress, captionSelected } = this.state;
+ const { isUploadFailed, retryMessage } = params;
+ const resizeMode = isCropped ? 'cover' : 'contain';
+
+ const captionPlaceholderStyle = getStylesFromColorScheme(
+ style.captionPlaceholder,
+ style.captionPlaceholderDark
+ );
+
+ const shouldShowCaptionEditable = ! isUploadFailed && isSelected;
+ const shouldShowCaptionExpanded =
+ ! isUploadFailed && ! isSelected && !! caption;
+
+ const captionContainerStyle = shouldShowCaptionExpanded
+ ? style.captionExpandedContainer
+ : style.captionContainer;
+
+ const captionStyle = shouldShowCaptionExpanded
+ ? style.captionExpanded
+ : style.caption;
+
+ const mediaPickerOptions = [
+ {
+ destructiveButton: true,
+ id: 'removeImage',
+ label: __( 'Remove' ),
+ onPress: onRemove,
+ separated: true,
+ value: 'removeImage',
+ },
+ ];
+
+ return (
+ <>
+
+
+ { ! isUploadInProgress && isSelected && (
+
+
+
+
+
+
+
+
+
+ ) }
+
+ { ! isUploadInProgress &&
+ ( shouldShowCaptionEditable ||
+ shouldShowCaptionExpanded ) && (
+
+
+
+
+
+ ) }
+ >
+ );
+ }
+
+ render() {
+ const { id, onRemove, getStylesFromColorScheme, isSelected } =
+ this.props;
+
+ const containerStyle = getStylesFromColorScheme(
+ style.galleryImageContainer,
+ style.galleryImageContainerDark
+ );
+
+ return (
+
+
+
+
+
+ );
+ }
+
+ accessibilityLabelImageContainer() {
+ const { caption, 'aria-label': ariaLabel } = this.props;
+
+ return isEmpty( caption )
+ ? ariaLabel
+ : ariaLabel +
+ '. ' +
+ sprintf(
+ /* translators: accessibility text. %s: image caption. */
+ __( 'Image caption. %s' ),
+ caption
+ );
+ }
+}
+
+export default withPreferredColorScheme( GalleryImage );
diff --git a/packages/block-library/src/gallery/v1/gallery-styles.native.scss b/packages/block-library/src/gallery/v1/gallery-styles.native.scss
new file mode 100644
index 0000000000000..9b3169da048b2
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery-styles.native.scss
@@ -0,0 +1,8 @@
+.galleryTilesContainerSelected {
+ margin-bottom: 16px;
+}
+
+.fullWidth {
+ margin-left: $block-edge-to-content;
+ margin-right: $block-edge-to-content;
+}
diff --git a/packages/block-library/src/gallery/v1/gallery.js b/packages/block-library/src/gallery/v1/gallery.js
new file mode 100644
index 0000000000000..7fc587f27911a
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery.js
@@ -0,0 +1,125 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ RichText,
+ __experimentalGetElementClassName,
+} from '@wordpress/block-editor';
+import { VisuallyHidden } from '@wordpress/components';
+import { __, sprintf } from '@wordpress/i18n';
+import { createBlock, getDefaultBlockName } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import GalleryImage from './gallery-image';
+import { defaultColumnsNumberV1 } from '../deprecated';
+
+export const Gallery = ( props ) => {
+ const {
+ attributes,
+ isSelected,
+ setAttributes,
+ selectedImage,
+ mediaPlaceholder,
+ onMoveBackward,
+ onMoveForward,
+ onRemoveImage,
+ onSelectImage,
+ onDeselectImage,
+ onSetImageAttributes,
+ insertBlocksAfter,
+ blockProps,
+ } = props;
+
+ const {
+ align,
+ columns = defaultColumnsNumberV1( attributes ),
+ caption,
+ imageCrop,
+ images,
+ } = attributes;
+
+ return (
+
+
+ { images.map( ( img, index ) => {
+ const ariaLabel = sprintf(
+ /* translators: 1: the order number of the image. 2: the total number of images. */
+ __( 'image %1$d of %2$d in gallery' ),
+ index + 1,
+ images.length
+ );
+
+ return (
+
+
+ onSetImageAttributes( index, attrs )
+ }
+ caption={ img.caption }
+ aria-label={ ariaLabel }
+ sizeSlug={ attributes.sizeSlug }
+ />
+
+ );
+ } ) }
+
+ { mediaPlaceholder }
+ setAttributes( { caption: value } ) }
+ inlineToolbar
+ __unstableOnSplitAtEnd={ () =>
+ insertBlocksAfter( createBlock( getDefaultBlockName() ) )
+ }
+ />
+
+ );
+};
+
+function RichTextVisibilityHelper( { isHidden, ...richTextProps } ) {
+ return isHidden ? (
+
+ ) : (
+
+ );
+}
+
+export default Gallery;
diff --git a/packages/block-library/src/gallery/v1/gallery.native.js b/packages/block-library/src/gallery/v1/gallery.native.js
new file mode 100644
index 0000000000000..7908d17988a1a
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/gallery.native.js
@@ -0,0 +1,162 @@
+/**
+ * External dependencies
+ */
+import { View } from 'react-native';
+import { isEmpty } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import GalleryImage from './gallery-image';
+import { defaultColumnsNumberV1 } from '../deprecated';
+import styles from './gallery-styles.scss';
+import Tiles from './tiles';
+
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+import {
+ BlockCaption,
+ store as blockEditorStore,
+} from '@wordpress/block-editor';
+import { useState, useEffect } from '@wordpress/element';
+import { mediaUploadSync } from '@wordpress/react-native-bridge';
+import { useSelect } from '@wordpress/data';
+import { alignmentHelpers } from '@wordpress/components';
+
+const TILE_SPACING = 15;
+
+// we must limit displayed columns since readable content max-width is 580px
+const MAX_DISPLAYED_COLUMNS = 4;
+const MAX_DISPLAYED_COLUMNS_NARROW = 2;
+
+const { isFullWidth } = alignmentHelpers;
+
+export const Gallery = ( props ) => {
+ const [ isCaptionSelected, setIsCaptionSelected ] = useState( false );
+ useEffect( mediaUploadSync, [] );
+
+ const isRTL = useSelect( ( select ) => {
+ return !! select( blockEditorStore ).getSettings().isRTL;
+ }, [] );
+
+ const {
+ clientId,
+ selectedImage,
+ mediaPlaceholder,
+ onBlur,
+ onMoveBackward,
+ onMoveForward,
+ onRemoveImage,
+ onSelectImage,
+ onSetImageAttributes,
+ onFocusGalleryCaption,
+ attributes,
+ isSelected,
+ isNarrow,
+ onFocus,
+ insertBlocksAfter,
+ } = props;
+
+ const {
+ align,
+ columns = defaultColumnsNumberV1( attributes ),
+ imageCrop,
+ images,
+ } = attributes;
+
+ // limit displayed columns when isNarrow is true (i.e. when viewport width is
+ // less than "small", where small = 600)
+ const displayedColumns = isNarrow
+ ? Math.min( columns, MAX_DISPLAYED_COLUMNS_NARROW )
+ : Math.min( columns, MAX_DISPLAYED_COLUMNS );
+
+ const selectImage = ( index ) => {
+ return () => {
+ if ( isCaptionSelected ) {
+ setIsCaptionSelected( false );
+ }
+ // We need to fully invoke the curried function here.
+ onSelectImage( index )();
+ };
+ };
+
+ const focusGalleryCaption = () => {
+ if ( ! isCaptionSelected ) {
+ setIsCaptionSelected( true );
+ }
+ onFocusGalleryCaption();
+ };
+
+ return (
+
+
+ { images.map( ( img, index ) => {
+ const ariaLabel = sprintf(
+ /* translators: 1: the order number of the image. 2: the total number of images. */
+ __( 'image %1$d of %2$d in gallery' ),
+ index + 1,
+ images.length
+ );
+
+ return (
+
+ onSetImageAttributes( index, attrs )
+ }
+ caption={ img.caption }
+ aria-label={ ariaLabel }
+ isRTL={ isRTL }
+ />
+ );
+ } ) }
+
+
+ { mediaPlaceholder }
+
+
+ isEmpty( caption )
+ ? /* translators: accessibility text. Empty gallery caption. */
+ 'Gallery caption. Empty'
+ : sprintf(
+ /* translators: accessibility text. %s: gallery caption. */
+ __( 'Gallery caption. %s' ),
+ caption
+ )
+ }
+ onFocus={ focusGalleryCaption }
+ onBlur={ onBlur } // Always assign onBlur as props.
+ insertBlocksAfter={ insertBlocksAfter }
+ />
+
+ );
+};
+
+export default Gallery;
diff --git a/packages/block-library/src/gallery/v1/save.js b/packages/block-library/src/gallery/v1/save.js
new file mode 100644
index 0000000000000..bf8be9ba413b4
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/save.js
@@ -0,0 +1,98 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ RichText,
+ useBlockProps,
+ __experimentalGetElementClassName,
+} from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import { defaultColumnsNumberV1 } from '../deprecated';
+import {
+ LINK_DESTINATION_ATTACHMENT,
+ LINK_DESTINATION_MEDIA,
+} from './constants';
+
+export default function saveV1( { attributes } ) {
+ const {
+ images,
+ columns = defaultColumnsNumberV1( attributes ),
+ imageCrop,
+ caption,
+ linkTo,
+ } = attributes;
+ const className = `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }`;
+
+ return (
+
+
+ { images.map( ( image ) => {
+ let href;
+
+ switch ( linkTo ) {
+ case LINK_DESTINATION_MEDIA:
+ href = image.fullUrl || image.url;
+ break;
+ case LINK_DESTINATION_ATTACHMENT:
+ href = image.link;
+ break;
+ }
+
+ const img = (
+
+ );
+
+ return (
+
+
+ { href ? { img } : img }
+ { ! RichText.isEmpty( image.caption ) && (
+
+ ) }
+
+
+ );
+ } ) }
+
+ { ! RichText.isEmpty( caption ) && (
+
+ ) }
+
+ );
+}
diff --git a/packages/block-library/src/gallery/v1/shared.js b/packages/block-library/src/gallery/v1/shared.js
new file mode 100644
index 0000000000000..484020cb9d58c
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/shared.js
@@ -0,0 +1,19 @@
+/**
+ * External dependencies
+ */
+import { get, pick } from 'lodash';
+
+export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
+ const imageProps = pick( image, [ 'alt', 'id', 'link', 'caption' ] );
+ imageProps.url =
+ get( image, [ 'sizes', sizeSlug, 'url' ] ) ||
+ get( image, [ 'media_details', 'sizes', sizeSlug, 'source_url' ] ) ||
+ image.url;
+ const fullUrl =
+ get( image, [ 'sizes', 'full', 'url' ] ) ||
+ get( image, [ 'media_details', 'sizes', 'full', 'source_url' ] );
+ if ( fullUrl ) {
+ imageProps.fullUrl = fullUrl;
+ }
+ return imageProps;
+};
diff --git a/packages/block-library/src/gallery/v1/tiles-styles.native.scss b/packages/block-library/src/gallery/v1/tiles-styles.native.scss
new file mode 100644
index 0000000000000..c2e3d4bad7979
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/tiles-styles.native.scss
@@ -0,0 +1,11 @@
+.containerStyle {
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+
+.tileStyle {
+ overflow: hidden;
+ flex-direction: row;
+ align-items: center;
+ border-color: transparent;
+}
diff --git a/packages/block-library/src/gallery/v1/tiles.native.js b/packages/block-library/src/gallery/v1/tiles.native.js
new file mode 100644
index 0000000000000..30126ce872593
--- /dev/null
+++ b/packages/block-library/src/gallery/v1/tiles.native.js
@@ -0,0 +1,79 @@
+/**
+ * External dependencies
+ */
+import { View, StyleSheet } from 'react-native';
+
+/**
+ * WordPress dependencies
+ */
+import { Children } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import styles from './tiles-styles.scss';
+
+function Tiles( props ) {
+ const { columns, children, spacing = 10, style } = props;
+
+ const { compose } = StyleSheet;
+
+ const tileCount = Children.count( children );
+ const lastTile = tileCount - 1;
+ const lastRow = Math.floor( lastTile / columns );
+
+ const wrappedChildren = Children.map( children, ( child, index ) => {
+ /**
+ * Since we don't have `calc()`, we must calculate our spacings here in
+ * order to preserve even spacing between tiles and equal width for tiles
+ * in a given row.
+ *
+ * In order to ensure equal sizing of tile contents, we distribute the
+ * spacing such that each tile has an equal "share" of the fixed spacing. To
+ * keep the tiles properly aligned within their rows, we calculate the left
+ * and right paddings based on the tile's relative position within the row.
+ *
+ * Note: we use padding instead of margins so that the fixed spacing is
+ * included within the relative spacing (i.e. width percentage), and
+ * wrapping behavior is preserved.
+ *
+ * - The left most tile in a row must have left padding of zero.
+ * - The right most tile in a row must have a right padding of zero.
+ *
+ * The values of these left and right paddings are interpolated for tiles in
+ * between. The right padding is complementary with the left padding of the
+ * next tile (i.e. the right padding of [tile n] + the left padding of
+ * [tile n + 1] will be equal for all tiles except the last one in a given
+ * row).
+ */
+
+ const row = Math.floor( index / columns );
+ const rowLength =
+ row === lastRow ? ( lastTile % columns ) + 1 : columns;
+ const indexInRow = index % columns;
+
+ return (
+
+ { child }
+
+ );
+ } );
+
+ const containerStyle = compose( styles.containerStyle, style );
+
+ return { wrappedChildren } ;
+}
+
+export default Tiles;
From ae1cfe358558e14b91cb1dde8e760094ca9aac02 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Wed, 29 Jun 2022 11:25:35 +0300
Subject: [PATCH 07/13] maybe keep gallery for 6.0?
---
lib/compat/wordpress-6.0/block-gallery.php | 57 ++++++++++++++++++++++
lib/load.php | 1 +
2 files changed, 58 insertions(+)
create mode 100644 lib/compat/wordpress-6.0/block-gallery.php
diff --git a/lib/compat/wordpress-6.0/block-gallery.php b/lib/compat/wordpress-6.0/block-gallery.php
new file mode 100644
index 0000000000000..53c3a0c2247fd
--- /dev/null
+++ b/lib/compat/wordpress-6.0/block-gallery.php
@@ -0,0 +1,57 @@
+= 6.0.
+ *
+ * @return void.
+ */
+function gutenberg_check_gallery_block_v2_compatibility() {
+ $use_balance_tags = (int) get_option( 'use_balanceTags' );
+ $v2_gallery_enabled = boolval( 1 !== $use_balance_tags || is_wp_version_compatible( '6.0' ) ) ? 'true' : 'false';
+
+ wp_add_inline_script(
+ 'wp-dom-ready',
+ 'wp.galleryBlockV2Enabled = ' . $v2_gallery_enabled . ';',
+ 'after'
+ );
+}
+add_action( 'init', 'gutenberg_check_gallery_block_v2_compatibility' );
+
+/**
+ * Prevent use_balanceTags being enabled on WordPress 5.8 or earlier as it breaks
+ * the layout of the new Gallery block.
+ *
+ * @since 12.1.0
+ * @todo This should be removed when the minimum required WP version is >= 6.0.
+ *
+ * @param int $new_value The new value for use_balanceTags.
+ */
+function gutenberg_use_balancetags_check( $new_value ) {
+ global $wp_version;
+
+ if ( 1 === (int) $new_value && version_compare( $wp_version, '6.0', '<' ) ) {
+ /* translators: %s: Minimum required version */
+ $message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the “Correct invalidly nested XHTML automatically” option. Please upgrade WordPress before enabling.', 'gutenberg' ), '6.0' );
+ add_settings_error( 'gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
+ if ( class_exists( 'WP_CLI' ) ) {
+ WP_CLI::error( $message );
+ }
+ return 0;
+ }
+
+ return $new_value;
+}
+add_filter( 'pre_update_option_use_balanceTags', 'gutenberg_use_balancetags_check' );
diff --git a/lib/load.php b/lib/load.php
index 61d7bedba5b86..cac1d1430bc36 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -59,6 +59,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/editor-settings.php';
// WordPress 6.0 compat.
+require __DIR__ . '/compat/wordpress-6.0/block-gallery.php';
require __DIR__ . '/compat/wordpress-6.0/block-editor-settings.php';
require __DIR__ . '/compat/wordpress-6.0/get-global-styles-and-settings.php';
require __DIR__ . '/compat/wordpress-6.0/render-svg-filters.php';
From 9fd854d2d74acdc9180b509e0f215d0f4ac64ba5 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Wed, 29 Jun 2022 14:17:03 +0300
Subject: [PATCH 08/13] php tests
---
phpunit/class-wp-theme-json-test.php | 2838 ++------------------------
phpunit/global-styles-test.php | 28 -
phpunit/navigation-test.php | 94 -
3 files changed, 170 insertions(+), 2790 deletions(-)
delete mode 100644 phpunit/global-styles-test.php
delete mode 100644 phpunit/navigation-test.php
diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php
index 36f28873fc0ac..4ba9b65aa4f5b 100644
--- a/phpunit/class-wp-theme-json-test.php
+++ b/phpunit/class-wp-theme-json-test.php
@@ -8,33 +8,32 @@
class WP_Theme_JSON_Gutenberg_Test extends WP_UnitTestCase {
- function test_get_settings() {
+ function test_get_stylesheet_handles_whitelisted_element_pseudo_selectors() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => false,
- ),
- 'layout' => array(
- 'contentSize' => 'value',
- 'invalid/key' => 'value',
- ),
- 'invalid/key' => 'value',
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'custom' => false,
- ),
- 'invalid/key' => 'value',
- ),
- ),
- ),
- 'styles' => array(
+ 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+ 'styles' => array(
'elements' => array(
'link' => array(
- 'color' => array(
- 'text' => '#111',
+ 'color' => array(
+ 'text' => 'green',
+ 'background' => 'red',
+ ),
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
+ ),
+ 'typography' => array(
+ 'textTransform' => 'uppercase',
+ 'fontSize' => '10em',
+ ),
+ ),
+ ':focus' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
+ ),
),
),
),
@@ -42,78 +41,37 @@ function test_get_settings() {
)
);
- $actual = $theme_json->get_settings();
+ $base_styles = 'body { margin: 0; }.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; }';
+
+ $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:focus{background-color: black;color: yellow;}';
- $expected = array(
- 'color' => array(
- 'custom' => false,
- ),
- 'layout' => array(
- 'contentSize' => 'value',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'custom' => false,
- ),
- ),
- ),
- );
+ $expected = $base_styles . $element_styles;
- $this->assertEqualSetsWithIndex( $expected, $actual );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
}
- function test_get_settings_presets_are_keyed_by_origin() {
- $default_origin = new WP_Theme_JSON_Gutenberg(
+ function test_get_stylesheet_handles_only_psuedo_selector_rules_for_given_property() {
+ $theme_json = new WP_Theme_JSON_Gutenberg(
array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- ),
- ),
- ),
- 'invalid/key' => 'value',
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- ),
+ 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+ 'styles' => array(
+ 'elements' => array(
+ 'link' => array(
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
+ ),
+ 'typography' => array(
+ 'textTransform' => 'uppercase',
+ 'fontSize' => '10em',
),
),
- ),
- ),
- ),
- ),
- 'default'
- );
- $no_origin = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'black',
- 'color' => 'black',
- ),
- ),
- ),
- 'invalid/key' => 'value',
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'black',
- 'color' => 'black',
- ),
+ ':focus' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
),
),
),
@@ -122,94 +80,77 @@ function test_get_settings_presets_are_keyed_by_origin() {
)
);
- $actual_default = $default_origin->get_raw_data();
- $actual_no_origin = $no_origin->get_raw_data();
+ $base_styles = 'body { margin: 0; }.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; }';
+
+ $element_styles = 'a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:focus{background-color: black;color: yellow;}';
+
+ $expected = $base_styles . $element_styles;
+
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+ }
- $expected_default = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
+ function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_elements() {
+ $theme_json = new WP_Theme_JSON_Gutenberg(
+ array(
+ 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+ 'styles' => array(
+ 'elements' => array(
+ 'h4' => array(
+ 'color' => array(
+ 'text' => 'green',
+ 'background' => 'red',
),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- ),
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
),
),
- ),
- ),
- ),
- ),
- );
- $expected_no_origin = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'theme' => array(
- array(
- 'slug' => 'black',
- 'color' => 'black',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- 'theme' => array(
- array(
- 'slug' => 'black',
- 'color' => 'black',
- ),
+ ':focus' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
),
),
),
),
),
- ),
+ )
);
- $this->assertEqualSetsWithIndex( $expected_default, $actual_default );
- $this->assertEqualSetsWithIndex( $expected_no_origin, $actual_no_origin );
+ $base_styles = 'body { margin: 0; }.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; }';
+
+ $element_styles = 'h4{background-color: red;color: green;}';
+
+ $expected = $base_styles . $element_styles;
+
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
}
- function test_get_settings_appearance_true_opts_in() {
+ function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'appearanceTools' => true,
- 'spacing' => array(
- 'blockGap' => false, // This should override appearanceTools.
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'typography' => array(
- 'lineHeight' => false,
+ 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+ 'styles' => array(
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'green',
+ 'background' => 'red',
),
- ),
- 'core/group' => array(
- 'appearanceTools' => true,
- 'typography' => array(
- 'lineHeight' => false, // This should override appearanceTools.
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
+ ),
),
- 'spacing' => array(
- 'blockGap' => null,
+ ':levitate' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
+ ),
),
),
),
@@ -217,74 +158,47 @@ function test_get_settings_appearance_true_opts_in() {
)
);
- $actual = $theme_json->get_settings();
- $expected = array(
- 'border' => array(
- 'width' => true,
- 'style' => true,
- 'radius' => true,
- 'color' => true,
- ),
- 'color' => array(
- 'link' => true,
- ),
- 'spacing' => array(
- 'blockGap' => false,
- 'margin' => true,
- 'padding' => true,
- ),
- 'typography' => array(
- 'lineHeight' => true,
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- 'core/group' => array(
- 'border' => array(
- 'width' => true,
- 'style' => true,
- 'radius' => true,
- 'color' => true,
- ),
- 'color' => array(
- 'link' => true,
- ),
- 'spacing' => array(
- 'blockGap' => false,
- 'margin' => true,
- 'padding' => true,
- ),
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- ),
- );
+ $base_styles = 'body { margin: 0; }.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; }';
- $this->assertEqualSetsWithIndex( $expected, $actual );
+ $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;}';
+
+ $expected = $base_styles . $element_styles;
+
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+ $this->assertStringNotContainsString( 'a:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) );
}
- function test_get_settings_appearance_false_does_not_opt_in() {
+ function test_get_stylesheet_handles_priority_of_elements_vs_block_elements_pseudo_selectors() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'appearanceTools' => false,
- 'border' => array(
- 'width' => true,
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- 'core/group' => array(
- 'typography' => array(
- 'lineHeight' => false,
+ 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+ 'styles' => array(
+ 'blocks' => array(
+ 'core/group' => array(
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'green',
+ 'background' => 'red',
+ ),
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
+ ),
+ 'typography' => array(
+ 'textTransform' => 'uppercase',
+ 'fontSize' => '10em',
+ ),
+ ),
+ ':focus' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
+ ),
+ ),
+ ),
),
),
),
@@ -292,57 +206,45 @@ function test_get_settings_appearance_false_does_not_opt_in() {
)
);
- $actual = $theme_json->get_settings();
- $expected = array(
- 'appearanceTools' => false,
- 'border' => array(
- 'width' => true,
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- 'core/group' => array(
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- ),
- );
+ $base_styles = 'body { margin: 0; }.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; }';
- $this->assertEqualSetsWithIndex( $expected, $actual );
+ $element_styles = '.wp-block-group a{background-color: red;color: green;}.wp-block-group a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}.wp-block-group a:focus{background-color: black;color: yellow;}';
+
+ $expected = $base_styles . $element_styles;
+
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
}
- function test_get_stylesheet_support_for_shorthand_and_longhand_values() {
+ function test_get_stylesheet_handles_whitelisted_block_level_element_pseudo_selectors() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'styles' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'radius' => '10px',
- ),
- 'spacing' => array(
- 'padding' => '24px',
- 'margin' => '1em',
+ 'elements' => array(
+ 'link' => array(
+ 'color' => array(
+ 'text' => 'green',
+ 'background' => 'red',
),
- ),
- 'core/image' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '10px',
- 'bottomRight' => '1em',
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'red',
+ 'background' => 'green',
),
),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '15px',
- ),
- 'margin' => array(
- 'bottom' => '30px',
+ ),
+ ),
+ 'blocks' => array(
+ 'core/group' => array(
+ 'elements' => array(
+ 'link' => array(
+ ':hover' => array(
+ 'color' => array(
+ 'text' => 'yellow',
+ 'background' => 'black',
+ ),
+ ),
),
),
),
@@ -351,1926 +253,14 @@ function test_get_stylesheet_support_for_shorthand_and_longhand_values() {
)
);
- $styles = 'body { margin: 0; }.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-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}';
- $this->assertEquals( $styles, $theme_json->get_stylesheet() );
- $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
+ $base_styles = 'body { margin: 0; }.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; }';
- function test_get_stylesheet_skips_disabled_protected_properties() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'spacing' => array(
- 'blockGap' => null,
- ),
- ),
- 'styles' => array(
- 'spacing' => array(
- 'blockGap' => '1em',
- ),
- 'blocks' => array(
- 'core/columns' => array(
- 'spacing' => array(
- 'blockGap' => '24px',
- ),
- ),
- ),
- ),
- )
- );
-
- $expected = 'body { margin: 0; }.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; }';
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_renders_enabled_protected_properties() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'spacing' => array(
- 'blockGap' => true,
- ),
- ),
- 'styles' => array(
- 'spacing' => array(
- 'blockGap' => '1em',
- ),
- ),
- )
- );
-
- $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 ); }';
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'text' => 'value',
- 'palette' => array(
- array(
- 'slug' => 'grey',
- 'color' => 'grey',
- ),
- ),
- ),
- 'typography' => array(
- 'fontFamilies' => array(
- array(
- 'slug' => 'small',
- 'fontFamily' => '14px',
- ),
- array(
- 'slug' => 'big',
- 'fontFamily' => '41px',
- ),
- ),
- ),
- 'spacing' => array(
- 'blockGap' => false,
- ),
- 'misc' => 'value',
- 'blocks' => array(
- 'core/group' => array(
- 'custom' => array(
- 'base-font' => 16,
- 'line-height' => array(
- 'small' => 1.2,
- 'medium' => 1.4,
- 'large' => 1.8,
- ),
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'color' => array(
- 'text' => 'var:preset|color|grey',
- ),
- 'misc' => 'value',
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => '#111',
- 'background' => '#333',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'radius' => '10px',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => '#111',
- ),
- ),
- ),
- 'spacing' => array(
- 'padding' => '24px',
- ),
- ),
- 'core/heading' => array(
- 'color' => array(
- 'text' => '#123456',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => '#111',
- 'background' => '#333',
- ),
- 'typography' => array(
- 'fontSize' => '60px',
- ),
- ),
- ),
- ),
- 'core/post-date' => array(
- 'color' => array(
- 'text' => '#123456',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'background' => '#777',
- 'text' => '#555',
- ),
- ),
- ),
- ),
- 'core/image' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '10px',
- 'bottomRight' => '1em',
- ),
- ),
- 'spacing' => array(
- 'margin' => array(
- 'bottom' => '30px',
- ),
- ),
- ),
- ),
- ),
- 'misc' => 'value',
- )
- );
-
- $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;}';
- $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;}';
- $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;}';
- $all = $variables . $styles . $presets;
- $this->assertEquals( $all, $theme_json->get_stylesheet() );
- $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
- $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
- $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
- }
-
- function test_get_stylesheet_preset_classes_work_with_compounded_selectors() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'blocks' => array(
- 'core/heading' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => '#fff',
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $this->assertEquals(
- 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}',
- $theme_json->get_stylesheet( array( 'presets' ) )
- );
- }
-
- function test_get_stylesheet_preset_rules_come_after_block_rules() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'grey',
- 'color' => 'grey',
- ),
- ),
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- )
- );
-
- $styles = 'body { margin: 0; }.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-block-group{color: red;}';
- $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}';
- $variables = '.wp-block-group{--wp--preset--color--grey: grey;}';
- $all = $variables . $styles . $presets;
- $this->assertEquals( $all, $theme_json->get_stylesheet() );
- $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
- $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
- $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
- }
-
- function test_get_stylesheet_generates_proper_classes_and_css_vars_from_slugs() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'grey',
- 'color' => 'grey',
- ),
- array(
- 'slug' => 'dark grey',
- 'color' => 'grey',
- ),
- array(
- 'slug' => 'light-grey',
- 'color' => 'grey',
- ),
- array(
- 'slug' => 'white2black',
- 'color' => 'grey',
- ),
- ),
- ),
- 'custom' => array(
- 'white2black' => 'value',
- ),
- ),
- )
- );
-
- $this->assertEquals(
- '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}',
- $theme_json->get_stylesheet( array( 'presets' ) )
- );
- $this->assertEquals(
- 'body{--wp--preset--color--grey: grey;--wp--preset--color--dark-grey: grey;--wp--preset--color--light-grey: grey;--wp--preset--color--white-2-black: grey;--wp--custom--white-2-black: value;}',
- $theme_json->get_stylesheet( array( 'variables' ) )
- );
-
- }
-
- public function test_get_stylesheet_preset_values_are_marked_as_important() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'grey',
- 'color' => 'grey',
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'blue',
- ),
- 'typography' => array(
- 'fontSize' => '12px',
- 'lineHeight' => '1.3',
- ),
- ),
- ),
- ),
- ),
- 'default'
- );
-
- $this->assertEquals(
- 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.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; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.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;}',
- $theme_json->get_stylesheet()
- );
- }
-
- function test_get_stylesheet_handles_whitelisted_element_pseudo_selectors() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'green',
- 'background' => 'red',
- ),
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- 'typography' => array(
- 'textTransform' => 'uppercase',
- 'fontSize' => '10em',
- ),
- ),
- ':focus' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:focus{background-color: black;color: yellow;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_handles_only_psuedo_selector_rules_for_given_property() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'elements' => array(
- 'link' => array(
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- 'typography' => array(
- 'textTransform' => 'uppercase',
- 'fontSize' => '10em',
- ),
- ),
- ':focus' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = 'a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:focus{background-color: black;color: yellow;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_elements() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'elements' => array(
- 'h4' => array(
- 'color' => array(
- 'text' => 'green',
- 'background' => 'red',
- ),
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- ),
- ':focus' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = 'h4{background-color: red;color: green;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'green',
- 'background' => 'red',
- ),
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- ),
- ':levitate' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- $this->assertStringNotContainsString( 'a:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_handles_priority_of_elements_vs_block_elements_pseudo_selectors() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'green',
- 'background' => 'red',
- ),
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- 'typography' => array(
- 'textTransform' => 'uppercase',
- 'fontSize' => '10em',
- ),
- ),
- ':focus' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = '.wp-block-group a{background-color: red;color: green;}.wp-block-group a:hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}.wp-block-group a:focus{background-color: black;color: yellow;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- function test_get_stylesheet_handles_whitelisted_block_level_element_pseudo_selectors() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'green',
- 'background' => 'red',
- ),
- ':hover' => array(
- 'color' => array(
- 'text' => 'red',
- 'background' => 'green',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'elements' => array(
- 'link' => array(
- ':hover' => array(
- 'color' => array(
- 'text' => 'yellow',
- 'background' => 'black',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $base_styles = 'body { margin: 0; }.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; }';
-
- $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;}.wp-block-group a:hover{background-color: black;color: yellow;}';
-
- $expected = $base_styles . $element_styles;
-
- $this->assertEquals( $expected, $theme_json->get_stylesheet() );
- $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
- }
-
- public function test_merge_incoming_data() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => false,
- 'palette' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'custom' => false,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'typography' => array(
- 'fontSize' => '12',
- ),
- ),
- )
- );
-
- $add_new_block = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'blocks' => array(
- 'core/list' => array(
- 'color' => array(
- 'custom' => false,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'blocks' => array(
- 'core/list' => array(
- 'typography' => array(
- 'fontSize' => '12',
- ),
- 'color' => array(
- 'background' => 'brown',
- ),
- ),
- ),
- ),
- );
-
- $add_key_in_settings = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'customGradient' => true,
- ),
- ),
- );
-
- $update_key_in_settings = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => true,
- ),
- ),
- );
-
- $add_styles = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '12px',
- ),
- ),
- ),
- ),
- ),
- );
-
- $add_key_in_styles = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'blocks' => array(
- 'core/group' => array(
- 'spacing' => array(
- 'padding' => array(
- 'bottom' => '12px',
- ),
- ),
- ),
- ),
- ),
- );
-
- $add_invalid_context = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'blocks' => array(
- 'core/para' => array(
- 'typography' => array(
- 'lineHeight' => '12',
- ),
- ),
- ),
- ),
- );
-
- $update_presets = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- ),
- ),
- 'gradients' => array(
- array(
- 'slug' => 'gradient',
- 'gradient' => 'gradient',
- ),
- ),
- ),
- 'typography' => array(
- 'fontSizes' => array(
- array(
- 'slug' => 'fontSize',
- 'size' => 'fontSize',
- ),
- ),
- 'fontFamilies' => array(
- array(
- 'slug' => 'fontFamily',
- 'fontFamily' => 'fontFamily',
- ),
- ),
- ),
- ),
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => true,
- 'customGradient' => true,
- 'palette' => array(
- 'theme' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- ),
- ),
- ),
- 'gradients' => array(
- 'theme' => array(
- array(
- 'slug' => 'gradient',
- 'gradient' => 'gradient',
- ),
- ),
- ),
- ),
- 'typography' => array(
- 'fontSizes' => array(
- 'theme' => array(
- array(
- 'slug' => 'fontSize',
- 'size' => 'fontSize',
- ),
- ),
- ),
- 'fontFamilies' => array(
- 'theme' => array(
- array(
- 'slug' => 'fontFamily',
- 'fontFamily' => 'fontFamily',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'custom' => false,
- ),
- ),
- 'core/list' => array(
- 'color' => array(
- 'custom' => false,
- ),
- ),
- ),
- ),
- 'styles' => array(
- 'typography' => array(
- 'fontSize' => '12',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '12px',
- 'bottom' => '12px',
- ),
- ),
- ),
- 'core/list' => array(
- 'typography' => array(
- 'fontSize' => '12',
- ),
- 'color' => array(
- 'background' => 'brown',
- ),
- ),
- ),
- ),
- );
-
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_new_block ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_key_in_settings ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $update_key_in_settings ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_styles ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_key_in_styles ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_invalid_context ) );
- $theme_json->merge( new WP_Theme_JSON_Gutenberg( $update_presets ) );
- $actual = $theme_json->get_raw_data();
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- public function test_merge_incoming_data_empty_presets() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'duotone' => array(
- array(
- 'slug' => 'value',
- 'colors' => array( 'red', 'green' ),
- ),
- ),
- 'gradients' => array(
- array(
- 'slug' => 'gradient',
- 'gradient' => 'gradient',
- ),
- ),
- 'palette' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- ),
- ),
- ),
- 'spacing' => array(
- 'units' => array( 'px', 'em' ),
- ),
- 'typography' => array(
- 'fontSizes' => array(
- array(
- 'slug' => 'size',
- 'value' => 'size',
- ),
- ),
- ),
- ),
- )
- );
-
- $theme_json->merge(
- new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'duotone' => array(),
- 'gradients' => array(),
- 'palette' => array(),
- ),
- 'spacing' => array(
- 'units' => array(),
- ),
- 'typography' => array(
- 'fontSizes' => array(),
- ),
- ),
- )
- )
- );
-
- $actual = $theme_json->get_raw_data();
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'duotone' => array(
- 'theme' => array(),
- ),
- 'gradients' => array(
- 'theme' => array(),
- ),
- 'palette' => array(
- 'theme' => array(),
- ),
- ),
- 'spacing' => array(
- 'units' => array(),
- ),
- 'typography' => array(
- 'fontSizes' => array(
- 'theme' => array(),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- public function test_merge_incoming_data_null_presets() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'duotone' => array(
- array(
- 'slug' => 'value',
- 'colors' => array( 'red', 'green' ),
- ),
- ),
- 'gradients' => array(
- array(
- 'slug' => 'gradient',
- 'gradient' => 'gradient',
- ),
- ),
- 'palette' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- ),
- ),
- ),
- 'spacing' => array(
- 'units' => array( 'px', 'em' ),
- ),
- 'typography' => array(
- 'fontSizes' => array(
- array(
- 'slug' => 'size',
- 'value' => 'size',
- ),
- ),
- ),
- ),
- )
- );
-
- $theme_json->merge(
- new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => false,
- ),
- 'spacing' => array(
- 'margin' => false,
- ),
- 'typography' => array(
- 'lineHeight' => false,
- ),
- ),
- )
- )
- );
-
- $actual = $theme_json->get_raw_data();
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => false,
- 'duotone' => array(
- 'theme' => array(
- array(
- 'slug' => 'value',
- 'colors' => array( 'red', 'green' ),
- ),
- ),
- ),
- 'gradients' => array(
- 'theme' => array(
- array(
- 'slug' => 'gradient',
- 'gradient' => 'gradient',
- ),
- ),
- ),
- 'palette' => array(
- 'theme' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- ),
- ),
- ),
- ),
- 'spacing' => array(
- 'margin' => false,
- 'units' => array( 'px', 'em' ),
- ),
- 'typography' => array(
- 'lineHeight' => false,
- 'fontSizes' => array(
- 'theme' => array(
- array(
- 'slug' => 'size',
- 'value' => 'size',
- ),
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- public function test_merge_incoming_data_color_presets_with_same_slugs_as_default_are_removed() {
- $defaults = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'defaultPalette' => true,
- 'palette' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- 'name' => 'Red',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Green',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Blue',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- 'default'
- );
- $theme = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'pink',
- 'color' => 'pink',
- 'name' => 'Pink',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Greenish',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Bluish',
- ),
- array(
- 'slug' => 'yellow',
- 'color' => 'yellow',
- 'name' => 'Yellow',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Block Green',
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- 'name' => 'Red',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Green',
- ),
- ),
- 'theme' => array(
- array(
- 'slug' => 'pink',
- 'color' => 'pink',
- 'name' => 'Pink',
- ),
- ),
- ),
- 'defaultPalette' => true,
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Blue',
- ),
- ),
- 'theme' => array(
- array(
- 'slug' => 'yellow',
- 'color' => 'yellow',
- 'name' => 'Yellow',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
-
- $defaults->merge( $theme );
- $actual = $defaults->get_raw_data();
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- public function test_merge_incoming_data_color_presets_with_same_slugs_as_default_are_not_removed_if_defaults_are_disabled() {
- $defaults = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'defaultPalette' => true, // Emulate the defaults from core theme.json.
- 'palette' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- 'name' => 'Red',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Green',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Blue',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- 'default'
- );
- $theme = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'defaultPalette' => false,
- 'palette' => array(
- array(
- 'slug' => 'pink',
- 'color' => 'pink',
- 'name' => 'Pink',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Greenish',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Bluish',
- ),
- array(
- 'slug' => 'yellow',
- 'color' => 'yellow',
- 'name' => 'Yellow',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Block Green',
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'defaultPalette' => false,
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'red',
- 'color' => 'red',
- 'name' => 'Red',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Green',
- ),
- ),
- 'theme' => array(
- array(
- 'slug' => 'pink',
- 'color' => 'pink',
- 'name' => 'Pink',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Greenish',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/paragraph' => array(
- 'color' => array(
- 'palette' => array(
- 'default' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Blue',
- ),
- ),
- 'theme' => array(
- array(
- 'slug' => 'blue',
- 'color' => 'blue',
- 'name' => 'Bluish',
- ),
- array(
- 'slug' => 'yellow',
- 'color' => 'yellow',
- 'name' => 'Yellow',
- ),
- array(
- 'slug' => 'green',
- 'color' => 'green',
- 'name' => 'Block Green',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
-
- $defaults->merge( $theme );
- $actual = $defaults->get_raw_data();
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- public function test_merge_incoming_data_presets_use_default_names() {
- $defaults = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'typography' => array(
- 'fontSizes' => array(
- array(
- 'name' => 'Small',
- 'slug' => 'small',
- 'size' => '12px',
- ),
- array(
- 'name' => 'Large',
- 'slug' => 'large',
- 'size' => '20px',
- ),
- ),
- ),
- ),
- ),
- 'default'
- );
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'typography' => array(
- 'fontSizes' => array(
- array(
- 'slug' => 'small',
- 'size' => '1.1rem',
- ),
- array(
- 'slug' => 'large',
- 'size' => '1.75rem',
- ),
- array(
- 'name' => 'Huge',
- 'slug' => 'huge',
- 'size' => '3rem',
- ),
- ),
- ),
- ),
- ),
- 'theme'
- );
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'typography' => array(
- 'fontSizes' => array(
- 'theme' => array(
- array(
- 'name' => 'Small',
- 'slug' => 'small',
- 'size' => '1.1rem',
- ),
- array(
- 'name' => 'Large',
- 'slug' => 'large',
- 'size' => '1.75rem',
- ),
- array(
- 'name' => 'Huge',
- 'slug' => 'huge',
- 'size' => '3rem',
- ),
- ),
- 'default' => array(
- array(
- 'name' => 'Small',
- 'slug' => 'small',
- 'size' => '12px',
- ),
- array(
- 'name' => 'Large',
- 'slug' => 'large',
- 'size' => '20px',
- ),
- ),
- ),
- ),
- ),
- );
- $defaults->merge( $theme_json );
- $actual = $defaults->get_raw_data();
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
-
-
- function test_remove_insecure_properties_removes_unsafe_styles() {
- $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'color' => array(
- 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
- 'text' => 'var:preset|color|dark-red',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
- 'text' => 'var:preset|color|dark-pink',
- 'background' => 'var:preset|color|dark-red',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/image' => array(
- 'filter' => array(
- 'duotone' => 'var:preset|duotone|blue-red',
- ),
- ),
- 'core/cover' => array(
- 'filter' => array(
- 'duotone' => 'var(--wp--preset--duotone--blue-red, var(--fallback-unsafe))',
- ),
- ),
- 'core/group' => array(
- 'color' => array(
- 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
- 'text' => 'var:preset|color|dark-gray',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
- 'text' => 'var:preset|color|dark-pink',
- ),
- ),
- ),
- ),
- 'invalid/key' => array(
- 'background' => 'green',
- ),
- ),
- ),
- )
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'color' => array(
- 'text' => 'var:preset|color|dark-red',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'var:preset|color|dark-pink',
- 'background' => 'var:preset|color|dark-red',
- ),
- ),
- ),
- 'blocks' => array(
- 'core/image' => array(
- 'filter' => array(
- 'duotone' => 'var:preset|duotone|blue-red',
- ),
- ),
- 'core/group' => array(
- 'color' => array(
- 'text' => 'var:preset|color|dark-gray',
- ),
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'var:preset|color|dark-pink',
- ),
- ),
- ),
- ),
- ),
- ),
- );
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() {
- $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '6px',
- 'topRight' => 'var(--top-right, var(--unsafe-fallback))',
- 'bottomRight' => '6px',
- 'bottomLeft' => '6px',
- ),
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '1px',
- 'right' => '1px',
- 'bottom' => 'var(--bottom, var(--unsafe-fallback))',
- 'left' => '1px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '2px',
- 'right' => '2px',
- 'bottom' => 'var(--bottom, var(--unsafe-fallback))',
- 'left' => '2px',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '5px',
- 'topRight' => 'var(--top-right, var(--unsafe-fallback))',
- 'bottomRight' => '5px',
- 'bottomLeft' => '5px',
- ),
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '3px',
- 'right' => '3px',
- 'bottom' => 'var(bottom, var(--unsafe-fallback))',
- 'left' => '3px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '4px',
- 'right' => '4px',
- 'bottom' => 'var(--bottom, var(--unsafe-fallback))',
- 'left' => '4px',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- true
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '6px',
- 'bottomRight' => '6px',
- 'bottomLeft' => '6px',
- ),
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '1px',
- 'right' => '1px',
- 'left' => '1px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '2px',
- 'right' => '2px',
- 'left' => '2px',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'border' => array(
- 'radius' => array(
- 'topLeft' => '5px',
- 'bottomRight' => '5px',
- 'bottomLeft' => '5px',
- ),
- ),
- 'spacing' => array(
- 'padding' => array(
- 'top' => '3px',
- 'right' => '3px',
- 'left' => '3px',
- ),
- ),
- 'elements' => array(
- 'link' => array(
- 'spacing' => array(
- 'padding' => array(
- 'top' => '4px',
- 'right' => '4px',
- 'left' => '4px',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_remove_insecure_properties_removes_non_preset_settings() {
- $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => true,
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Red',
- 'slug' => 'red',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Green',
- 'slug' => 'green',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Blue',
- 'slug' => 'blue',
- 'color' => '#0000ff',
- ),
- ),
- ),
- ),
- 'spacing' => array(
- 'padding' => false,
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'custom' => true,
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Yellow',
- 'slug' => 'yellow',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Orange',
- 'slug' => 'orange',
- 'color' => '#0000ff',
- ),
- ),
- ),
- ),
- 'spacing' => array(
- 'padding' => false,
- ),
- ),
- ),
- ),
- )
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Red',
- 'slug' => 'red',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Green',
- 'slug' => 'green',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Blue',
- 'slug' => 'blue',
- 'color' => '#0000ff',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Yellow',
- 'slug' => 'yellow',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Orange',
- 'slug' => 'orange',
- 'color' => '#0000ff',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_remove_insecure_properties_removes_unsafe_preset_settings() {
- $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Red/>ok',
- 'slug' => 'red',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Green',
- 'slug' => 'a" attr',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Blue',
- 'slug' => 'blue',
- 'color' => 'var(--color, var(--unsafe-fallback))',
- ),
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#FFC0CB',
- ),
- ),
- ),
- ),
- 'typography' => array(
- 'fontFamilies' => array(
- 'custom' => array(
- array(
- 'name' => 'Helvetica Arial/>test ',
- 'slug' => 'helvetica-arial',
- 'fontFamily' => 'Helvetica Neue, Helvetica, Arial, sans-serif',
- ),
- array(
- 'name' => 'Geneva',
- 'slug' => 'geneva#asa',
- 'fontFamily' => 'Geneva, Tahoma, Verdana, sans-serif',
- ),
- array(
- 'name' => 'Cambria',
- 'slug' => 'cambria',
- 'fontFamily' => 'Cambria, Georgia, serif',
- ),
- array(
- 'name' => 'Helvetica Arial',
- 'slug' => 'helvetica-arial',
- 'fontFamily' => 'var(--fontFamily, var(--unsafe-fallback))',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Red/>ok',
- 'slug' => 'red',
- 'color' => '#ff0000',
- ),
- array(
- 'name' => 'Green',
- 'slug' => 'a" attr',
- 'color' => '#00ff00',
- ),
- array(
- 'name' => 'Blue',
- 'slug' => 'blue',
- 'color' => 'var(--color, var(--unsafe--fallback))',
- ),
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#FFC0CB',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- )
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#FFC0CB',
- ),
- ),
- ),
- ),
- 'typography' => array(
- 'fontFamilies' => array(
- 'custom' => array(
- array(
- 'name' => 'Cambria',
- 'slug' => 'cambria',
- 'fontFamily' => 'Cambria, Georgia, serif',
- ),
- ),
- ),
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'color' => array(
- 'palette' => array(
- 'custom' => array(
- array(
- 'name' => 'Pink',
- 'slug' => 'pink',
- 'color' => '#FFC0CB',
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
+ $element_styles = 'a{background-color: red;color: green;}a:hover{background-color: green;color: red;}.wp-block-group a:hover{background-color: black;color: yellow;}';
- function test_remove_insecure_properties_applies_safe_styles() {
- $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
- array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'color' => array(
- 'text' => '#abcabc ', // Trailing space.
- ),
- ),
- ),
- true
- );
+ $expected = $base_styles . $element_styles;
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'styles' => array(
- 'color' => array(
- 'text' => '#abcabc ',
- ),
- ),
- );
- $this->assertEqualSetsWithIndex( $expected, $actual );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+ $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
}
function test_remove_invalid_element_pseudo_selectors() {
@@ -2320,494 +310,6 @@ function test_remove_invalid_element_pseudo_selectors() {
$this->assertEqualSetsWithIndex( $expected, $actual );
}
- function test_get_custom_templates() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'customTemplates' => array(
- array(
- 'name' => 'page-home',
- 'title' => 'Homepage template',
- ),
- ),
- )
- );
-
- $page_templates = $theme_json->get_custom_templates();
-
- $this->assertEqualSetsWithIndex(
- $page_templates,
- array(
- 'page-home' => array(
- 'title' => 'Homepage template',
- 'postTypes' => array( 'page' ),
- ),
- )
- );
- }
-
- function test_get_template_parts() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'templateParts' => array(
- array(
- 'name' => 'small-header',
- 'title' => 'Small Header',
- 'area' => 'header',
- ),
- ),
- )
- );
-
- $template_parts = $theme_json->get_template_parts();
-
- $this->assertEqualSetsWithIndex(
- $template_parts,
- array(
- 'small-header' => array(
- 'title' => 'Small Header',
- 'area' => 'header',
- ),
- )
- );
- }
-
- function test_get_from_editor_settings() {
- $input = array(
- 'disableCustomColors' => true,
- 'disableCustomGradients' => true,
- 'disableCustomFontSizes' => true,
- 'enableCustomLineHeight' => true,
- 'enableCustomUnits' => true,
- 'colors' => array(
- array(
- 'slug' => 'color-slug',
- 'name' => 'Color Name',
- 'color' => 'colorvalue',
- ),
- ),
- 'gradients' => array(
- array(
- 'slug' => 'gradient-slug',
- 'name' => 'Gradient Name',
- 'gradient' => 'gradientvalue',
- ),
- ),
- 'fontSizes' => array(
- array(
- 'slug' => 'size-slug',
- 'name' => 'Size Name',
- 'size' => 'sizevalue',
- ),
- ),
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => false,
- 'customGradient' => false,
- 'gradients' => array(
- array(
- 'slug' => 'gradient-slug',
- 'name' => 'Gradient Name',
- 'gradient' => 'gradientvalue',
- ),
- ),
- 'palette' => array(
- array(
- 'slug' => 'color-slug',
- 'name' => 'Color Name',
- 'color' => 'colorvalue',
- ),
- ),
- ),
- 'spacing' => array(
- 'units' => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ),
- ),
- 'typography' => array(
- 'customFontSize' => false,
- 'lineHeight' => true,
- 'fontSizes' => array(
- array(
- 'slug' => 'size-slug',
- 'name' => 'Size Name',
- 'size' => 'sizevalue',
- ),
- ),
- ),
- ),
- );
-
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( $input );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_get_editor_settings_no_theme_support() {
- $input = array(
- '__unstableEnableFullSiteEditingBlocks' => false,
- 'disableCustomColors' => false,
- 'disableCustomFontSizes' => false,
- 'disableCustomGradients' => false,
- 'enableCustomLineHeight' => false,
- 'enableCustomUnits' => false,
- 'imageSizes' => array(
- array(
- 'slug' => 'thumbnail',
- 'name' => 'Thumbnail',
- ),
- array(
- 'slug' => 'medium',
- 'name' => 'Medium',
- ),
- array(
- 'slug' => 'large',
- 'name' => 'Large',
- ),
- array(
- 'slug' => 'full',
- 'name' => 'Full Size',
- ),
- ),
- 'isRTL' => false,
- 'maxUploadFileSize' => 123,
- );
-
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(
- 'color' => array(
- 'custom' => true,
- 'customGradient' => true,
- ),
- 'spacing' => array(
- 'units' => false,
- ),
- 'typography' => array(
- 'customFontSize' => true,
- 'lineHeight' => false,
- ),
- ),
- );
-
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( $input );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_get_editor_settings_blank() {
- $expected = array(
- 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
- 'settings' => array(),
- );
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( array() );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_get_editor_settings_custom_units_can_be_disabled() {
- add_theme_support( 'custom-units', array() );
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_default_block_editor_settings() );
- remove_theme_support( 'custom-units' );
-
- $expected = array(
- 'units' => array( array() ),
- 'padding' => false,
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
- }
-
- function test_get_editor_settings_custom_units_can_be_enabled() {
- add_theme_support( 'custom-units' );
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_default_block_editor_settings() );
- remove_theme_support( 'custom-units' );
-
- $expected = array(
- 'units' => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ),
- 'padding' => false,
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
- }
-
- function test_get_editor_settings_custom_units_can_be_filtered() {
- add_theme_support( 'custom-units', 'rem', 'em' );
- $actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_default_block_editor_settings() );
- remove_theme_support( 'custom-units' );
-
- $expected = array(
- 'units' => array( 'rem', 'em' ),
- 'padding' => false,
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
- }
-
- function test_sanitization() {
- $theme_json = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'styles' => array(
- 'spacing' => array(
- 'blockGap' => 'valid value',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'spacing' => array(
- 'margin' => 'valid value',
- 'blockGap' => 'invalid value',
- ),
- ),
- ),
- ),
- )
- );
-
- $actual = $theme_json->get_raw_data();
- $expected = array(
- 'version' => 2,
- 'styles' => array(
- 'spacing' => array(
- 'blockGap' => 'valid value',
- ),
- 'blocks' => array(
- 'core/group' => array(
- 'spacing' => array(
- 'margin' => 'valid value',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_export_data() {
- $theme = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- 'label' => 'White',
- ),
- array(
- 'slug' => 'black',
- 'color' => 'black',
- 'label' => 'Black',
- ),
- ),
- ),
- ),
- )
- );
- $user = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => '#fff',
- 'label' => 'User White',
- ),
- array(
- 'slug' => 'hotpink',
- 'color' => 'hotpink',
- 'label' => 'hotpink',
- ),
- ),
- ),
- ),
- ),
- 'custom'
- );
-
- $theme->merge( $user );
- $actual = $theme->get_data();
- $expected = array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => '#fff',
- 'label' => 'User White',
- ),
- array(
- 'slug' => 'black',
- 'color' => 'black',
- 'label' => 'Black',
- ),
- array(
- 'slug' => 'hotpink',
- 'color' => 'hotpink',
- 'label' => 'hotpink',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_export_data_deals_with_empty_user_data() {
- $theme = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- 'label' => 'White',
- ),
- array(
- 'slug' => 'black',
- 'color' => 'black',
- 'label' => 'Black',
- ),
- ),
- ),
- ),
- )
- );
-
- $actual = $theme->get_data();
- $expected = array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => 'white',
- 'label' => 'White',
- ),
- array(
- 'slug' => 'black',
- 'color' => 'black',
- 'label' => 'Black',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_export_data_deals_with_empty_theme_data() {
- $user = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => '#fff',
- 'label' => 'User White',
- ),
- array(
- 'slug' => 'hotpink',
- 'color' => 'hotpink',
- 'label' => 'hotpink',
- ),
- ),
- ),
- ),
- ),
- 'custom'
- );
-
- $actual = $user->get_data();
- $expected = array(
- 'version' => 2,
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'white',
- 'color' => '#fff',
- 'label' => 'User White',
- ),
- array(
- 'slug' => 'hotpink',
- 'color' => 'hotpink',
- 'label' => 'hotpink',
- ),
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
- function test_export_data_deals_with_empty_data() {
- $theme_v2 = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- ),
- 'theme'
- );
- $actual_v2 = $theme_v2->get_data();
- $expected_v2 = array( 'version' => 2 );
- $this->assertEqualSetsWithIndex( $expected_v2, $actual_v2 );
-
- $theme_v1 = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 1,
- ),
- 'theme'
- );
- $actual_v1 = $theme_v1->get_data();
- $expected_v1 = array( 'version' => 2 );
- $this->assertEqualSetsWithIndex( $expected_v1, $actual_v1 );
- }
-
- function test_export_data_sets_appearance_tools() {
- $theme = new WP_Theme_JSON_Gutenberg(
- array(
- 'version' => 2,
- 'settings' => array(
- 'appearanceTools' => true,
- 'blocks' => array(
- 'core/paragraph' => array(
- 'appearanceTools' => true,
- ),
- ),
- ),
- )
- );
-
- $actual = $theme->get_data();
- $expected = array(
- 'version' => 2,
- 'settings' => array(
- 'appearanceTools' => true,
- 'blocks' => array(
- 'core/paragraph' => array(
- 'appearanceTools' => true,
- ),
- ),
- ),
- );
-
- $this->assertEqualSetsWithIndex( $expected, $actual );
- }
-
function test_get_element_class_name_button() {
$expected = 'wp-element-button';
$actual = WP_Theme_JSON_Gutenberg::get_element_class_name( 'button' );
diff --git a/phpunit/global-styles-test.php b/phpunit/global-styles-test.php
deleted file mode 100644
index 39712522293db..0000000000000
--- a/phpunit/global-styles-test.php
+++ /dev/null
@@ -1,28 +0,0 @@
-assertEquals( $user_theme_json, $filtered_user_theme_json );
- }
-}
diff --git a/phpunit/navigation-test.php b/phpunit/navigation-test.php
deleted file mode 100644
index d10f8cd24b835..0000000000000
--- a/phpunit/navigation-test.php
+++ /dev/null
@@ -1,94 +0,0 @@
-enable_editor_support();
- }
-
- public function tearDown() {
- $this->enable_editor_support();
- }
-
- public function test_it_doesnt_disable_block_editor_for_non_navigation_post_types() {
- $filtered_result = gutenberg_disable_block_editor_for_navigation_post_type( true, static::NON_NAVIGATION_POST_TYPE );
- $this->assertTrue( $filtered_result );
- }
-
- public function test_it_disables_block_editor_for_navigation_post_types() {
- $filtered_result = gutenberg_disable_block_editor_for_navigation_post_type( true, static::NAVIGATION_POST_TYPE );
- $this->assertFalse( $filtered_result );
- }
-
- public function test_it_doesnt_disable_content_editor_for_non_navigation_type_posts() {
- $post = $this->create_non_navigation_post();
- $this->assertTrue( $this->supports_block_editor() );
-
- gutenberg_disable_content_editor_for_navigation_post_type( $post );
-
- $this->assertTrue( $this->supports_block_editor() );
- }
-
- public function test_it_disables_content_editor_for_navigation_type_posts() {
- $post = $this->create_navigation_post();
- $this->assertTrue( $this->supports_block_editor() );
-
- gutenberg_disable_content_editor_for_navigation_post_type( $post );
-
- $this->assertFalse( $this->supports_block_editor() );
- }
-
- public function test_it_enables_content_editor_for_non_navigation_type_posts_after_the_content_editor_form() {
- $this->disable_editor_support();
- $post = $this->create_navigation_post();
- $this->assertFalse( $this->supports_block_editor() );
-
- gutenberg_enable_content_editor_for_navigation_post_type( $post );
-
- $this->assertTrue( $this->supports_block_editor() );
- }
-
- public function test_it_doesnt_enable_content_editor_for_non_navigation_type_posts_after_the_content_editor_form() {
- $this->disable_editor_support();
- $post = $this->create_non_navigation_post();
- $this->assertFalse( $this->supports_block_editor() );
-
- gutenberg_enable_content_editor_for_navigation_post_type( $post );
-
- $this->assertFalse( $this->supports_block_editor() );
- }
-
- private function create_post( $type ) {
- $post = new WP_Post( new StdClass() );
- $post->post_type = $type;
- $post->filter = 'raw';
- return $post;
- }
-
- private function create_non_navigation_post() {
- return $this->create_post( static::NON_NAVIGATION_POST_TYPE );
- }
-
- private function create_navigation_post() {
- return $this->create_post( static::NAVIGATION_POST_TYPE );
- }
-
- private function supports_block_editor() {
- return post_type_supports( static::NAVIGATION_POST_TYPE, 'editor' );
- }
-
- private function enable_editor_support() {
- add_post_type_support( static::NAVIGATION_POST_TYPE, 'editor' );
- }
-
- private function disable_editor_support() {
- remove_post_type_support( static::NAVIGATION_POST_TYPE, 'editor' );
- }
-}
From 4073ce91a73b659877f7202ab0e3ad15497c9aa8 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Wed, 29 Jun 2022 15:24:12 +0300
Subject: [PATCH 09/13] remove more tests that have been back ported
---
phpunit/class-elements-test.php | 107 ------------------
phpunit/functions-test.php | 91 ---------------
.../translate-settings-using-i18n-schema.php | 97 ----------------
3 files changed, 295 deletions(-)
delete mode 100644 phpunit/class-elements-test.php
delete mode 100644 phpunit/functions-test.php
delete mode 100644 phpunit/translate-settings-using-i18n-schema.php
diff --git a/phpunit/class-elements-test.php b/phpunit/class-elements-test.php
deleted file mode 100644
index 0d0c11c55d5ed..0000000000000
--- a/phpunit/class-elements-test.php
+++ /dev/null
@@ -1,107 +0,0 @@
-Hello WordPress !
',
- array(
- 'blockName' => 'core/paragraph',
- 'attrs' => array(
- 'style' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'var:preset|color|subtle-background',
- ),
- ),
- ),
- ),
- ),
- )
- )
- );
- $this->assertSame(
- $result,
- 'Hello WordPress !
'
- );
- }
-
- /**
- * Test gutenberg_render_elements_support() with a paragraph containing a class.
- */
- public function test_class_paragraph_link_color() {
- $result = self::make_unique_id_one(
- gutenberg_render_elements_support(
- 'Hello WordPress !
',
- array(
- 'blockName' => 'core/paragraph',
- 'attrs' => array(
- 'style' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => 'red',
- ),
- ),
- ),
- ),
- 'backgroundColor' => 'dark-gray',
- ),
- )
- )
- );
- $this->assertSame(
- $result,
- 'Hello WordPress !
'
- );
- }
-
- /**
- * Test gutenberg_render_elements_support() with a paragraph containing a anchor.
- */
- public function test_anchor_paragraph_link_color() {
- $result = self::make_unique_id_one(
- gutenberg_render_elements_support(
- 'Hello WordPress !
',
- array(
- 'blockName' => 'core/paragraph',
- 'attrs' => array(
- 'style' => array(
- 'elements' => array(
- 'link' => array(
- 'color' => array(
- 'text' => '#fff000',
- ),
- ),
- ),
- ),
- ),
- )
- )
- );
- $this->assertSame(
- $result,
- 'Hello WordPress !
'
- );
- }
-
-}
diff --git a/phpunit/functions-test.php b/phpunit/functions-test.php
deleted file mode 100644
index 87dcf0c7e77ee..0000000000000
--- a/phpunit/functions-test.php
+++ /dev/null
@@ -1,91 +0,0 @@
- 1,
- 'settings' => array(
- 'typography' => array(
- 'fontFamilies' => array(
- 'fontFamily' => 'DM Sans, sans-serif',
- 'slug' => 'dm-sans',
- 'name' => 'DM Sans',
- ),
- ),
- 'color' => array(
- 'palette' => array(
- array(
- 'slug' => 'foreground',
- 'color' => '#242321',
- 'name' => 'Foreground',
- ),
- array(
- 'slug' => 'background',
- 'color' => '#FCFBF8',
- 'name' => 'Background',
- ),
- array(
- 'slug' => 'primary',
- 'color' => '#71706E',
- 'name' => 'Primary',
- ),
- array(
- 'slug' => 'tertiary',
- 'color' => '#CFCFCF',
- 'name' => 'Tertiary',
- ),
- ),
- ),
- ),
- );
-
- // Sort the array.
- wp_recursive_ksort( $theme_json );
-
- // Expected result.
- $expected_theme_json = array(
- 'settings' => array(
- 'color' => array(
- 'palette' => array(
- array(
- 'color' => '#242321',
- 'name' => 'Foreground',
- 'slug' => 'foreground',
- ),
- array(
- 'color' => '#FCFBF8',
- 'name' => 'Background',
- 'slug' => 'background',
- ),
- array(
- 'color' => '#71706E',
- 'name' => 'Primary',
- 'slug' => 'primary',
- ),
- array(
- 'color' => '#CFCFCF',
- 'name' => 'Tertiary',
- 'slug' => 'tertiary',
- ),
- ),
- ),
- 'typography' => array(
- 'fontFamilies' => array(
- 'fontFamily' => 'DM Sans, sans-serif',
- 'name' => 'DM Sans',
- 'slug' => 'dm-sans',
- ),
- ),
- ),
- 'version' => 1,
- );
- $this->assertEquals( $theme_json, $expected_theme_json );
- }
-}
diff --git a/phpunit/translate-settings-using-i18n-schema.php b/phpunit/translate-settings-using-i18n-schema.php
deleted file mode 100644
index f91cd84d6dad8..0000000000000
--- a/phpunit/translate-settings-using-i18n-schema.php
+++ /dev/null
@@ -1,97 +0,0 @@
- 'block title',
- 'keywords' => array( 'block keyword' ),
- 'styles' => array(
- (object) array( 'label' => 'block style label' ),
- ),
- 'context' => (object) array(
- '*' => (object) array(
- 'variations' => array(
- (object) array(
- 'title' => 'block variation title',
- 'description' => 'block variation description',
- 'keywords' => array( 'block variation keyword' ),
- ),
- ),
- ),
- ),
- );
- $settings = array(
- 'title' => 'Notice',
- 'keywords' => array(
- 'alert',
- 'message',
- ),
- 'styles' => array(
- array( 'label' => 'Default' ),
- array( 'label' => 'Other' ),
- ),
- 'context' => array(
- 'namespace' => array(
- 'variations' => array(
- array(
- 'title' => 'Error',
- 'description' => 'Shows error.',
- 'keywords' => array( 'failure' ),
- ),
- ),
- ),
- ),
- );
- $result = translate_settings_using_i18n_schema(
- $i18n_schema,
- $settings,
- $textdomain
- );
-
- unload_textdomain( $textdomain );
- remove_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) );
-
- $this->assertSame( 'Powiadomienie', $result['title'] );
- $this->assertSameSets( array( 'ostrzeżenie', 'wiadomość' ), $result['keywords'] );
- $this->assertSame(
- array(
- array(
- 'label' => 'Domyślny',
- ),
- array(
- 'label' => 'Inny',
- ),
- ),
- $result['styles']
- );
- $this->assertSame(
- array(
- array(
- 'title' => 'Błąd',
- 'description' => 'Wyświetla błąd.',
- 'keywords' => array( 'niepowodzenie' ),
- ),
- ),
- $result['context']['namespace']['variations']
- );
- }
-}
From 04499011299e6591b3acf09f7810f66aee76b670 Mon Sep 17 00:00:00 2001
From: Ben Dwyer
Date: Thu, 30 Jun 2022 12:21:29 +0100
Subject: [PATCH 10/13] Add a class variable for core data on the 6.1 resolver
---
.../wordpress-6.1/class-wp-theme-json-resolver-6-1.php | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
index efb2025d6f4dd..310889c04e26f 100644
--- a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
+++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
@@ -16,6 +16,15 @@
* @access private
*/
class WP_Theme_JSON_Resolver_6_1 extends WP_Theme_JSON_Resolver_6_0 {
+
+ /**
+ * Container for data coming from core.
+ *
+ * @since 5.8.0
+ * @var WP_Theme_JSON
+ */
+ public static $core = null;
+
/**
* Given a theme.json structure modifies it in place
* to update certain values by its translated strings
From cad0e84155868652c2a8e1e260abe6403cffab3f Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Thu, 30 Jun 2022 14:32:55 +0300
Subject: [PATCH 11/13] tweaks
---
.../class-wp-theme-json-resolver-6-0.php | 17 -----------------
.../class-wp-theme-json-resolver-6-1.php | 2 +-
2 files changed, 1 insertion(+), 18 deletions(-)
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
index 13e00171ce082..b85e8e335037a 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php
@@ -38,23 +38,6 @@ protected static function translate( $theme_json, $domain = 'default' ) {
return translate_settings_using_i18n_schema( static::$i18n_schema, $theme_json, $domain );
}
- /**
- * Return core's origin config.
- *
- * @return WP_Theme_JSON_Gutenberg Entity that holds core data.
- */
- public static function get_core_data() {
- if ( null !== static::$core ) {
- return static::$core;
- }
-
- $config = static::read_json_file( __DIR__ . '/theme.json' );
- $config = static::translate( $config );
- static::$core = new WP_Theme_JSON_Gutenberg( $config, 'default' );
-
- return static::$core;
- }
-
/**
* Returns the theme's data.
*
diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
index 310889c04e26f..f704daaffafb1 100644
--- a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
+++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
@@ -23,7 +23,7 @@ class WP_Theme_JSON_Resolver_6_1 extends WP_Theme_JSON_Resolver_6_0 {
* @since 5.8.0
* @var WP_Theme_JSON
*/
- public static $core = null;
+ protected static $core = null;
/**
* Given a theme.json structure modifies it in place
From 5992cdc61ecc9b6c0b3f78d086c29114557bd477 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Fri, 1 Jul 2022 10:41:29 +0300
Subject: [PATCH 12/13] remove navigation test
---
.../specs/editor/blocks/navigation.test.js | 36 -------------------
1 file changed, 36 deletions(-)
diff --git a/packages/e2e-tests/specs/editor/blocks/navigation.test.js b/packages/e2e-tests/specs/editor/blocks/navigation.test.js
index 63af9e346d78c..ee75374afdf53 100644
--- a/packages/e2e-tests/specs/editor/blocks/navigation.test.js
+++ b/packages/e2e-tests/specs/editor/blocks/navigation.test.js
@@ -1317,42 +1317,6 @@ Expected mock function not to be called but it was called with: ["POST", "http:/
await switchUserToAdmin();
} );
- it( 'shows a warning if user does not have permission to edit or update navigation menus', async () => {
- await createNewPost();
- await insertBlock( 'Navigation' );
-
- const startEmptyButton = await page.waitForXPath(
- START_EMPTY_XPATH
- );
-
- // This creates an empty Navigation post type entity.
- await startEmptyButton.click();
-
- // Publishing the Post ensures the Navigation entity is saved.
- // The Post itself is irrelevant.
- await publishPost();
-
- // Switch to a Contributor role user - they should not have
- // permission to update Navigation menus.
- await loginUser( contributorUsername, contributorPassword );
-
- await createNewPost();
-
- // At this point the block will automatically pick the first Navigation Menu
- // which will be the one created by the Admin User.
- await insertBlock( 'Navigation' );
-
- // Make sure the snackbar error shows up.
- await page.waitForXPath(
- `//*[contains(@class, 'components-snackbar__content')][ text()="You do not have permission to edit this Menu. Any changes made will not be saved." ]`
- );
-
- // Expect a console 403 for requests to:
- // * /wp/v2/settings?_locale=user
- // * /wp/v2/templates?context=edit&post_type=post&per_page=100&_locale=user
- expect( console ).toHaveErrored();
- } );
-
it( 'shows a warning if user does not have permission to create navigation menus', async () => {
const noticeText =
'You do not have permission to create Navigation Menus.';
From 051807dc6f7319fa301930e28ad586b52f945da9 Mon Sep 17 00:00:00 2001
From: ntsekouras
Date: Fri, 1 Jul 2022 11:45:43 +0300
Subject: [PATCH 13/13] in 5.9 we default to `0` for site-logo setting in REST
---
packages/block-library/src/site-logo/edit.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/block-library/src/site-logo/edit.js b/packages/block-library/src/site-logo/edit.js
index a48e9e28546ff..c7a97219983f7 100644
--- a/packages/block-library/src/site-logo/edit.js
+++ b/packages/block-library/src/site-logo/edit.js
@@ -533,7 +533,7 @@ export default function LogoEdit( {
{ !! logoUrl && logoImage }
{ ! logoUrl && ! canUserEdit && (
- { isLoading && (
+ { !! isLoading && (