From 91338339c3ef2be29783d6b09f4d3a7fced1f938 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:10:05 +0100 Subject: [PATCH 1/7] Editor: Register the core entity fields on the server. Copies the PHP of the `core-fields` package from the Gutenberg build and loads it, so WordPress registers its own fields through the entity fields API: the default fields every post type derives from its supports, and the field collections of pages, templates, template parts, patterns, attachments, navigation menus, and the site. Synced from https://github.com/WordPress/gutenberg/pull/84696. See #66229. --- Gruntfile.js | 4 +- src/wp-includes/entity-fields.php | 4 +- src/wp-settings.php | 3 + tests/phpunit/tests/entity-fields/base.php | 6 +- .../wpRegisterCoreFieldCollections.php | 732 ++++++++++++++++++ 5 files changed, 745 insertions(+), 4 deletions(-) create mode 100644 tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php diff --git a/Gruntfile.js b/Gruntfile.js index b720379a23101..e2ad7d0a074b0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -697,7 +697,7 @@ module.exports = function(grunt) { src: 'vendor/composer/ca-bundle/res/cacert.pem', dest: SOURCE_DIR + 'wp-includes/certificates/ca-bundle.crt' }, - // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/). + // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/, core fields). 'gutenberg-php': { options: { process: function( content ) { @@ -716,6 +716,8 @@ module.exports = function(grunt) { 'pages.php', 'constants.php', 'pages/**/*.php', + // The fields Core registers through the entity fields API. + 'scripts/core-fields/**/*.php', ], dest: SOURCE_DIR + 'wp-includes/build/', } ], diff --git a/src/wp-includes/entity-fields.php b/src/wp-includes/entity-fields.php index fff833b6788b8..91a0547557a3e 100644 --- a/src/wp-includes/entity-fields.php +++ b/src/wp-includes/entity-fields.php @@ -19,7 +19,9 @@ * Fields can also be declared as files, grouped in a field collection, which * a `wp_entity_fields_api_init` callback registers with * {@see wp_register_entity_field_collection()}. A collection is for a single - * entity. + * entity. WordPress core registers its own fields this way, from + * packages/core-fields: the defaults every post type derives from its + * supports in code, the fields of single post types as collections. * * The registered fields are exposed by the `/wp/v2/entity-fields` REST API * route, see {@see WP_REST_Entity_Fields_Controller}. diff --git a/src/wp-settings.php b/src/wp-settings.php index 98ecc5781917f..4ae9f164b0178 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -254,6 +254,9 @@ if ( file_exists( ABSPATH . WPINC . '/build/pages.php' ) ) { require ABSPATH . WPINC . '/build/pages.php'; } +if ( file_exists( ABSPATH . WPINC . '/build/scripts/core-fields/index.php' ) ) { + require ABSPATH . WPINC . '/build/scripts/core-fields/index.php'; +} require ABSPATH . WPINC . '/taxonomy.php'; require ABSPATH . WPINC . '/class-wp-taxonomy.php'; require ABSPATH . WPINC . '/class-wp-term.php'; diff --git a/tests/phpunit/tests/entity-fields/base.php b/tests/phpunit/tests/entity-fields/base.php index b95d57897a7ce..5ef0d39f0cddf 100644 --- a/tests/phpunit/tests/entity-fields/base.php +++ b/tests/phpunit/tests/entity-fields/base.php @@ -27,12 +27,14 @@ abstract class WP_Entity_Fields_UnitTestCase extends WP_UnitTestCase { * Sets up each test. * * Registers the fixture defaults, see register_default_fields(), at - * priority 0, before the callbacks of the tests, and resets the registry - * so its next read fires `wp_entity_fields_api_init` anew. + * priority 0, before the callbacks of the tests, in place of the fields + * WordPress core registers, and resets the registry so its next read + * fires `wp_entity_fields_api_init` anew. */ public function set_up() { parent::set_up(); + remove_action( 'wp_entity_fields_api_init', 'wp_register_core_field_collections', 0 ); add_action( 'wp_entity_fields_api_init', array( static::class, 'register_default_fields' ), 0 ); self::reset_registry(); } diff --git a/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php b/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php new file mode 100644 index 0000000000000..199be49d5b89c --- /dev/null +++ b/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php @@ -0,0 +1,732 @@ +theme_features = $_wp_theme_features; + remove_action( 'wp_entity_fields_api_init', array( static::class, 'register_default_fields' ), 0 ); + add_action( 'wp_entity_fields_api_init', 'wp_register_core_field_collections', 0 ); + self::reset_registry(); + } + + /** + * Tears down each test. + * + * Restores the theme features a test changed. + */ + public function tear_down() { + global $_wp_theme_features; + + $_wp_theme_features = $this->theme_features; + + parent::tear_down(); + } + + /** + * Returns the fields registered on an entity, keyed by id. + * + * @param string $kind The entity kind. + * @param string $name The entity name. + * @return array[] The field definitions, keyed by id. + */ + private static function get_fields( $kind, $name ) { + return array_column( wp_get_registered_entity_fields( $kind, $name ), null, 'id' ); + } + + /** + * Returns the ids of the fields registered on a post type. + * + * @param string $post_type The post type. + * @return string[] The ids, in registration order. + */ + private static function get_field_ids( $post_type ) { + return array_column( wp_get_registered_entity_fields( 'postType', $post_type ), 'id' ); + } + + /** + * Returns the ids of the fields registered on a post type, but for the + * defaults every post type gets, see EVERY_POST_TYPE_FIELDS. + * + * @param string $post_type The post type. + * @return string[] The ids, in registration order. + */ + private static function get_support_field_ids( $post_type ) { + return array_values( array_diff( self::get_field_ids( $post_type ), self::EVERY_POST_TYPE_FIELDS ) ); + } + + /** + * The core fields are registered on `wp_entity_fields_api_init` at + * priority 0, before the plugins hooking the action at the default + * priority. + * + * @ticket 66229 + */ + public function test_the_core_fields_are_registered_at_priority_zero() { + // The hooks as WordPress sets them up, before the other entity fields + // tests replace the core fields with their fixture defaults. + $hook = self::$hooks_saved['wp_filter']['wp_entity_fields_api_init']; + + $this->assertSame( 0, $hook->has_filter( 'wp_entity_fields_api_init', 'wp_register_core_field_collections' ), 'The core fields are registered at priority 0.' ); + } + + /** + * The script module of the default fields is declared as a dependency + * of the editor script, since the author field of every post type + * supporting authors ships its JavaScript parts in it. + * + * @ticket 66229 + */ + public function test_the_default_fields_module_is_a_dependency_of_the_editor_script() { + $scripts = new WP_Scripts(); + $scripts->add( 'wp-editor', '/editor.js' ); + $scripts->enqueue( 'wp-editor' ); + $scripts->add_data( 'wp-editor', 'module_dependencies', array() ); + + _wp_add_entity_field_modules_to_editor_script( $scripts ); + + $this->assertContains( + array( + 'id' => self::POST_TYPE_SUPPORTS, + 'import' => 'dynamic', + ), + $scripts->get_data( 'wp-editor', 'module_dependencies' ), + 'The module of the default fields is a dynamic dependency of the editor script.' + ); + } + + /** + * The script modules of the core fields are registered, so they reach + * the import map. + * + * @ticket 66229 + */ + public function test_the_script_modules_of_the_core_fields_are_registered() { + foreach ( wp_get_all_registered_entity_field_modules() as $kind => $entities ) { + foreach ( $entities as $name => $modules ) { + foreach ( $modules as $module ) { + $this->assertNotNull( wp_script_modules()->get_registered( $module ), "The $module module of $kind $name is registered." ); + } + } + } + } + + /** + * The labels are translated with the default text domain of WordPress. + * + * @ticket 66229 + */ + public function test_the_labels_are_translated_with_the_default_text_domain() { + $domains = array(); + add_filter( + 'gettext', + static function ( $translation, $text, $domain ) use ( &$domains ) { + if ( 'Discussion' === $text ) { + $domains[] = $domain; + } + return $translation; + }, + 10, + 3 + ); + + self::reset_registry(); + wp_get_registered_entity_fields( 'postType', 'post' ); + + $this->assertSame( array( 'default' ), array_unique( $domains ), 'The label of the discussion field is translated in the default text domain.' ); + } + + /** + * Any post type supporting authors, comments, or notes, including a + * custom one, gets the default fields: the defaults derive from the post + * types registered when the action fires. + * + * @ticket 66229 + */ + public function test_a_custom_post_type_gets_the_default_fields_of_its_supports() { + $this->register_post_types( + array( + 'test_book' => array( + 'title', + 'author', + 'comments', + 'editor' => array( 'notes' => true ), + ), + ) + ); + self::reset_registry(); + + $fields = wp_get_registered_entity_fields( 'postType', 'test_book' ); + + $this->assertSame( array( 'author', 'comment_status', 'discussion', 'notesCount', 'post-content-info', 'title' ), self::get_support_field_ids( 'test_book' ), 'The post type gets the fields of its supports.' ); + $this->assertSame( array( 'core' ), array_unique( array_column( array_column( $fields, 'origin' ), 'registeredBy' ) ), 'The fields carry the origin of WordPress core.' ); + $this->assertSame( + array( self::POST_TYPE_SUPPORTS => array_column( $fields, 'id' ) ), + wp_get_registered_entity_field_modules( 'postType', 'test_book' ), + 'Every default field is registered with the module of its folder.' + ); + } + + /** + * The notes field follows the `notes` argument of the `editor` support, + * which WordPress stores as a list of argument arrays; a bare `editor` + * support does not enable it. + * + * @ticket 66229 + */ + public function test_the_notes_field_follows_the_editor_support_argument() { + $this->register_post_types( + array( + 'test_book' => array( 'editor' => array( 'notes' => true ) ), + 'test_note' => array( 'editor' ), + ) + ); + self::reset_registry(); + + $this->assertContains( 'notesCount', self::get_field_ids( 'test_book' ), 'The post type with notes gets the field.' ); + $this->assertNotContains( 'notesCount', self::get_field_ids( 'test_note' ), 'The post type without notes does not get the field.' ); + } + + /** + * Data provider. + * + * @return array[] The id of each field and whether the design post types + * exclude it. + */ + public function data_default_fields_of_every_post_type() { + return array( + 'date' => array( 'date', true ), + 'last_edited_date' => array( 'last_edited_date', false ), + 'password' => array( 'password', true ), + 'scheduled_date' => array( 'scheduled_date', true ), + 'status' => array( 'status', true ), + 'template' => array( 'template', true ), + ); + } + + /** + * A default field of every post type is registered on a post type + * supporting nothing, and on the design post types unless they exclude + * it. + * + * @ticket 66229 + * + * @dataProvider data_default_fields_of_every_post_type + * + * @param string $field_id The id of the field. + * @param bool $excluded_for_design Whether the design post types exclude it. + */ + public function test_a_default_field_of_every_post_type( $field_id, $excluded_for_design ) { + $this->register_post_types( array( 'test_plain' => array( 'title' ) ) ); + self::reset_registry(); + + $this->assertContains( $field_id, self::get_field_ids( 'test_plain' ), 'A post type supporting only titles gets the field.' ); + foreach ( array( 'wp_template', 'wp_template_part', 'wp_block', 'wp_navigation' ) as $post_type ) { + if ( $excluded_for_design ) { + $this->assertNotContains( $field_id, self::get_field_ids( $post_type ), "$post_type excludes the field." ); + } else { + $this->assertContains( $field_id, self::get_field_ids( $post_type ), "$post_type gets the field." ); + } + } + } + + /** + * Data provider. + * + * @return array[] The id of each field and the supports that give it. + */ + public function data_default_fields_and_their_supports() { + return array( + 'discussion' => array( 'discussion', array( 'comments', 'trackbacks' ) ), + 'excerpt' => array( 'excerpt', array( 'excerpt' ) ), + 'parent' => array( 'parent', array( 'page-attributes' ) ), + 'ping_status' => array( 'ping_status', array( 'trackbacks' ) ), + 'title' => array( 'title', array( 'title' ) ), + 'post-content-info' => array( 'post-content-info', array( 'editor' ) ), + ); + } + + /** + * A default field is registered on the post types with one of its + * supports, and only on them. + * + * @ticket 66229 + * + * @dataProvider data_default_fields_and_their_supports + * + * @param string $field_id The id of the field. + * @param string[] $supports The supports that give the field. + */ + public function test_a_default_field_follows_its_supports( $field_id, $supports ) { + $with_support = array(); + foreach ( $supports as $index => $support ) { + $with_support[ "test_with_$index" ] = array( 'title', $support ); + } + $this->register_post_types( $with_support + array( 'test_without' => array( 'author' ) ) ); + self::reset_registry(); + + foreach ( array_keys( $with_support ) as $post_type ) { + $this->assertContains( $field_id, self::get_field_ids( $post_type ), "$post_type gets the field." ); + } + $this->assertNotContains( $field_id, self::get_field_ids( 'test_without' ), 'A post type without the supports does not get the field.' ); + } + + /** + * The default fields come from the `field.php` files of the + * `post_type_supports` folder. + * + * @ticket 66229 + */ + public function test_the_default_fields_come_from_the_post_type_supports_folder() { + $fields = self::get_fields( 'postType', 'post' ); + + $this->assertSame( 'integer', $fields['author']['type'], 'The author field is the post author.' ); + $this->assertSame( 'core', $fields['author']['origin']['registeredBy'], 'The author field is registered by WordPress core.' ); + $this->assertSame( 'radio', $fields['comment_status']['Edit'], 'The comment status field is edited with radio buttons.' ); + $this->assertSame( 'boolean', $fields['ping_status']['type'], 'The ping status field is a boolean.' ); + $this->assertSame( 'Discussion', $fields['discussion']['label'], 'The discussion field has its label.' ); + $this->assertSame( 'textarea', $fields['excerpt']['Edit']['control'], 'The excerpt field is edited in a textarea.' ); + $this->assertTrue( $fields['post-content-info']['readOnly'], 'The content information field is read-only.' ); + } + + /** + * The defaults make no exception for any post type: on their own, they + * register the author field on templates, template parts, and + * attachments, which support authors. + * + * @ticket 66229 + */ + public function test_the_defaults_make_no_exception() { + remove_action( 'wp_entity_fields_api_init', 'wp_register_core_field_collections', 0 ); + $this->on_entity_fields_api_init( 'wp_register_core_post_type_supports_fields', 0 ); + + foreach ( array( 'wp_template', 'wp_template_part', 'attachment' ) as $post_type ) { + $fields = self::get_fields( 'postType', $post_type ); + $this->assertArrayHasKey( 'author', $fields, "The defaults register the author field on $post_type." ); + $this->assertSame( 'integer', $fields['author']['type'], "The default author field of $post_type is the post author." ); + } + } + + /** + * Pages support titles but their title shows the page's role on the + * site: they get the title field of their collection, with its own + * script module, instead of the default one. + * + * @ticket 66229 + */ + public function test_pages_get_their_own_title_field() { + $fields = self::get_fields( 'postType', 'page' ); + $modules = wp_get_registered_entity_field_modules( 'postType', 'page' ); + + $this->assertFalse( $fields['title']['enableHiding'], 'The page title is the one of the collection.' ); + $this->assertSame( 'core', $fields['title']['origin']['registeredBy'], 'The page title is registered by WordPress core.' ); + $this->assertSame( array( 'title' ), $modules['@wordpress/core-fields/page'], 'The page title ships its JavaScript parts in the module of the collection.' ); + $this->assertNotContains( 'title', $modules[ self::POST_TYPE_SUPPORTS ], 'The default title field is not registered.' ); + } + + /** + * Templates support authors but their author is the theme, plugin, site, + * or user that provides them: they get the fields of their collection, + * with its own script module, instead of the default author and title + * fields. + * + * @ticket 66229 + */ + public function test_templates_get_their_own_fields() { + $ids = array( 'author', 'default_comment_status', 'description', 'description_readonly', 'posts_page_title', 'posts_per_page', 'title' ); + $fields = self::get_fields( 'postType', 'wp_template' ); + + $this->assertTrue( post_type_supports( 'wp_template', 'author' ), 'The post type supports authors.' ); + $this->assertSame( $ids, self::get_support_field_ids( 'wp_template' ), 'The fields are the ones of the collection.' ); + $this->assertSame( 'Template', $fields['title']['label'], 'The title is the one of the collection.' ); + $this->assertArrayNotHasKey( 'type', $fields['author'], 'The template author is not the integer post author.' ); + $this->assertSame( 'core', $fields['author']['origin']['registeredBy'], 'The template author is registered by WordPress core.' ); + $this->assertSame( + $ids, + wp_get_registered_entity_field_modules( 'postType', 'wp_template' )['@wordpress/core-fields/wp_template'], + 'The fields of templates ship their JavaScript parts in the module of the collection.' + ); + } + + /** + * Template parts support authors but their author is the theme, plugin, + * site, or user that provides them: they get the fields of their + * collection, with its own script module, instead of the default author + * and title fields. + * + * @ticket 66229 + */ + public function test_template_parts_get_their_own_fields() { + $fields = self::get_fields( 'postType', 'wp_template_part' ); + + $this->assertTrue( post_type_supports( 'wp_template_part', 'author' ), 'The post type supports authors.' ); + $this->assertSame( array( 'author', 'title' ), self::get_support_field_ids( 'wp_template_part' ), 'The fields are the ones of the collection.' ); + $this->assertArrayNotHasKey( 'type', $fields['author'], 'The template part author is not the integer post author.' ); + $this->assertSame( 'Title', $fields['title']['label'], 'The title is the one of the collection.' ); + $this->assertSame( + array( 'author', 'title' ), + wp_get_registered_entity_field_modules( 'postType', 'wp_template_part' )['@wordpress/core-fields/wp_template_part'], + 'The fields of template parts ship their JavaScript parts in the module of the collection.' + ); + } + + /** + * Patterns have their description, title, and sync status in their + * collection instead of the default excerpt and title fields. + * + * @ticket 66229 + */ + public function test_patterns_get_their_own_fields() { + $fields = self::get_fields( 'postType', 'wp_block' ); + $modules = wp_get_registered_entity_field_modules( 'postType', 'wp_block' ); + + $this->assertTrue( post_type_supports( 'wp_block', 'excerpt' ), 'The post type supports excerpts.' ); + $this->assertSame( 'Description', $fields['excerpt']['label'], 'The excerpt is the description of the collection.' ); + $this->assertSame( 'Title', $fields['title']['label'], 'The title is the one of the collection.' ); + $this->assertTrue( $fields['sync-status']['readOnly'], 'The sync status is read-only.' ); + $this->assertSame( array( 'excerpt', 'sync-status', 'title' ), $modules['@wordpress/core-fields/wp_block'], 'The fields of patterns ship their JavaScript parts in the module of the collection.' ); + $this->assertNotContains( 'excerpt', $modules[ self::POST_TYPE_SUPPORTS ], 'The default excerpt field is not registered.' ); + } + + /** + * Patterns are sorted on the client, where the id of their author is not + * a useful order, so their author field cannot sort them. + * + * @ticket 66229 + */ + public function test_patterns_cannot_be_sorted_by_author() { + $author = self::get_fields( 'postType', 'wp_block' )['author']; + + $this->assertFalse( $author['enableSorting'], 'The author of patterns cannot sort them.' ); + $this->assertSame( + array( + 'registeredBy' => 'core', + 'updatedBy' => array( 'core' ), + ), + $author['origin'], + 'WordPress core updates the default author field of patterns.' + ); + } + + /** + * Navigation menus support the editor, but their content is blocks + * laying out a site rather than text to read, so they do not get the + * content information field. Patterns keep it. + * + * @ticket 66229 + */ + public function test_navigation_menus_do_not_get_the_content_information_field() { + $this->assertTrue( post_type_supports( 'wp_navigation', 'editor' ), 'The post type supports the editor.' ); + $this->assertNotContains( 'post-content-info', self::get_field_ids( 'wp_navigation' ), 'Navigation menus do not get the field.' ); + $this->assertContains( 'post-content-info', self::get_field_ids( 'wp_block' ), 'Patterns get the field.' ); + } + + /** + * Attachments support authors and comments but the media editor has its + * own fields, so they get the media fields of their collection, with its + * script module, instead of every default field. + * + * @ticket 66229 + */ + public function test_attachments_get_the_media_fields_instead_of_the_defaults() { + $ids = array( 'alt_text', 'attached_to', 'author', 'caption', 'date', 'description', 'filename', 'filesize', 'media_dimensions', 'media_thumbnail', 'mime_type', 'modified', 'title' ); + $fields = self::get_fields( 'postType', 'attachment' ); + + $this->assertTrue( post_type_supports( 'attachment', 'author' ), 'The post type supports authors.' ); + $this->assertTrue( post_type_supports( 'attachment', 'comments' ), 'The post type supports comments.' ); + $this->assertSame( $ids, array_keys( $fields ), 'Only the media fields are registered.' ); + $this->assertSame( 'datetime', $fields['date']['type'], 'The date is the one of the collection.' ); + $this->assertSame( 'core', $fields['date']['origin']['registeredBy'], 'The media fields are registered by WordPress core.' ); + $this->assertSame( + array( '@wordpress/core-fields/attachment' => $ids ), + wp_get_registered_entity_field_modules( 'postType', 'attachment' ), + 'Every media field is registered with the module of the collection.' + ); + } + + /** + * The site is not a post type: its collection has all of its fields, + * the ones the identity screen of the site editor shows, with its + * script module. + * + * @ticket 66229 + */ + public function test_the_site_gets_the_fields_of_its_collection() { + $ids = array( 'description', 'site_icon', 'site_logo', 'title' ); + $fields = self::get_fields( 'root', 'site' ); + + $this->assertSame( $ids, array_keys( $fields ), 'The fields are the ones of the collection.' ); + $this->assertSame( 'text', $fields['title']['type'], 'The title of the site is text.' ); + $this->assertSame( 'media', $fields['site_logo']['type'], 'The logo of the site is media.' ); + $this->assertSame( 'core', $fields['title']['origin']['registeredBy'], 'The fields of the site are registered by WordPress core.' ); + $this->assertSame( + array( '@wordpress/core-fields/root_site' => $ids ), + wp_get_registered_entity_field_modules( 'root', 'site' ), + 'Every field of the site is registered with the module of the collection.' + ); + } + + /** + * The slug field is registered on the viewable post types, which have a + * permalink, but for the design ones. + * + * @ticket 66229 + */ + public function test_the_viewable_post_types_get_the_slug_field() { + register_post_type( + 'test_book', + array( + 'public' => true, + 'show_in_rest' => true, + ) + ); + register_post_type( + 'test_note', + array( + 'public' => false, + 'show_in_rest' => true, + ) + ); + self::reset_registry(); + + $this->assertContains( 'slug', self::get_field_ids( 'test_book' ), 'A viewable post type gets the field.' ); + $this->assertNotContains( 'slug', self::get_field_ids( 'test_note' ), 'A post type that is not viewable has no permalink.' ); + $this->assertContains( 'slug', self::get_field_ids( 'page' ), 'Pages get the field.' ); + $this->assertNotContains( 'slug', self::get_field_ids( 'wp_template' ), 'Templates do not get the field.' ); + } + + /** + * Only posts have sticky posts, so only they get the sticky field. + * + * @ticket 66229 + */ + public function test_only_posts_get_the_sticky_field() { + $this->register_post_types( array( 'test_book' => array( 'title', 'editor' ) ) ); + self::reset_registry(); + + $this->assertContains( 'sticky', self::get_field_ids( 'post' ), 'Posts get the field.' ); + $this->assertNotContains( 'sticky', self::get_field_ids( 'page' ), 'Pages do not get the field.' ); + $this->assertNotContains( 'sticky', self::get_field_ids( 'test_book' ), 'Custom post types do not get the field.' ); + } + + /** + * The featured media field needs the post type to support `thumbnail` + * and the theme to support post thumbnails for that post type, which a + * theme may opt into for some post types only. + * + * @ticket 66229 + */ + public function test_the_featured_media_field_follows_the_post_type_support_and_the_theme() { + $this->register_post_types( + array( + 'test_book' => array( 'title', 'thumbnail' ), + 'test_note' => array( 'title', 'thumbnail' ), + 'test_page' => array( 'title' ), + ) + ); + + // Naming post types only narrows the support when the theme does not + // already support post thumbnails for every post type. + remove_theme_support( 'post-thumbnails' ); + add_theme_support( 'post-thumbnails', array( 'test_book' ) ); + self::reset_registry(); + + $this->assertContains( 'featured_media', self::get_support_field_ids( 'test_book' ), 'The theme opted the post type into post thumbnails.' ); + $this->assertNotContains( 'featured_media', self::get_support_field_ids( 'test_note' ), 'The theme did not opt the post type into post thumbnails.' ); + $this->assertNotContains( 'featured_media', self::get_support_field_ids( 'test_page' ), 'The post type does not support thumbnails.' ); + + remove_theme_support( 'post-thumbnails' ); + add_theme_support( 'post-thumbnails' ); + self::reset_registry(); + + $this->assertContains( 'featured_media', self::get_support_field_ids( 'test_note' ), 'A theme may opt every post type in at once.' ); + + remove_theme_support( 'post-thumbnails' ); + self::reset_registry(); + + $this->assertNotContains( 'featured_media', self::get_support_field_ids( 'test_book' ), 'A theme without post thumbnails has no featured image to set.' ); + } + + /** + * The format field needs the post type to support `post-formats` and + * the theme to support post formats. Its elements are the formats of + * the theme, plus `standard`, sorted by label. + * + * @ticket 66229 + */ + public function test_the_format_field_follows_the_post_type_support_and_the_theme() { + $this->register_post_types( + array( + 'test_book' => array( 'title', 'post-formats' ), + 'test_note' => array( 'title' ), + ) + ); + + add_theme_support( 'post-formats', array( 'gallery', 'aside' ) ); + self::reset_registry(); + $fields = self::get_fields( 'postType', 'test_book' ); + + $this->assertArrayHasKey( 'format', $fields, 'The post type supporting post formats gets the field.' ); + $this->assertNotContains( 'format', self::get_field_ids( 'test_note' ), 'A post type that does not support post formats has no format field.' ); + $this->assertSame( + array( 'aside', 'gallery', 'standard' ), + array_column( $fields['format']['elements'], 'value' ), + 'The elements are the formats of the theme, plus standard, sorted by label.' + ); + + remove_theme_support( 'post-formats' ); + self::reset_registry(); + + $this->assertNotContains( 'format', self::get_field_ids( 'test_book' ), 'A theme without post formats has none to assign.' ); + } + + /** + * A plugin hooking the action at the default priority sees the core + * fields: it can update a default field, or remove it. + * + * @ticket 66229 + */ + public function test_a_plugin_can_alter_the_default_fields() { + $this->on_entity_fields_api_init( + static function () { + wp_update_entity_fields( + 'test-plugin', + 'postType', + 'page', + array( + array( + 'id' => 'comment_status', + 'enableSorting' => true, + ), + ) + ); + wp_unregister_entity_fields( 'postType', 'page', array( 'author' ) ); + } + ); + + $fields = self::get_fields( 'postType', 'page' ); + + $this->assertTrue( $fields['comment_status']['enableSorting'], 'The property is updated.' ); + $this->assertSame( 'text', $fields['comment_status']['type'], 'The rest of the default definition is kept.' ); + $this->assertSame( + array( + 'registeredBy' => 'core', + 'updatedBy' => array( 'test-plugin' ), + ), + $fields['comment_status']['origin'], + 'Updating a field keeps who registered it and records who updated it.' + ); + $this->assertArrayNotHasKey( 'author', $fields, 'The default field is removed.' ); + } + + /** + * A plugin drops a default field from its post type by unregistering + * it at the default priority, keeping the others. + * + * @ticket 66229 + */ + public function test_a_plugin_can_drop_a_default_field_from_a_post_type() { + $this->register_post_types( + array( + 'test_book' => array( 'title', 'author', 'comments' ), + 'test_note' => array( 'title', 'author', 'comments' ), + ) + ); + $this->on_entity_fields_api_init( + static function () { + wp_unregister_entity_fields( 'postType', 'test_book', array( 'comment_status' ) ); + } + ); + + $this->assertSame( array( 'author', 'discussion', 'title' ), self::get_support_field_ids( 'test_book' ), 'The post type drops the field.' ); + $this->assertSame( array( 'author', 'comment_status', 'discussion', 'title' ), self::get_support_field_ids( 'test_note' ), 'The other post types keep the field.' ); + } + + /** + * A plugin can replace the author field of templates. + * + * @ticket 66229 + */ + public function test_a_plugin_can_replace_the_author_field_of_templates() { + $this->on_entity_fields_api_init( + static function () { + wp_unregister_entity_fields( 'postType', 'wp_template', array( 'author' ) ); + wp_register_entity_fields( + 'my-plugin', + 'postType', + 'wp_template', + array( + array( + 'id' => 'author', + 'type' => 'text', + 'label' => 'Maker', + ), + ) + ); + } + ); + + $modules = wp_get_registered_entity_field_modules( 'postType', 'wp_template' ); + + $this->assertSame( 'my-plugin', self::get_fields( 'postType', 'wp_template' )['author']['origin']['registeredBy'], 'The author field is the one of the plugin.' ); + $this->assertNotContains( 'author', $modules['@wordpress/core-fields/wp_template'], 'The module of the replaced field no longer applies to it.' ); + } + + /** + * A plugin registering attachment fields keeps them next to the media + * fields. + * + * @ticket 66229 + */ + public function test_attachments_keep_the_fields_plugins_register() { + $this->register_fields( 'postType', 'attachment', array( $this->field( 'credit' ) ) ); + + $ids = self::get_field_ids( 'attachment' ); + + $this->assertContains( 'date', $ids, 'The media fields are kept.' ); + $this->assertSame( 'credit', end( $ids ), 'The plugin field follows the media fields.' ); + } +} From ea80ab3fe8aff39dcba07f369fbe13a9eedd5e02 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:19:30 +0100 Subject: [PATCH 2/7] Tests: Cover the core entity fields in the REST API. Ports the REST controller tests of https://github.com/WordPress/gutenberg/pull/84696: the fields route exposes the default fields of pages and the fields of patterns, with the script modules of their collections and the `core` origin. See #66229. --- .../rest-entity-fields-controller.php | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php b/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php index 400bbb5740f76..693295574fa82 100644 --- a/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php +++ b/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php @@ -69,8 +69,9 @@ public static function wpTearDownAfterClass() { /** * Tears down each test. * - * Resetting the registry drops the fields a test registered; the next - * read fires `wp_entity_fields_api_init` again. + * Resetting the registry drops the fields a test registered along with + * the defaults; the next read fires `wp_entity_fields_api_init` again and + * registers the defaults anew. */ public function tear_down() { foreach ( $this->callbacks as $callback ) { @@ -100,7 +101,8 @@ private static function reset_registry() { * * Registering only runs on the action, so the registration is hooked to * it, as a plugin does, and the action fired anew: the registry is reset - * and read, which replays the registrations made so far, in order. + * and read, which registers the defaults again and replays the + * registrations made so far, in order. * * @param string $kind The entity kind. * @param string $name The entity name. @@ -425,6 +427,81 @@ public function test_get_items_returns_entity_and_fields_shape() { $this->assertIsArray( $data['script_modules'] ); } + /** + * The default fields registered for a post type are exposed. + * + * @covers ::get_items + * + * @ticket 66229 + */ + public function test_get_items_exposes_the_default_post_type_fields() { + wp_set_current_user( self::$editor_id ); + + $data = $this->dispatch_request( 'postType', 'page' )->get_data(); + $ids = array_column( $data['fields'], 'id' ); + + $this->assertContains( 'author', $ids, 'Pages support authors.' ); + $this->assertContains( 'comment_status', $ids, 'Pages support comments.' ); + $this->assertSame( wp_get_registered_entity_fields( 'postType', 'page' ), $data['fields'], 'The fields are exposed as registered.' ); + $this->assertSame( + array( + array( + 'id' => '@wordpress/core-fields/post_type_supports', + 'fields' => array_values( array_diff( $ids, array( 'title' ) ) ), + ), + array( + 'id' => '@wordpress/core-fields/page', + 'fields' => array( 'title' ), + ), + ), + $data['script_modules'], + 'The fields are registered with the module of their collection.' + ); + } + + /** + * The pattern collection exposes its fields and their script module. + * + * @covers ::get_items + * + * @ticket 66229 + */ + public function test_get_items_exposes_pattern_fields_and_their_script_module() { + wp_set_current_user( self::$editor_id ); + + $data = $this->dispatch_request( 'postType', 'wp_block' )->get_data(); + $ids = array_column( $data['fields'], 'id' ); + $modules = array_column( $data['script_modules'], 'fields', 'id' ); + + $this->assertContains( 'excerpt', $ids, 'Patterns have a description.' ); + $this->assertContains( 'sync-status', $ids, 'Patterns have a sync status.' ); + $this->assertContains( 'title', $ids, 'Patterns have a title.' ); + $this->assertSame( wp_get_registered_entity_fields( 'postType', 'wp_block' ), $data['fields'], 'The fields of patterns are exposed as registered.' ); + $this->assertSame( + array( 'excerpt', 'sync-status', 'title' ), + $modules['@wordpress/core-fields/wp_block'], + 'The fields of the pattern collection are registered with its module.' + ); + } + + /** + * The default fields are exposed with the `core` origin. + * + * @covers ::get_items + * + * @ticket 66229 + */ + public function test_get_items_exposes_the_default_fields_with_the_core_origin() { + wp_set_current_user( self::$editor_id ); + + $data = $this->dispatch_request( 'postType', 'page' )->get_data(); + + $this->assertNotEmpty( $data['fields'], 'Pages have default fields.' ); + foreach ( $data['fields'] as $field ) { + $this->assertSame( 'core', $field['origin']['registeredBy'], "The {$field['id']} field is registered by WordPress core." ); + } + } + /** * An entity without registered fields returns empty lists, which encode as * JSON arrays. From 865745e974f9bb00ea35df115892d64184782830 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:44:57 +0100 Subject: [PATCH 3/7] Tests: Skip the core entity fields tests until the Gutenberg pin includes them. The core fields are copied from the Gutenberg build, so their tests are skipped while the pinned build does not include them. See #66229. --- .../wpRegisterCoreFieldCollections.php | 7 +++++++ .../rest-api/rest-entity-fields-controller.php | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php b/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php index 199be49d5b89c..5ac659477b7f2 100644 --- a/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php +++ b/tests/phpunit/tests/entity-fields/wpRegisterCoreFieldCollections.php @@ -41,12 +41,19 @@ class Tests_Entity_Fields_WpRegisterCoreFieldCollections extends WP_Entity_Field * * Registers the fields WordPress core registers instead of the fixture * defaults of the other entity fields tests. + * + * The core fields are copied from the Gutenberg build, so the tests are + * skipped until the pinned build includes them. */ public function set_up() { global $_wp_theme_features; parent::set_up(); + if ( ! function_exists( 'wp_register_core_field_collections' ) ) { + $this->markTestSkipped( 'The pinned Gutenberg build does not include the core fields.' ); + } + $this->theme_features = $_wp_theme_features; remove_action( 'wp_entity_fields_api_init', array( static::class, 'register_default_fields' ), 0 ); add_action( 'wp_entity_fields_api_init', 'wp_register_core_field_collections', 0 ); diff --git a/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php b/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php index 693295574fa82..12c855bc6afaf 100644 --- a/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php +++ b/tests/phpunit/tests/rest-api/rest-entity-fields-controller.php @@ -149,6 +149,16 @@ private function update_fields( $kind, $name, $fields, $module = null ) { return $updated; } + /** + * Skips a test of the fields WordPress core registers, which are copied + * from the Gutenberg build, until the pinned build includes them. + */ + private function skip_without_core_fields() { + if ( ! function_exists( 'wp_register_core_field_collections' ) ) { + $this->markTestSkipped( 'The pinned Gutenberg build does not include the core fields.' ); + } + } + /** * Dispatches a request to the fields route. * @@ -435,6 +445,7 @@ public function test_get_items_returns_entity_and_fields_shape() { * @ticket 66229 */ public function test_get_items_exposes_the_default_post_type_fields() { + $this->skip_without_core_fields(); wp_set_current_user( self::$editor_id ); $data = $this->dispatch_request( 'postType', 'page' )->get_data(); @@ -467,6 +478,7 @@ public function test_get_items_exposes_the_default_post_type_fields() { * @ticket 66229 */ public function test_get_items_exposes_pattern_fields_and_their_script_module() { + $this->skip_without_core_fields(); wp_set_current_user( self::$editor_id ); $data = $this->dispatch_request( 'postType', 'wp_block' )->get_data(); @@ -492,6 +504,7 @@ public function test_get_items_exposes_pattern_fields_and_their_script_module() * @ticket 66229 */ public function test_get_items_exposes_the_default_fields_with_the_core_origin() { + $this->skip_without_core_fields(); wp_set_current_user( self::$editor_id ); $data = $this->dispatch_request( 'postType', 'page' )->get_data(); From 624459c80bda0edba83284f4162d96aee9e898a0 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:34:46 +0100 Subject: [PATCH 4/7] Editor: Add the attachment entity view config for the media editor. Adds the view config of the `attachment` post type, whose form is the media editor form. It replaces the default post type form, so the media editor no longer shows only the date and author. Synced from https://github.com/WordPress/gutenberg/pull/84700. See #66229. --- src/wp-includes/default-filters.php | 2 +- src/wp-includes/view-config.php | 55 +++++++++++++ .../rest-api/rest-view-config-controller.php | 28 +++++++ tests/phpunit/tests/view-config.php | 78 +++++++++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 16afab80fcd58..55053f4c2193d 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -841,7 +841,7 @@ add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ); // View Config API. -foreach ( array( 'page', 'wp_block', 'wp_template_part', 'wp_template' ) as $post_type ) { +foreach ( array( 'page', 'wp_block', 'wp_template_part', 'wp_template', 'attachment' ) as $post_type ) { // Base definitions run before the default priority, so third-party // callbacks registered at the default compose on top of them // regardless of registration order. diff --git a/src/wp-includes/view-config.php b/src/wp-includes/view-config.php index bf10553500907..ebf3dd3dc5fc0 100644 --- a/src/wp-includes/view-config.php +++ b/src/wp-includes/view-config.php @@ -771,6 +771,61 @@ function _wp_get_entity_view_config_posttype_wp_template( $data ) { return $data; } +/** + * Provides the view configuration for the `attachment` post type. + * + * An attachment is edited through a form (the media editor) that shares + * nothing with the default post type form, so the `form` is replaced: the + * editable fields first, in a regular layout, then the metadata and the file + * information, in panels. The generic `default_view`, `default_layouts`, and + * `view_list` built by wp_get_entity_view_config() are left untouched. + * + * @since 7.2.0 + * + * @param WP_View_Config_Data $data The view configuration container for the entity. + * @return WP_View_Config_Data The updated view configuration container. + */ +function _wp_get_entity_view_config_posttype_attachment( $data ) { + $regular_layout = array( + 'type' => 'regular', + 'labelPosition' => 'top', + ); + + return $data->set( + array( + 'form' => array( + 'layout' => array( 'type' => 'panel' ), + 'fields' => array( + array( + 'id' => 'title', + 'layout' => $regular_layout, + ), + array( + 'id' => 'alt_text', + 'layout' => $regular_layout, + ), + array( + 'id' => 'caption', + 'layout' => $regular_layout, + ), + array( + 'id' => 'description', + 'layout' => $regular_layout, + ), + 'date', + 'author', + 'filename', + 'mime_type', + 'filesize', + 'media_dimensions', + 'attached_to', + ), + ), + ), + 1 + ); +} + /** * Provides the view configuration for the `root`/`site` entity. * diff --git a/tests/phpunit/tests/rest-api/rest-view-config-controller.php b/tests/phpunit/tests/rest-api/rest-view-config-controller.php index c6a7ed86b816d..833f4e598a138 100644 --- a/tests/phpunit/tests/rest-api/rest-view-config-controller.php +++ b/tests/phpunit/tests/rest-api/rest-view-config-controller.php @@ -302,6 +302,34 @@ public function test_get_items_matches_underlying_config() { $this->assertSame( $config['form'], $data['form'] ); } + /** + * The `attachment` post type provides the form of the media editor. + * + * @ticket 66229 + * + * @covers ::get_items + */ + public function test_get_items_attachment_form() { + wp_set_current_user( self::$editor_id ); + + $response = $this->dispatch_request( 'postType', 'attachment' ); + $this->assertSame( 200, $response->get_status(), 'The request succeeds.' ); + + $data = json_decode( wp_json_encode( $response->get_data() ), true ); + + $this->assertSame( array( 'type' => 'panel' ), $data['form']['layout'], 'The form uses a panel layout.' ); + $this->assertSame( + array( 'title', 'alt_text', 'caption', 'description', 'date', 'author', 'filename', 'mime_type', 'filesize', 'media_dimensions', 'attached_to' ), + array_map( + static function ( $field ) { + return is_array( $field ) ? $field['id'] : $field; + }, + $data['form']['fields'] + ), + 'The form lists the media editor fields in order.' + ); + } + /** * The `root`/`site` entity provides the form of the site identity screen. * diff --git a/tests/phpunit/tests/view-config.php b/tests/phpunit/tests/view-config.php index e30e7820368c6..28966f8c33f13 100644 --- a/tests/phpunit/tests/view-config.php +++ b/tests/phpunit/tests/view-config.php @@ -139,6 +139,84 @@ public function data_default_form_post_types() { ); } + /** + * Post types with their own form do not receive the default form groups. + * + * @ticket 66229 + * + * @dataProvider data_post_types_with_own_form + * + * @param string $post_type The post type. + */ + public function test_own_form_post_types_do_not_get_default_groups( $post_type ) { + $config = wp_get_entity_view_config( 'postType', $post_type ); + + $this->assertNull( $this->get_form_field( $config, 'status' ), 'The `status` group is absent.' ); + $this->assertNull( $this->get_form_field( $config, 'discussion' ), 'The `discussion` group is absent.' ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_post_types_with_own_form() { + return array( + 'attachment' => array( 'attachment' ), + 'wp_block' => array( 'wp_block' ), + 'wp_template' => array( 'wp_template' ), + 'wp_template_part' => array( 'wp_template_part' ), + ); + } + + /** + * The `attachment` post type provides the form of the media editor: the + * editable fields in a regular layout, then the rest in panels. + * + * @ticket 66229 + * + * @covers ::_wp_get_entity_view_config_posttype_attachment + */ + public function test_attachment_form() { + $config = wp_get_entity_view_config( 'postType', 'attachment' ); + + $this->assertSame( array( 'type' => 'panel' ), $config['form']['layout'], 'The form uses a panel layout.' ); + + $regular_layout = array( + 'type' => 'regular', + 'labelPosition' => 'top', + ); + $this->assertSame( + array( + array( + 'id' => 'title', + 'layout' => $regular_layout, + ), + array( + 'id' => 'alt_text', + 'layout' => $regular_layout, + ), + array( + 'id' => 'caption', + 'layout' => $regular_layout, + ), + array( + 'id' => 'description', + 'layout' => $regular_layout, + ), + 'date', + 'author', + 'filename', + 'mime_type', + 'filesize', + 'media_dimensions', + 'attached_to', + ), + $config['form']['fields'], + 'The form lists the media editor fields in order.' + ); + } + /** * The default configuration exposes the documented shape for an unknown entity. */ From 499f08ea6a89ce848d4a574585bbdae86d14632a Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 18:19:35 +0100 Subject: [PATCH 5/7] Editor: Register the core entity actions on the server. Loads `register_core_entity_actions()` from the `core-actions` package, which registers the actions of posts, pages, patterns and template parts through the entity actions API at priority 0, so a plugin can unregister or replace them. The build copies its PHP files to `wp-includes/build/scripts/core-actions/`. The post editor also preloads the actions of the post type it edits. Synced from https://github.com/WordPress/gutenberg/pull/84660. See #66343. --- Gruntfile.js | 4 +- src/wp-includes/default-filters.php | 1 + src/wp-includes/entity-actions.php | 32 +- src/wp-settings.php | 3 + tests/phpunit/tests/entity-actions.php | 60 +- .../registerCoreEntityActions.php | 520 ++++++++++++++++++ .../wpAddEntityFieldModulesToEditorScript.php | 3 + 7 files changed, 616 insertions(+), 7 deletions(-) create mode 100644 tests/phpunit/tests/entity-actions/registerCoreEntityActions.php diff --git a/Gruntfile.js b/Gruntfile.js index e2ad7d0a074b0..ef82a84128ce9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -697,7 +697,7 @@ module.exports = function(grunt) { src: 'vendor/composer/ca-bundle/res/cacert.pem', dest: SOURCE_DIR + 'wp-includes/certificates/ca-bundle.crt' }, - // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/, core fields). + // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/, core actions, core fields). 'gutenberg-php': { options: { process: function( content ) { @@ -716,6 +716,8 @@ module.exports = function(grunt) { 'pages.php', 'constants.php', 'pages/**/*.php', + // The actions Core registers through the entity actions API. + 'scripts/core-actions/**/*.php', // The fields Core registers through the entity fields API. 'scripts/core-fields/**/*.php', ], diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 55053f4c2193d..c962b962fa552 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -855,6 +855,7 @@ // Entity Actions API. add_action( 'admin_footer', '_wp_add_entity_action_modules_to_editor_script', 10, 0 ); +add_filter( 'block_editor_rest_api_preload_paths', '_wp_preload_entity_actions', 10, 2 ); // Entity Fields API. add_action( 'admin_footer', '_wp_add_entity_field_modules_to_editor_script', 10, 0 ); diff --git a/src/wp-includes/entity-actions.php b/src/wp-includes/entity-actions.php index 6496048f71d9e..13ec78bc03e33 100644 --- a/src/wp-includes/entity-actions.php +++ b/src/wp-includes/entity-actions.php @@ -15,7 +15,8 @@ * * Actions are registered on the `wp_entity_actions_api_init` action, and only there, * with {@see wp_register_entity_actions()} and {@see wp_unregister_entity_actions()}. The - * other functions read the registry. + * other functions read the registry. WordPress core registers its own + * actions the same way, from packages/core-actions. * * @package WordPress * @subpackage Entity Actions @@ -186,3 +187,32 @@ function _wp_add_entity_action_modules_to_editor_script( $scripts = null ) { $scripts->add_data( 'wp-editor', 'module_dependencies', $dependencies ); } } + +/** + * Preloads the actions of the post type the post editor edits. + * + * The actions menu of the post editor reads them from the `wp/v2/entity-actions` + * route as it loads, see `useEntityActions()` in + * packages/editor/src/dataviews/entity-actions-loader.ts. The path must match the + * request of the `getEntityActionsConfig` core data resolver, see + * packages/core-data/src/resolvers.js. + * + * @since 7.2.0 + * + * @param array $paths REST API paths to preload. + * @param WP_Block_Editor_Context $context Block editor context. + * @return array Filtered preload paths. + */ +function _wp_preload_entity_actions( $paths, $context ) { + if ( 'core/edit-post' === $context->name && isset( $context->post ) ) { + $paths[] = add_query_arg( + array( + 'kind' => 'postType', + 'name' => $context->post->post_type, + ), + '/wp/v2/entity-actions' + ); + } + + return $paths; +} diff --git a/src/wp-settings.php b/src/wp-settings.php index 4ae9f164b0178..8bd37b43d635e 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -254,6 +254,9 @@ if ( file_exists( ABSPATH . WPINC . '/build/pages.php' ) ) { require ABSPATH . WPINC . '/build/pages.php'; } +if ( file_exists( ABSPATH . WPINC . '/build/scripts/core-actions/index.php' ) ) { + require ABSPATH . WPINC . '/build/scripts/core-actions/index.php'; +} if ( file_exists( ABSPATH . WPINC . '/build/scripts/core-fields/index.php' ) ) { require ABSPATH . WPINC . '/build/scripts/core-fields/index.php'; } diff --git a/tests/phpunit/tests/entity-actions.php b/tests/phpunit/tests/entity-actions.php index 8283a81b6b64d..17e73686efcf7 100644 --- a/tests/phpunit/tests/entity-actions.php +++ b/tests/phpunit/tests/entity-actions.php @@ -8,6 +8,7 @@ * @group entity-actions * * @covers ::_wp_add_entity_action_modules_to_editor_script + * @covers ::_wp_preload_entity_actions * @covers ::wp_register_entity_actions * @covers ::wp_unregister_entity_actions * @covers WP_Entity_Actions_Registry::initialize @@ -17,12 +18,15 @@ class Tests_Entity_Actions extends WP_UnitTestCase { /** - * Sets up each test: the default actions of the tests are registered at - * priority 0, so every test runs against a registry holding actions - * registered before the callbacks hooked at the default priority run. + * Sets up each test: the actions WordPress registers are replaced with + * the default actions of the tests, hooked at priority 0 as WordPress + * registers its own, so every test runs against a registry holding + * actions registered before the callbacks hooked at the default priority + * run. */ public function set_up() { parent::set_up(); + remove_action( 'wp_entity_actions_api_init', 'register_core_entity_actions', 0 ); add_action( 'wp_entity_actions_api_init', array( __CLASS__, 'register_default_actions' ), 0 ); } @@ -31,8 +35,9 @@ public function set_up() { * * Resetting the registry drops the actions a test registered along with * the defaults; the next read fires `wp_entity_actions_api_init` again and - * registers the defaults anew. The test case removes the callbacks the - * test hooked, the defaults included. + * registers the defaults anew. The test case restores the hooks: it + * removes the callbacks the test hooked, the defaults included, and hooks + * the actions WordPress registers back. */ public function tear_down() { self::reset_registry(); @@ -387,6 +392,51 @@ public function test_action_script_modules_reach_the_import_map() { $this->assertArrayHasKey( 'plugin/feature', $import_map['imports'] ); } + /** + * The post editor preloads the actions of the post type it edits, with + * the path the `getEntityActionsConfig` resolver of core data requests. + * + * @ticket 66343 + */ + public function test_the_post_editor_preloads_the_actions_of_the_post_type() { + $context = new WP_Block_Editor_Context( array( 'post' => self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ) ) ); + + $paths = apply_filters( 'block_editor_rest_api_preload_paths', array(), $context ); + + $this->assertContains( '/wp/v2/entity-actions?kind=postType&name=page', $paths, 'The actions of pages are preloaded.' ); + } + + /** + * The preloaded path serves the actions of the post type. + * + * @ticket 66343 + */ + public function test_the_preloaded_actions_path_serves_the_actions() { + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + $context = new WP_Block_Editor_Context( array( 'post' => self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ) ) ); + + $paths = _wp_preload_entity_actions( array(), $context ); + $preloaded = array_reduce( $paths, 'rest_preload_api_request', array() ); + + $this->assertSame( + array( 'rename-post', 'move-to-trash' ), + array_column( $preloaded['/wp/v2/entity-actions?kind=postType&name=page']['body']['actions'], 'id' ), + 'The preloaded response holds the actions of pages.' + ); + } + + /** + * Only the post editor preloads the actions: the other editors have no + * post type of their own. + * + * @ticket 66343 + */ + public function test_other_editors_do_not_preload_the_actions() { + $context = new WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) ); + + $this->assertSame( array( '/wp/v2/types' ), _wp_preload_entity_actions( array( '/wp/v2/types' ), $context ), 'The paths are unchanged.' ); + } + /** * Reading the registry fires `wp_entity_actions_api_init` once, whichever * getter is read first and however many times it is read. diff --git a/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php b/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php new file mode 100644 index 0000000000000..188edf7b1d165 --- /dev/null +++ b/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php @@ -0,0 +1,520 @@ +user->create( array( 'role' => 'administrator' ) ); + self::$editor_id = $factory->user->create( array( 'role' => 'editor' ) ); + self::$contributor_id = $factory->user->create( array( 'role' => 'contributor' ) ); + } + + /** + * Deletes shared users. + */ + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$editor_id ); + self::delete_user( self::$contributor_id ); + } + + /** + * Tears down each test. + * + * Resetting the registry drops the actions registered for the user and + * theme of the test; the next read fires `wp_entity_actions_api_init` again. + */ + public function tear_down() { + self::reset_registry(); + + parent::tear_down(); + } + + /** + * Resets the registry: drops the singleton instance, so the next + * get_instance() creates an empty registry and its first read fires + * `wp_entity_actions_api_init` again. + */ + private static function reset_registry() { + $instance = new ReflectionProperty( WP_Entity_Actions_Registry::class, 'instance' ); + if ( PHP_VERSION_ID < 80100 ) { + $instance->setAccessible( true ); + } + $instance->setValue( null, null ); + } + + /** + * Returns the ids of the actions registered for a post type, from a + * registry built anew for the current user and theme, as each request + * builds it. + * + * @param string $post_type The post type. + * @return string[] The ids of the actions, in registration order. + */ + private function get_action_ids( $post_type ) { + self::reset_registry(); + return array_column( wp_get_registered_entity_actions( 'postType', $post_type ), 'id' ); + } + + /** + * The core actions are registered at priority 0, so a plugin hooking the + * action at the default priority sees them. + * + * @ticket 66343 + */ + public function test_the_core_actions_are_registered_at_priority_0() { + $this->assertSame( 0, has_action( 'wp_entity_actions_api_init', 'register_core_entity_actions' ) ); + } + + /** + * Pages are viewable and support revisions, titles, and page attributes, + * and the user can manage the site options: they have every action of the + * `post_type_supports` module, then the actions of the `page` module, in + * the order the actions are shown. + * + * @ticket 66343 + */ + public function test_the_actions_of_pages() { + wp_set_current_user( self::$admin_id ); + + $ids = array_merge( array( 'view-post', 'view-post-revisions', 'rename-post', 'order-pages' ), self::EVERY_POST_TYPE ); + $this->assertSame( array_merge( $ids, self::PAGE_ACTIONS ), $this->get_action_ids( 'page' ), 'Pages have these actions, in this order.' ); + $this->assertSame( + array( + self::POST_TYPE_SUPPORTS => $ids, + self::PAGE => self::PAGE_ACTIONS, + ), + wp_get_registered_entity_action_modules( 'postType', 'page' ), + 'Each action of pages is registered with the module of its folder.' + ); + $this->assertSame( array( 'core' ), array_unique( array_column( array_column( wp_get_registered_entity_actions( 'postType', 'page' ), 'origin' ), 'registeredBy' ) ), 'Every action is registered by core.' ); + } + + /** + * Posts do not support page attributes, so they cannot be ordered. + * + * @ticket 66343 + */ + public function test_the_actions_of_posts() { + wp_set_current_user( self::$admin_id ); + + $this->assertSame( + array_merge( array( 'view-post', 'view-post-revisions', 'rename-post' ), self::EVERY_POST_TYPE ), + $this->get_action_ids( 'post' ), + 'Posts have these actions, in this order.' + ); + } + + /** + * Patterns are not viewable and are duplicated and exported by the + * actions of the `wp_block` module, placed among the others in the order + * the actions are shown. + * + * @ticket 66343 + */ + public function test_the_actions_of_patterns() { + wp_set_current_user( self::$admin_id ); + + $this->assertSame( + array_merge( array( 'view-post-revisions', 'duplicate-pattern', 'rename-post', 'export-pattern' ), self::EVERY_POST_TYPE ), + $this->get_action_ids( 'wp_block' ), + 'Patterns have these actions, in this order.' + ); + $this->assertSame( + array( + self::POST_TYPE_SUPPORTS => array_merge( array( 'view-post-revisions', 'rename-post' ), self::EVERY_POST_TYPE ), + self::WP_BLOCK => array( 'duplicate-pattern', 'export-pattern' ), + ), + wp_get_registered_entity_action_modules( 'postType', 'wp_block' ), + 'The actions of patterns come from the `wp_block` module.' + ); + } + + /** + * Templates are not duplicated: the editor creates them from the theme. + * + * @ticket 66343 + */ + public function test_the_actions_of_templates() { + wp_set_current_user( self::$admin_id ); + switch_theme( 'block-theme' ); + + $this->assertSame( + array_merge( array( 'view-post-revisions', 'rename-post' ), self::EVERY_POST_TYPE ), + $this->get_action_ids( 'wp_template' ), + 'Templates have these actions, in this order.' + ); + } + + /** + * Template parts are duplicated by the action of the `wp_template_part` + * module, with a block theme only. + * + * @ticket 66343 + */ + public function test_template_parts_are_only_duplicated_with_a_block_theme() { + wp_set_current_user( self::$admin_id ); + + switch_theme( 'block-theme' ); + $this->assertSame( + array_merge( array( 'view-post-revisions', 'duplicate-template-part', 'rename-post' ), self::EVERY_POST_TYPE ), + $this->get_action_ids( 'wp_template_part' ), + 'A block theme can duplicate template parts.' + ); + $this->assertSame( + array( 'duplicate-template-part' ), + wp_get_registered_entity_action_modules( 'postType', 'wp_template_part' )[ self::WP_TEMPLATE_PART ], + 'The action comes from the `wp_template_part` module.' + ); + + switch_theme( 'default' ); + $this->assertNotContains( 'duplicate-template-part', $this->get_action_ids( 'wp_template_part' ), 'A classic theme cannot.' ); + } + + /** + * The "Duplicate…" action of the other post types is only registered by + * the Gutenberg plugin. + * + * @ticket 66343 + */ + public function test_posts_are_not_duplicated() { + wp_set_current_user( self::$admin_id ); + + foreach ( get_post_types( array( 'show_in_rest' => true ) ) as $post_type ) { + $this->assertNotContains( 'duplicate-post', $this->get_action_ids( $post_type ), "The $post_type post type has no duplicate action." ); + } + } + + /** + * The duplicate actions are only registered for the users who can create + * the posts they duplicate into: the registry is built for the current + * user. + * + * @ticket 66343 + */ + public function test_the_duplicate_actions_depend_on_the_current_user() { + switch_theme( 'block-theme' ); + + wp_set_current_user( self::$admin_id ); + $this->assertContains( 'duplicate-pattern', $this->get_action_ids( 'wp_block' ), 'Administrators can create patterns.' ); + $this->assertContains( 'duplicate-template-part', $this->get_action_ids( 'wp_template_part' ), 'Administrators can create template parts.' ); + + wp_set_current_user( self::$editor_id ); + $this->assertContains( 'duplicate-pattern', $this->get_action_ids( 'wp_block' ), 'Editors can create patterns.' ); + $this->assertNotContains( 'duplicate-template-part', $this->get_action_ids( 'wp_template_part' ), 'Editors cannot create template parts.' ); + + wp_set_current_user( self::$contributor_id ); + $this->assertNotContains( 'duplicate-pattern', $this->get_action_ids( 'wp_block' ), 'Contributors cannot create patterns.' ); + $this->assertContains( 'export-pattern', $this->get_action_ids( 'wp_block' ), 'Contributors can export them.' ); + } + + /** + * The actions of a custom post type follow what it supports. + * + * @ticket 66343 + */ + public function test_the_actions_of_a_custom_post_type_follow_its_supports() { + register_post_type( + 'test_book', + array( + 'public' => true, + 'show_in_rest' => true, + 'supports' => array( 'title', 'revisions' ), + ) + ); + register_post_type( + 'test_note', + array( + 'show_ui' => true, + 'show_in_rest' => true, + 'supports' => array( 'editor' ), + ) + ); + register_post_type( + 'test_hidden', + array( + 'public' => true, + 'show_in_rest' => false, + ) + ); + wp_set_current_user( self::$admin_id ); + + $this->assertSame( + array_merge( array( 'view-post', 'view-post-revisions', 'rename-post' ), self::EVERY_POST_TYPE ), + $this->get_action_ids( 'test_book' ), + 'A viewable post type with titles and revisions.' + ); + $this->assertSame( + self::EVERY_POST_TYPE, + $this->get_action_ids( 'test_note' ), + 'A post type that is not viewable, without titles or revisions.' + ); + $this->assertSame( array(), $this->get_action_ids( 'test_hidden' ), 'A post type not exposed in the REST API has no action.' ); + } + + /** + * Setting the homepage or the posts page changes the site settings, so + * the actions of the `page` module are only registered for the users who + * can manage the site options. + * + * @ticket 66343 + */ + public function test_the_homepage_actions_depend_on_the_current_user() { + wp_set_current_user( self::$admin_id ); + $this->assertSame( self::PAGE_ACTIONS, array_values( array_intersect( $this->get_action_ids( 'page' ), self::PAGE_ACTIONS ) ), 'Administrators can manage the site options.' ); + + wp_set_current_user( self::$editor_id ); + $this->assertSame( array(), array_intersect( $this->get_action_ids( 'page' ), self::PAGE_ACTIONS ), 'Editors cannot manage the site options.' ); + } + + /** + * A `front-page` template shows on the front page whatever the site + * settings say, so the actions of the `page` module are not registered + * when the theme has one. + * + * @ticket 66343 + */ + public function test_the_homepage_actions_are_not_registered_with_a_front_page_template() { + wp_set_current_user( self::$admin_id ); + switch_theme( 'block-theme' ); + $this->assertContains( 'set-as-homepage', $this->get_action_ids( 'page' ), 'The theme has no `front-page` template.' ); + + $template_id = self::factory()->post->create( + array( + 'post_type' => 'wp_template', + 'post_name' => 'front-page', + 'post_title' => 'Front Page', + 'post_content' => '

Front page

', + ) + ); + wp_set_post_terms( $template_id, get_stylesheet(), 'wp_theme' ); + + $this->assertSame( array(), array_intersect( $this->get_action_ids( 'page' ), self::PAGE_ACTIONS ), 'The `front-page` template removes the actions.' ); + } + + /** + * The definitions are the ones of the `action.php` files, with the id + * first. + * + * @ticket 66343 + */ + public function test_the_definitions_come_from_the_action_files() { + wp_set_current_user( self::$admin_id ); + + $actions = array_column( wp_get_registered_entity_actions( 'postType', 'page' ), null, 'id' ); + $this->assertSame( + array( + 'id' => 'move-to-trash', + 'label' => 'Trash…', + 'isPrimary' => true, + 'supportsBulk' => true, + 'hideModalHeader' => true, + 'modalFocusOnMount' => 'firstContentElement', + 'origin' => array( 'registeredBy' => 'core' ), + ), + $actions['move-to-trash'], + 'The definition of the trash action.' + ); + $this->assertSame( + array( + 'id' => 'view-post-revisions', + 'label' => 'View revisions', + 'context' => 'list', + 'origin' => array( 'registeredBy' => 'core' ), + ), + $actions['view-post-revisions'], + 'The definition of the revisions action.' + ); + } + + /** + * The labels are translated with the default text domain of WordPress. + * + * @ticket 66343 + */ + public function test_the_labels_are_translated_with_the_default_text_domain() { + $domains = array(); + add_filter( + 'gettext', + static function ( $translation, $text, $domain ) use ( &$domains ) { + if ( 'Trash…' === $text ) { + $domains[] = $domain; + } + return $translation; + }, + 10, + 3 + ); + add_filter( + 'gettext_with_context', + static function ( $translation, $text, $context, $domain ) use ( &$domains ) { + if ( 'Trash…' === $text ) { + $domains[] = $domain; + } + return $translation; + }, + 10, + 4 + ); + wp_set_current_user( self::$admin_id ); + + $this->get_action_ids( 'page' ); + + $this->assertSame( array( 'default' ), array_unique( $domains ), 'The label of the trash action is translated in the default text domain.' ); + } + + /** + * The REST route serves the core actions of each post type, with their + * script modules, in a response that validates against its schema. + * + * @ticket 66343 + */ + public function test_the_rest_route_serves_the_core_actions() { + wp_set_current_user( self::$admin_id ); + switch_theme( 'block-theme' ); + $schema = ( new WP_REST_Entity_Actions_Controller() )->get_item_schema(); + + $modules = array( + 'page' => array( self::POST_TYPE_SUPPORTS, self::PAGE ), + 'wp_block' => array( self::POST_TYPE_SUPPORTS, self::WP_BLOCK ), + 'wp_template_part' => array( self::POST_TYPE_SUPPORTS, self::WP_TEMPLATE_PART ), + ); + foreach ( $modules as $post_type => $module_ids ) { + self::reset_registry(); + $request = new WP_REST_Request( 'GET', '/wp/v2/entity-actions' ); + $request->set_query_params( + array( + 'kind' => 'postType', + 'name' => $post_type, + ) + ); + $data = rest_get_server()->dispatch( $request )->get_data(); + + $this->assertSame( array_column( wp_get_registered_entity_actions( 'postType', $post_type ), 'id' ), array_column( $data['actions'], 'id' ), "The actions of $post_type are served." ); + $this->assertSame( $module_ids, array_column( $data['script_modules'], 'id' ), "The script modules of $post_type are served." ); + $this->assertTrue( rest_validate_value_from_schema( $data, $schema ), "The response for $post_type matches the schema." ); + } + } + + /** + * The script modules of the core actions are registered, so the editor + * script can add them to the import map. + * + * @ticket 66343 + */ + public function test_the_script_modules_of_the_core_actions_are_registered() { + wp_set_current_user( self::$admin_id ); + + foreach ( wp_get_all_registered_entity_action_modules()['postType'] as $post_type => $modules ) { + foreach ( $modules as $module ) { + $this->assertNotNull( wp_script_modules()->get_registered( $module ), "The $module module of $post_type is registered." ); + } + } + } + + /** + * A plugin hooking the action at the default priority can remove a core + * action, and replace one: it unregisters the action and registers its + * own definition with the script module of the core action, which keeps + * its JavaScript parts. The replaced action moves after the others. + * + * @ticket 66343 + */ + public function test_a_plugin_can_remove_and_replace_core_actions() { + $callback = static function () { + wp_unregister_entity_actions( 'postType', 'page', array( 'order-pages' ) ); + $trash = wp_unregister_entity_actions( 'postType', 'page', array( 'move-to-trash' ) ); + wp_register_entity_actions( + 'test-plugin', + 'postType', + 'page', + array( array_merge( $trash[0], array( 'label' => 'Bin…' ) ) ), + '@wordpress/core-actions/post_type_supports' + ); + }; + add_action( 'wp_entity_actions_api_init', $callback ); + wp_set_current_user( self::$admin_id ); + + $ids = array( 'view-post', 'view-post-revisions', 'rename-post', 'restore', 'reset-post', 'delete-post', 'permanently-delete' ); + $this->assertSame( array_merge( $ids, self::PAGE_ACTIONS, array( 'move-to-trash' ) ), $this->get_action_ids( 'page' ), 'The order action is removed and the trash action moves last.' ); + $this->assertSame( + array( + self::POST_TYPE_SUPPORTS => array_merge( $ids, array( 'move-to-trash' ) ), + self::PAGE => self::PAGE_ACTIONS, + ), + wp_get_registered_entity_action_modules( 'postType', 'page' ), + 'The replaced action keeps the module of the core action.' + ); + + $actions = array_column( wp_get_registered_entity_actions( 'postType', 'page' ), null, 'id' ); + $this->assertSame( 'Bin…', $actions['move-to-trash']['label'], 'The label of the plugin is used.' ); + $this->assertSame( 'test-plugin', $actions['move-to-trash']['origin']['registeredBy'], 'The plugin registered the action.' ); + $this->assertTrue( $actions['move-to-trash']['isPrimary'], 'The rest of the core definition is kept.' ); + } +} diff --git a/tests/phpunit/tests/entity-fields/wpAddEntityFieldModulesToEditorScript.php b/tests/phpunit/tests/entity-fields/wpAddEntityFieldModulesToEditorScript.php index 8dc91728ff123..f345156089e5d 100644 --- a/tests/phpunit/tests/entity-fields/wpAddEntityFieldModulesToEditorScript.php +++ b/tests/phpunit/tests/entity-fields/wpAddEntityFieldModulesToEditorScript.php @@ -79,6 +79,9 @@ public function test_the_admin_footer_action_uses_the_global_scripts() { $wp_scripts->enqueue( 'wp-editor' ); $wp_scripts->add_data( 'wp-editor', 'module_dependencies', array() ); + // The core entity actions add their own modules to the editor script. + remove_action( 'admin_footer', '_wp_add_entity_action_modules_to_editor_script', 10 ); + // Other callbacks of the action may print markup. get_echo( 'do_action', array( 'admin_footer', 'site-editor.php' ) ); From cb6fbd3e933ef40cb747439f21273f345725b6f8 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:50:47 +0100 Subject: [PATCH 6/7] Tests: Skip the core entity actions tests until the Gutenberg pin includes them. The core actions are copied from the Gutenberg build, so their tests are skipped while the pinned build does not include them. See #66343. --- .../entity-actions/registerCoreEntityActions.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php b/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php index 188edf7b1d165..c7b960d2349fe 100644 --- a/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php +++ b/tests/phpunit/tests/entity-actions/registerCoreEntityActions.php @@ -84,6 +84,20 @@ public static function wpTearDownAfterClass() { self::delete_user( self::$contributor_id ); } + /** + * Sets up each test. + * + * The core actions are copied from the Gutenberg build, so the tests are + * skipped until the pinned build includes them. + */ + public function set_up() { + parent::set_up(); + + if ( ! function_exists( 'register_core_entity_actions' ) ) { + $this->markTestSkipped( 'The pinned Gutenberg build does not include the core actions.' ); + } + } + /** * Tears down each test. * From c7aafe40e1f9ffa665989e087f29d5cc0ff63429 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Oct 2026 19:57:28 +0100 Subject: [PATCH 7/7] General: Pin Gutenberg to `2a96cb2` for testing the entity actions, fields, and view configs together. Points the Gutenberg pin to a trunk build that includes the core entity actions, fields, and view configs, with their labels in the default text domain, and adds the files that build copies over. For testing only, the pin update belongs to its own PR. --- package.json | 2 +- .../assets/icon-library-manifest.php | 45 +- .../assets/script-loader-packages.php | 106 +- .../assets/script-modules-packages.php | 267 +- .../blocks/accordion-heading/block.json | 4 + src/wp-includes/blocks/archives/block.json | 3 + src/wp-includes/blocks/avatar.php | 8 +- src/wp-includes/blocks/avatar/block.json | 4 + src/wp-includes/blocks/blocks-json.php | 472 +- src/wp-includes/blocks/breadcrumbs.php | 2 +- src/wp-includes/blocks/button.php | 47 + src/wp-includes/blocks/button/block.json | 6 + src/wp-includes/blocks/buttons/block.json | 7 +- src/wp-includes/blocks/code/block.json | 4 + src/wp-includes/blocks/columns/block.json | 4 + .../comments-pagination-next/block.json | 15 +- .../comments-pagination-previous/block.json | 15 +- .../blocks/comments-pagination/block.json | 11 + src/wp-includes/blocks/cover.php | 101 +- src/wp-includes/blocks/cover/block.json | 8 +- src/wp-includes/blocks/details/block.json | 3 + src/wp-includes/blocks/embed/block.json | 7 +- src/wp-includes/blocks/file/block.json | 18 + src/wp-includes/blocks/footnotes/block.json | 4 + src/wp-includes/blocks/gallery.php | 12 +- src/wp-includes/blocks/gallery/block.json | 13 + src/wp-includes/blocks/heading/block.json | 6 + src/wp-includes/blocks/home-link/block.json | 12 + src/wp-includes/blocks/icon.php | 37 +- src/wp-includes/blocks/icon/block.json | 18 +- src/wp-includes/blocks/image.php | 8 +- .../blocks/latest-comments/block.json | 10 + .../blocks/latest-posts/block.json | 4 + src/wp-includes/blocks/list-item/block.json | 9 +- src/wp-includes/blocks/list/block.json | 18 +- src/wp-includes/blocks/loginout/block.json | 7 +- src/wp-includes/blocks/math/block.json | 4 + src/wp-includes/blocks/media-text/block.json | 18 + .../navigation-overlay-close/block.json | 5 + src/wp-includes/blocks/navigation.php | 7 - src/wp-includes/blocks/page-list/block.json | 4 + src/wp-includes/blocks/paragraph/block.json | 11 + src/wp-includes/blocks/playlist/block.json | 13 + .../blocks/post-author-biography/block.json | 1 + .../blocks/post-comments-form/block.json | 5 + .../blocks/post-content/block.json | 3 +- .../blocks/post-excerpt/block.json | 4 + .../blocks/post-featured-image/block.json | 1 + .../blocks/post-navigation-link.php | 58 +- .../blocks/post-navigation-link/block.json | 13 + .../blocks/post-template/block.json | 4 + src/wp-includes/blocks/post-terms/block.json | 4 + .../blocks/post-time-to-read/block.json | 4 + src/wp-includes/blocks/post-title/block.json | 5 + .../blocks/preformatted/block.json | 4 + src/wp-includes/blocks/pullquote/block.json | 5 +- .../blocks/query-no-results/block.json | 4 + .../blocks/query-pagination/block.json | 17 + src/wp-includes/blocks/query-total/block.json | 4 + src/wp-includes/blocks/quote/block.json | 3 + src/wp-includes/blocks/read-more/block.json | 5 + src/wp-includes/blocks/rss/block.json | 4 + src/wp-includes/blocks/search.php | 50 +- src/wp-includes/blocks/search/block.json | 21 +- .../blocks/site-tagline/block.json | 4 + src/wp-includes/blocks/site-title/block.json | 4 + .../blocks/social-links/block.json | 1 + src/wp-includes/blocks/tab-list.php | 4 +- src/wp-includes/blocks/tab-list/block.json | 17 +- src/wp-includes/blocks/tab-panels/block.json | 3 + src/wp-includes/blocks/table/block.json | 13 +- src/wp-includes/blocks/tabs/block.json | 3 + src/wp-includes/blocks/template-part.php | 19 +- src/wp-includes/blocks/term-count.php | 6 +- src/wp-includes/blocks/term-count/block.json | 7 +- .../blocks/term-description/block.json | 4 + src/wp-includes/blocks/term-name/block.json | 7 +- src/wp-includes/blocks/term-template.php | 6 +- .../blocks/term-template/block.json | 6 + src/wp-includes/blocks/terms-query/block.json | 5 + src/wp-includes/blocks/verse/block.json | 4 + src/wp-includes/blocks/video.php | 25 + src/wp-includes/blocks/video/block.json | 17 +- src/wp-includes/build/constants.php | 2 +- .../pages/font-library/page-wp-admin.php | 14 +- .../build/pages/font-library/page.php | 2 +- .../options-connectors/page-wp-admin.php | 14 +- .../build/pages/options-connectors/page.php | 2 +- .../build/routes/connectors-home/content.js | 10521 +++--- .../connectors-home/content.min.asset.php | 2 +- .../routes/connectors-home/content.min.js | 2 +- .../build/routes/font-list/content.js | 29545 ++++++++++++---- .../routes/font-list/content.min.asset.php | 2 +- .../build/routes/font-list/content.min.js | 14 +- .../build/scripts/core-actions/index.php | 100 + .../page/set-as-homepage/action.php | 15 + .../page/set-as-posts-page/action.php | 15 + .../post_type_supports/delete-post/action.php | 17 + .../duplicate-post/action.php | 15 + .../move-to-trash/action.php | 16 + .../post_type_supports/order-pages/action.php | 14 + .../permanently-delete/action.php | 16 + .../post_type_supports/rename-post/action.php | 14 + .../post_type_supports/reset-post/action.php | 15 + .../post_type_supports/restore/action.php | 14 + .../view-post-revisions/action.php | 14 + .../post_type_supports/view-post/action.php | 13 + .../wp_block/duplicate-pattern/action.php | 14 + .../wp_block/export-pattern/action.php | 12 + .../duplicate-template-part/action.php | 14 + .../core-fields/attachment/alt_text/field.php | 19 + .../attachment/attached_to/field.php | 15 + .../core-fields/attachment/author/field.php | 18 + .../core-fields/attachment/caption/field.php | 19 + .../core-fields/attachment/date/field.php | 16 + .../attachment/description/field.php | 19 + .../core-fields/attachment/filename/field.php | 16 + .../core-fields/attachment/filesize/field.php | 16 + .../scripts/core-fields/attachment/index.php | 19 + .../attachment/media_dimensions/field.php | 16 + .../attachment/media_thumbnail/field.php | 15 + .../attachment/mime_type/field.php | 18 + .../core-fields/attachment/modified/field.php | 16 + .../core-fields/attachment/title/field.php | 16 + .../build/scripts/core-fields/index.php | 149 + .../build/scripts/core-fields/page/index.php | 19 + .../scripts/core-fields/page/title/field.php | 17 + .../post_type_supports/author/field.php | 16 + .../comment_status/field.php | 28 + .../post_type_supports/date/field.php | 16 + .../post_type_supports/discussion/field.php | 14 + .../post_type_supports/excerpt/field.php | 19 + .../featured_media/field.php | 16 + .../post_type_supports/format/field.php | 47 + .../last_edited_date/field.php | 17 + .../post_type_supports/notes/field.php | 18 + .../post_type_supports/parent/field.php | 15 + .../post_type_supports/password/field.php | 16 + .../post_type_supports/ping_status/field.php | 18 + .../post_content_info/field.php | 21 + .../scheduled_date/field.php | 21 + .../post_type_supports/slug/field.php | 18 + .../post_type_supports/status/field.php | 52 + .../post_type_supports/sticky/field.php | 16 + .../post_type_supports/template/field.php | 19 + .../post_type_supports/title/field.php | 18 + .../root_site/description/field.php | 13 + .../scripts/core-fields/root_site/index.php | 17 + .../core-fields/root_site/site_icon/field.php | 14 + .../core-fields/root_site/site_logo/field.php | 14 + .../core-fields/root_site/title/field.php | 13 + .../wp_block/description/field.php | 22 + .../scripts/core-fields/wp_block/index.php | 16 + .../wp_block/sync_status/field.php | 32 + .../core-fields/wp_block/title/field.php | 15 + .../core-fields/wp_template/author/field.php | 16 + .../default_comment_status/field.php | 28 + .../wp_template/description/field.php | 21 + .../description_readonly/field.php | 17 + .../scripts/core-fields/wp_template/index.php | 21 + .../wp_template/posts_page_title/field.php | 16 + .../wp_template/posts_per_page/field.php | 17 + .../core-fields/wp_template/title/field.php | 16 + .../wp_template_part/author/field.php | 21 + .../core-fields/wp_template_part/index.php | 21 + .../wp_template_part/title/field.php | 15 + .../images/icon-library/arrow-down-left.svg | 2 +- .../images/icon-library/arrow-down-right.svg | 2 +- .../images/icon-library/arrow-down.svg | 2 +- .../images/icon-library/arrow-left.svg | 2 +- .../images/icon-library/arrow-right.svg | 2 +- .../images/icon-library/arrow-up-left.svg | 2 +- .../images/icon-library/arrow-up-right.svg | 2 +- .../images/icon-library/arrow-up.svg | 2 +- .../images/icon-library/at-symbol.svg | 2 +- src/wp-includes/images/icon-library/audio.svg | 2 +- src/wp-includes/images/icon-library/bell.svg | 4 +- .../images/icon-library/block-default.svg | 3 +- .../images/icon-library/block-meta.svg | 2 +- .../images/icon-library/block-table.svg | 2 +- src/wp-includes/images/icon-library/brush.svg | 2 +- .../images/icon-library/calendar.svg | 4 +- .../images/icon-library/capture-photo.svg | 4 +- .../images/icon-library/capture-video.svg | 4 +- src/wp-includes/images/icon-library/cart.svg | 6 +- .../images/icon-library/category.svg | 8 +- .../images/icon-library/caution.svg | 2 +- .../images/icon-library/chart-bar.svg | 2 +- src/wp-includes/images/icon-library/check.svg | 2 +- .../icon-library/chevron-down-small.svg | 2 +- .../images/icon-library/chevron-down.svg | 2 +- .../icon-library/chevron-left-small.svg | 2 +- .../images/icon-library/chevron-left.svg | 2 +- .../icon-library/chevron-right-small.svg | 2 +- .../images/icon-library/chevron-right.svg | 2 +- .../images/icon-library/chevron-up-down.svg | 2 +- .../images/icon-library/chevron-up-small.svg | 2 +- .../images/icon-library/chevron-up.svg | 2 +- .../images/icon-library/comment.svg | 2 +- src/wp-includes/images/icon-library/cover.svg | 2 +- .../images/icon-library/create.svg | 2 +- .../images/icon-library/dashboard.svg | 2 +- .../images/icon-library/desktop.svg | 2 +- .../images/icon-library/download.svg | 2 +- .../images/icon-library/drawer-left.svg | 2 +- .../images/icon-library/drawer-right.svg | 2 +- .../images/icon-library/envelope.svg | 2 +- src/wp-includes/images/icon-library/error.svg | 2 +- .../images/icon-library/external.svg | 2 +- src/wp-includes/images/icon-library/file.svg | 2 +- .../images/icon-library/gallery.svg | 4 +- src/wp-includes/images/icon-library/group.svg | 4 +- .../images/icon-library/heading.svg | 2 +- src/wp-includes/images/icon-library/help.svg | 4 +- src/wp-includes/images/icon-library/home.svg | 2 +- src/wp-includes/images/icon-library/image.svg | 2 +- src/wp-includes/images/icon-library/info.svg | 2 +- src/wp-includes/images/icon-library/key.svg | 2 +- .../images/icon-library/language.svg | 4 +- src/wp-includes/images/icon-library/link.svg | 2 +- .../images/icon-library/map-marker.svg | 2 +- src/wp-includes/images/icon-library/media.svg | 12 +- src/wp-includes/images/icon-library/menu.svg | 2 +- .../images/icon-library/mobile.svg | 2 +- .../images/icon-library/more-horizontal.svg | 6 +- .../images/icon-library/more-vertical.svg | 6 +- src/wp-includes/images/icon-library/next.svg | 2 +- src/wp-includes/images/icon-library/page.svg | 2 +- .../images/icon-library/paragraph.svg | 4 +- .../images/icon-library/payment.svg | 4 +- .../images/icon-library/pencil.svg | 4 +- .../images/icon-library/people.svg | 2 +- src/wp-includes/images/icon-library/pin.svg | 2 +- .../images/icon-library/plugins.svg | 2 +- .../images/icon-library/plus-circle.svg | 4 +- src/wp-includes/images/icon-library/plus.svg | 2 +- .../images/icon-library/previous.svg | 2 +- .../images/icon-library/published.svg | 4 +- src/wp-includes/images/icon-library/quote.svg | 2 +- .../images/icon-library/receipt.svg | 2 +- src/wp-includes/images/icon-library/rss.svg | 4 +- .../images/icon-library/scheduled.svg | 4 +- .../images/icon-library/search.svg | 2 +- .../images/icon-library/settings.svg | 2 +- .../images/icon-library/shadow.svg | 2 +- src/wp-includes/images/icon-library/share.svg | 2 +- .../images/icon-library/shield.svg | 2 +- .../images/icon-library/shuffle.svg | 2 +- src/wp-includes/images/icon-library/sites.svg | 6 +- .../images/icon-library/star-empty.svg | 2 +- .../images/icon-library/star-filled.svg | 2 +- .../images/icon-library/star-half.svg | 2 +- src/wp-includes/images/icon-library/store.svg | 2 +- .../images/icon-library/styles.svg | 2 +- .../images/icon-library/symbol-filled.svg | 4 +- .../images/icon-library/symbol.svg | 4 +- src/wp-includes/images/icon-library/table.svg | 2 +- .../images/icon-library/tablet.svg | 2 +- src/wp-includes/images/icon-library/tag.svg | 2 +- src/wp-includes/images/icon-library/tip.svg | 2 +- src/wp-includes/images/icon-library/tool.svg | 2 +- .../images/icon-library/update.svg | 2 +- .../images/icon-library/upload.svg | 2 +- src/wp-includes/images/icon-library/verse.svg | 6 +- 264 files changed, 30631 insertions(+), 13020 deletions(-) create mode 100644 src/wp-includes/build/scripts/core-actions/index.php create mode 100644 src/wp-includes/build/scripts/core-actions/page/set-as-homepage/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/page/set-as-posts-page/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/delete-post/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/duplicate-post/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/move-to-trash/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/order-pages/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/permanently-delete/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/rename-post/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/reset-post/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/restore/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/view-post-revisions/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/post_type_supports/view-post/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/wp_block/duplicate-pattern/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/wp_block/export-pattern/action.php create mode 100644 src/wp-includes/build/scripts/core-actions/wp_template_part/duplicate-template-part/action.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/alt_text/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/attached_to/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/author/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/caption/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/date/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/description/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/filename/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/filesize/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/media_dimensions/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/media_thumbnail/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/mime_type/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/modified/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/attachment/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/page/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/page/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/author/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/comment_status/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/date/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/discussion/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/excerpt/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/featured_media/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/format/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/last_edited_date/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/notes/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/parent/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/password/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/ping_status/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/post_content_info/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/scheduled_date/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/slug/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/status/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/sticky/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/template/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/post_type_supports/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/root_site/description/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/root_site/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/root_site/site_icon/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/root_site/site_logo/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/root_site/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_block/description/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_block/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_block/sync_status/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_block/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/author/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/default_comment_status/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/description/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/description_readonly/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/posts_page_title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/posts_per_page/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template/title/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template_part/author/field.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template_part/index.php create mode 100644 src/wp-includes/build/scripts/core-fields/wp_template_part/title/field.php diff --git a/package.json b/package.json index eb165bda5b0f0..b17eb7a7c6008 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "url": "https://develop.svn.wordpress.org/trunk" }, "gutenberg": { - "sha": "954fe7f9e04631c41aec4552acd627a7203a2aad", + "sha": "2a96cb241e138e39035c5afbc65ec1ff5e2f6cb0", "ghcrRepo": "WordPress/gutenberg/gutenberg-wp-develop-build" }, "engines": { diff --git a/src/wp-includes/assets/icon-library-manifest.php b/src/wp-includes/assets/icon-library-manifest.php index 6af5b7d421051..10b34a0b86a83 100644 --- a/src/wp-includes/assets/icon-library-manifest.php +++ b/src/wp-includes/assets/icon-library-manifest.php @@ -49,16 +49,18 @@ 'label' => _x( 'At Symbol (@)', 'icon label' ), 'filePath' => 'at-symbol.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'email', 'icon keyword' ) ), ), 'audio' => array( 'label' => _x( 'Audio', 'icon label' ), 'filePath' => 'audio.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), + 'keywords' => array( _x( 'music', 'icon keyword' ), _x( 'note', 'icon keyword' ) ), ), 'bell' => array( 'label' => _x( 'Bell', 'icon label' ), 'filePath' => 'bell.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'block-default' => array( 'label' => _x( 'Block Default', 'icon label' ), @@ -83,7 +85,7 @@ 'calendar' => array( 'label' => _x( 'Calendar', 'icon label' ), 'filePath' => 'calendar.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'capture-photo' => array( 'label' => _x( 'Capture Photo', 'icon label' ), @@ -98,7 +100,7 @@ 'cart' => array( 'label' => _x( 'Cart', 'icon label' ), 'filePath' => 'cart.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'category' => array( 'label' => _x( 'Category', 'icon label' ), @@ -109,11 +111,12 @@ 'label' => _x( 'Caution', 'icon label' ), 'filePath' => 'caution.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'alert', 'icon keyword' ), _x( 'exclamation', 'icon keyword' ), _x( 'warning', 'icon keyword' ) ), ), 'chart-bar' => array( 'label' => _x( 'Chart Bar', 'icon label' ), 'filePath' => 'chart-bar.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'check' => array( 'label' => _x( 'Check', 'icon label' ), @@ -179,6 +182,7 @@ 'label' => _x( 'Create', 'icon label' ), 'filePath' => 'create.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'add', 'icon keyword' ), _x( 'new', 'icon keyword' ), _x( 'plus', 'icon keyword' ) ), ), 'dashboard' => array( 'label' => _x( 'Dashboard', 'icon label' ), @@ -208,12 +212,14 @@ 'envelope' => array( 'label' => _x( 'Envelope', 'icon label' ), 'filePath' => 'envelope.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), + 'keywords' => array( _x( 'email', 'icon keyword' ), _x( 'letter', 'icon keyword' ), _x( 'post', 'icon keyword' ) ), ), 'error' => array( 'label' => _x( 'Error', 'icon label' ), 'filePath' => 'error.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'alert', 'icon keyword' ), _x( 'caution', 'icon keyword' ), _x( 'exclamation', 'icon keyword' ), _x( 'warning', 'icon keyword' ) ), ), 'external' => array( 'label' => _x( 'External', 'icon label' ), @@ -224,11 +230,12 @@ 'label' => _x( 'File', 'icon label' ), 'filePath' => 'file.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'archive', 'icon keyword' ), _x( 'document', 'icon keyword' ), _x( 'folder', 'icon keyword' ) ), ), 'gallery' => array( 'label' => _x( 'Gallery', 'icon label' ), 'filePath' => 'gallery.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'group' => array( 'label' => _x( 'Group', 'icon label' ), @@ -243,7 +250,7 @@ 'help' => array( 'label' => _x( 'Help', 'icon label' ), 'filePath' => 'help.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'home' => array( 'label' => _x( 'Home', 'icon label' ), @@ -253,7 +260,7 @@ 'image' => array( 'label' => _x( 'Image', 'icon label' ), 'filePath' => 'image.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'info' => array( 'label' => _x( 'Info', 'icon label' ), @@ -278,7 +285,7 @@ 'map-marker' => array( 'label' => _x( 'Map Marker', 'icon label' ), 'filePath' => 'map-marker.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'media' => array( 'label' => _x( 'Media', 'icon label' ), @@ -323,12 +330,13 @@ 'payment' => array( 'label' => _x( 'Payment', 'icon label' ), 'filePath' => 'payment.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'pencil' => array( 'label' => _x( 'Pencil', 'icon label' ), 'filePath' => 'pencil.svg', 'collections' => array( 'core', 'core-admin' ), + 'keywords' => array( _x( 'edit', 'icon keyword' ) ), ), 'people' => array( 'label' => _x( 'People', 'icon label' ), @@ -368,17 +376,17 @@ 'quote' => array( 'label' => _x( 'Quote', 'icon label' ), 'filePath' => 'quote.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'receipt' => array( 'label' => _x( 'Receipt', 'icon label' ), 'filePath' => 'receipt.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'rss' => array( 'label' => _x( 'RSS', 'icon label' ), 'filePath' => 'rss.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'scheduled' => array( 'label' => _x( 'Scheduled', 'icon label' ), @@ -403,12 +411,12 @@ 'share' => array( 'label' => _x( 'Share', 'icon label' ), 'filePath' => 'share.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'shield' => array( 'label' => _x( 'Shield', 'icon label' ), 'filePath' => 'shield.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'shuffle' => array( 'label' => _x( 'Shuffle', 'icon label' ), @@ -429,6 +437,7 @@ 'label' => _x( 'Star Filled', 'icon label' ), 'filePath' => 'star-filled.svg', 'collections' => array( 'core' ), + 'keywords' => array( _x( 'favorite', 'icon keyword' ) ), ), 'star-half' => array( 'label' => _x( 'Star Half', 'icon label' ), @@ -438,7 +447,7 @@ 'store' => array( 'label' => _x( 'Store', 'icon label' ), 'filePath' => 'store.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'styles' => array( 'label' => _x( 'Styles', 'icon label' ), @@ -468,7 +477,7 @@ 'tag' => array( 'label' => _x( 'Tag', 'icon label' ), 'filePath' => 'tag.svg', - 'collections' => array( 'core' ), + 'collections' => array( 'core', 'core-admin' ), ), 'tip' => array( 'label' => _x( 'Tip', 'icon label' ), diff --git a/src/wp-includes/assets/script-loader-packages.php b/src/wp-includes/assets/script-loader-packages.php index 130c022842931..c5049e6d1ad09 100644 --- a/src/wp-includes/assets/script-loader-packages.php +++ b/src/wp-includes/assets/script-loader-packages.php @@ -67,7 +67,7 @@ 'wp-url', 'wp-warning' ), - 'version' => '56da94f1b6ed56573e78' + 'version' => '2cd675763a8218c966ec' ), 'block-editor.js' => array( 'dependencies' => array( @@ -105,7 +105,7 @@ 'wp-url', 'wp-warning' ), - 'version' => '578ad9eb61ae8af0123d' + 'version' => 'da6e796949fcb911a90f' ), 'block-library.js' => array( 'dependencies' => array( @@ -151,7 +151,7 @@ 'import' => 'dynamic' ) ), - 'version' => '0999d15ea60b2ca101bc' + 'version' => 'f71a7a01a0cb1ba6e9a1' ), 'block-serialization-default-parser.js' => array( 'dependencies' => array( @@ -184,7 +184,7 @@ 'wp-shortcode', 'wp-warning' ), - 'version' => 'c453468c6e4fc4dacf25' + 'version' => '8e35a0edaf0ac8ebf0fb' ), 'commands.js' => array( 'dependencies' => array( @@ -201,7 +201,7 @@ 'wp-primitives', 'wp-private-apis' ), - 'version' => '4a4a518ce9c4838666fe' + 'version' => 'bfadfc4e96de15d6f689' ), 'components.js' => array( 'dependencies' => array( @@ -226,11 +226,10 @@ 'wp-theme', 'wp-warning' ), - 'version' => 'fecbc7a454bacff1527c' + 'version' => '94fead7ee78ffbb72667' ), 'compose.js' => array( 'dependencies' => array( - 'react', 'react-jsx-runtime', 'wp-deprecated', 'wp-dom', @@ -241,7 +240,7 @@ 'wp-private-apis', 'wp-undo-manager' ), - 'version' => '18547cd8d2e6674f7b98' + 'version' => '2c882cef80a075bd7c3d' ), 'core-commands.js' => array( 'dependencies' => array( @@ -258,7 +257,7 @@ 'wp-router', 'wp-url' ), - 'version' => '738a4beca871c27833f1' + 'version' => '8839a8b3ea5cc4eb698e' ), 'core-data.js' => array( 'dependencies' => array( @@ -271,6 +270,7 @@ 'wp-data', 'wp-deprecated', 'wp-element', + 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-notices', @@ -278,12 +278,11 @@ 'wp-private-apis', 'wp-rich-text', 'wp-style-engine', - 'wp-sync', 'wp-undo-manager', 'wp-url', 'wp-warning' ), - 'version' => 'a1d74a5d3b46ac893e05' + 'version' => 'd2b82f6369d5503e99bb' ), 'customize-widgets.js' => array( 'dependencies' => array( @@ -319,7 +318,7 @@ 'import' => 'static' ) ), - 'version' => 'dcab974f036fbee73086' + 'version' => '50c2d8139dc39c1e047a' ), 'data.js' => array( 'dependencies' => array( @@ -347,7 +346,7 @@ 'moment', 'wp-deprecated' ), - 'version' => '8173fc0fc12b7bb7eaf0' + 'version' => 'af0f72250d99230696c5' ), 'deprecated.js' => array( 'dependencies' => array( @@ -359,7 +358,7 @@ 'dependencies' => array( 'wp-deprecated' ), - 'version' => 'd72034b45da1dc03f5fb' + 'version' => 'd9b90cd4513d2092fd3c' ), 'dom-ready.js' => array( 'dependencies' => array( @@ -410,7 +409,7 @@ 'import' => 'static' ) ), - 'version' => 'b4b1fb53ee012caa42fb' + 'version' => 'f1cb12c1fd14293aec82' ), 'edit-site.js' => array( 'dependencies' => array( @@ -450,8 +449,7 @@ 'wp-theme', 'wp-url', 'wp-warning', - 'wp-widgets', - 'wp-wordcount' + 'wp-widgets' ), 'module_dependencies' => array( array( @@ -459,7 +457,7 @@ 'import' => 'static' ) ), - 'version' => '1d1116c276ed95618737' + 'version' => 'c8492f0430258646a606' ), 'edit-widgets.js' => array( 'dependencies' => array( @@ -501,7 +499,7 @@ 'import' => 'static' ) ), - 'version' => 'b66dc643708560ba6922' + 'version' => '3a5bc43bc625e0f5774a' ), 'editor.js' => array( 'dependencies' => array( @@ -523,7 +521,6 @@ 'wp-deprecated', 'wp-dom', 'wp-element', - 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', @@ -552,7 +549,7 @@ 'import' => 'static' ) ), - 'version' => '021570d5ceb676201638' + 'version' => 'd029bd62a89843653c78' ), 'element.js' => array( 'dependencies' => array( @@ -560,7 +557,7 @@ 'react-dom', 'wp-escape-html' ), - 'version' => '4822427ad9c627826631' + 'version' => '5a4b1aa0e42aad8ef7db' ), 'escape-html.js' => array( 'dependencies' => array( @@ -594,7 +591,7 @@ 'import' => 'dynamic' ) ), - 'version' => 'f9ce39fe8a3d8aaf2fd6' + 'version' => '80364106dc5cdf6f30e0' ), 'hooks.js' => array( 'dependencies' => array( @@ -627,7 +624,7 @@ 'wp-element', 'wp-keycodes' ), - 'version' => 'ba3985e44a8fdca9462f' + 'version' => '2871b9b36c2a9aef74c4' ), 'keycodes.js' => array( 'dependencies' => array( @@ -662,6 +659,7 @@ 'wp-date', 'wp-deprecated', 'wp-element', + 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-notices', @@ -672,15 +670,26 @@ 'wp-url', 'wp-warning' ), - 'version' => '64a4692e40e4685caccd' + 'version' => 'e41aab29ecfcd5067912' ), 'notices.js' => array( 'dependencies' => array( + 'react', + 'react-dom', 'react-jsx-runtime', + 'wp-a11y', 'wp-components', - 'wp-data' + 'wp-compose', + 'wp-data', + 'wp-deprecated', + 'wp-element', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' ), - 'version' => 'c09a068fdab0eb465e14' + 'version' => '50f64da182bc1a7417c8' ), 'nux.js' => array( 'dependencies' => array( @@ -711,7 +720,7 @@ 'wp-url', 'wp-warning' ), - 'version' => 'bb86130c876014c4db11' + 'version' => 'ced901bac0e8160bedae' ), 'plugins.js' => array( 'dependencies' => array( @@ -723,7 +732,7 @@ 'wp-is-shallow-equal', 'wp-primitives' ), - 'version' => 'a03a674fba0130981f2d' + 'version' => '4c53884fe0283a7ee4c5' ), 'preferences.js' => array( 'dependencies' => array( @@ -739,13 +748,13 @@ 'wp-primitives', 'wp-private-apis' ), - 'version' => 'f0a611a312173e6b7648' + 'version' => '00228fed7f2d274a78ab' ), 'preferences-persistence.js' => array( 'dependencies' => array( 'wp-api-fetch' ), - 'version' => 'a34abbdacd8f50f9acb1' + 'version' => '63049460a74cb385aa0a' ), 'primitives.js' => array( 'dependencies' => array( @@ -764,7 +773,7 @@ 'dependencies' => array( ), - 'version' => 'df3615fd5cb379fb7ffe' + 'version' => '81dd8f4c0e768e396025' ), 'react-i18n.js' => array( 'dependencies' => array( @@ -800,7 +809,7 @@ 'wp-keycodes', 'wp-private-apis' ), - 'version' => 'd04c731671320d12f4e9' + 'version' => '961958792146058e5f18' ), 'router.js' => array( 'dependencies' => array( @@ -824,7 +833,7 @@ 'wp-i18n', 'wp-url' ), - 'version' => '83e806a0634df6b93530' + 'version' => '93e0b8da5b06ea5406ab' ), 'shortcode.js' => array( 'dependencies' => array( @@ -836,15 +845,7 @@ 'dependencies' => array( ), - 'version' => '914befb08774033e6265' - ), - 'sync.js' => array( - 'dependencies' => array( - 'wp-api-fetch', - 'wp-hooks', - 'wp-private-apis' - ), - 'version' => '7179e38284ff7aba21e6' + 'version' => '6500509f32193fe14392' ), 'theme.js' => array( 'dependencies' => array( @@ -854,7 +855,7 @@ 'wp-element', 'wp-private-apis' ), - 'version' => '795a74a11c336f95159b' + 'version' => '21b0c3318f26c7b3d71e' ), 'token-list.js' => array( 'dependencies' => array( @@ -876,8 +877,7 @@ 'wp-data', 'wp-element', 'wp-i18n', - 'wp-private-apis', - 'wp-url' + 'wp-private-apis' ), 'module_dependencies' => array( array( @@ -889,13 +889,13 @@ 'import' => 'dynamic' ) ), - 'version' => '86068d5116da27ca40a5' + 'version' => '4bd74c9d859a5ffe1e31' ), 'url.js' => array( 'dependencies' => array( ), - 'version' => '65e1ab123ef3bf825dea' + 'version' => '3c0d9743cc6cd9591281' ), 'viewport.js' => array( 'dependencies' => array( @@ -913,7 +913,10 @@ ), 'widgets.js' => array( 'dependencies' => array( + 'react', + 'react-dom', 'react-jsx-runtime', + 'wp-a11y', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', @@ -924,9 +927,12 @@ 'wp-element', 'wp-i18n', 'wp-notices', - 'wp-primitives' + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' ), - 'version' => 'f2802a2d8ac37b86bc43' + 'version' => '809cf178473fab752cef' ), 'wordcount.js' => array( 'dependencies' => array( diff --git a/src/wp-includes/assets/script-modules-packages.php b/src/wp-includes/assets/script-modules-packages.php index b000cd3d852b6..fc801f0ae0590 100644 --- a/src/wp-includes/assets/script-modules-packages.php +++ b/src/wp-includes/assets/script-modules-packages.php @@ -10,7 +10,7 @@ 'wp-data', 'wp-i18n' ), - 'version' => 'e5fcd7e304ead0fc9779' + 'version' => '9448bb3053516a39086d' ), 'block-editor/utils/fit-text-frontend.js' => array( 'dependencies' => array( @@ -36,6 +36,18 @@ ), 'version' => '6d4bb8df9c7539843ad3' ), + 'block-library/cover/view.js' => array( + 'dependencies' => array( + + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/interactivity', + 'import' => 'static' + ) + ), + 'version' => 'ef26735bd1dbc6c830a0' + ), 'block-library/file/view.js' => array( 'dependencies' => array( @@ -58,7 +70,7 @@ 'import' => 'static' ) ), - 'version' => '25ee935fd6c67371d0f3' + 'version' => 'b5457389a410156f7ef6' ), 'block-library/navigation/view.js' => array( 'dependencies' => array( @@ -82,7 +94,7 @@ 'import' => 'static' ) ), - 'version' => 'fde7e3309a48521aed56' + 'version' => 'dcead465d87ec21eea99' ), 'block-library/query/view.js' => array( 'dependencies' => array( @@ -124,6 +136,18 @@ ), 'version' => '0684c8bb3dc8582b92ea' ), + 'block-library/video/view.js' => array( + 'dependencies' => array( + + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/interactivity', + 'import' => 'static' + ) + ), + 'version' => 'a75554c34e6430cbfdd5' + ), 'boot/index.js' => array( 'dependencies' => array( 'react', @@ -162,7 +186,7 @@ 'import' => 'static' ) ), - 'version' => 'f6c3c0c637fe0ed2dbd2' + 'version' => 'd8489a9113a214b5278c' ), 'connectors/index.js' => array( 'dependencies' => array( @@ -188,6 +212,222 @@ ), 'version' => '012760fd849397dd0031' ), + 'core-actions/page.js' => array( + 'dependencies' => array( + 'react-jsx-runtime', + 'wp-components', + 'wp-core-data', + 'wp-data', + 'wp-html-entities', + 'wp-i18n', + 'wp-notices' + ), + 'version' => 'b7ea80c796cbace5c19c' + ), + 'core-actions/post_type_supports.js' => array( + 'dependencies' => array( + 'react-jsx-runtime', + 'wp-api-fetch', + 'wp-blocks', + 'wp-components', + 'wp-core-data', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-notices', + 'wp-patterns', + 'wp-primitives', + 'wp-private-apis', + 'wp-url' + ), + 'version' => 'c68e5c9a605e907ca528' + ), + 'core-actions/wp_block.js' => array( + 'dependencies' => array( + 'react-jsx-runtime', + 'wp-blob', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-patterns', + 'wp-primitives', + 'wp-private-apis' + ), + 'version' => 'fc39ba4261c0c13a18da' + ), + 'core-actions/wp_template_part.js' => array( + 'dependencies' => array( + 'react-jsx-runtime', + 'wp-blocks', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-notices', + 'wp-patterns', + 'wp-private-apis' + ), + 'version' => 'c8126a6b5289ae455bf2' + ), + 'core-fields/attachment.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-components', + 'wp-compose', + 'wp-core-data', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-url', + 'wp-warning' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => '3a28135598a5699ab7f1' + ), + 'core-fields/page.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-compose', + 'wp-core-data', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => 'cc78f2192db7a69e99c1' + ), + 'core-fields/post_type_supports.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-components', + 'wp-compose', + 'wp-core-data', + 'wp-data', + 'wp-date', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-media-utils', + 'wp-notices', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-url', + 'wp-warning', + 'wp-wordcount' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => 'af71ae1624cb6d030f74' + ), + 'core-fields/root_site.js' => array( + 'dependencies' => array( + 'wp-html-entities', + 'wp-media-utils', + 'wp-private-apis' + ), + 'version' => 'e94092be498b0e24d377' + ), + 'core-fields/wp_block.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-compose', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => '98921ca4890d4baead31' + ), + 'core-fields/wp_template.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-compose', + 'wp-core-data', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => 'd94f646d08e9c4a6ba08' + ), + 'core-fields/wp_template_part.js' => array( + 'dependencies' => array( + 'react', + 'react-dom', + 'react-jsx-runtime', + 'wp-compose', + 'wp-core-data', + 'wp-data', + 'wp-element', + 'wp-html-entities', + 'wp-i18n', + 'wp-primitives', + 'wp-private-apis', + 'wp-theme', + 'wp-warning' + ), + 'module_dependencies' => array( + array( + 'id' => '@wordpress/a11y', + 'import' => 'static' + ) + ), + 'version' => 'b528ca2741b1c38e32ff' + ), 'dashboard-init/index.js' => array( 'dependencies' => array( 'react', @@ -219,13 +459,13 @@ 'import' => 'static' ) ), - 'version' => '9e49b9edcfc16dc5203c' + 'version' => '4b2e3230d57e16afb0ad' ), 'interactivity/index.js' => array( 'dependencies' => array( ), - 'version' => 'b0929e0bd08daccaa778' + 'version' => '38309185015c9ca20405' ), 'interactivity-router/full-page.js' => array( 'dependencies' => array( @@ -253,13 +493,13 @@ 'import' => 'static' ) ), - 'version' => '711aa380e66c57009b7a' + 'version' => '3ee11de34faed693e7a5' ), 'latex-to-mathml/index.js' => array( 'dependencies' => array( ), - 'version' => '662cb0e7f68d2a21cd68' + 'version' => '4ec5f96f5cf884f76516' ), 'latex-to-mathml/loader.js' => array( 'dependencies' => array( @@ -287,7 +527,7 @@ 'wp-private-apis', 'wp-style-engine' ), - 'version' => 'e50dce77f58e6e3ffa9b' + 'version' => 'b1449ad638987b6a4b90' ), 'route/index.js' => array( 'dependencies' => array( @@ -314,7 +554,7 @@ 'dependencies' => array( ), - 'version' => '4d3627c45251ceb4f9a2' + 'version' => 'fa777d9ac60940d2e501' ), 'vips/loader.js' => array( 'dependencies' => array( @@ -332,14 +572,14 @@ 'dependencies' => array( ), - 'version' => '7e33cd8c4128731126e8' + 'version' => '4aef10eaf28026b90482' ), 'widget-primitives/index.js' => array( 'dependencies' => array( 'react-jsx-runtime', 'wp-element' ), - 'version' => '3047de39283129372300' + 'version' => '355066e7b5d458a5968d' ), 'workflow/index.js' => array( 'dependencies' => array( @@ -350,6 +590,7 @@ 'wp-compose', 'wp-data', 'wp-element', + 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', @@ -372,6 +613,6 @@ 'import' => 'dynamic' ) ), - 'version' => '02fa947b818b1e4c07fd' + 'version' => 'fc89488ffb88c0560c75' ) ); \ No newline at end of file diff --git a/src/wp-includes/blocks/accordion-heading/block.json b/src/wp-includes/blocks/accordion-heading/block.json index 8f51644120d41..c301c6c1b7e5e 100644 --- a/src/wp-includes/blocks/accordion-heading/block.json +++ b/src/wp-includes/blocks/accordion-heading/block.json @@ -13,6 +13,9 @@ ], "supports": { "anchor": true, + "background": { + "gradient": true + }, "color": { "background": true, "gradients": true @@ -45,6 +48,7 @@ "letterSpacing" ], "fontSize": true, + "lineHeight": true, "__experimentalFontFamily": true, "__experimentalFontWeight": true, "__experimentalFontStyle": true, diff --git a/src/wp-includes/blocks/archives/block.json b/src/wp-includes/blocks/archives/block.json index 7455b558c0d18..b0063dd06663b 100644 --- a/src/wp-includes/blocks/archives/block.json +++ b/src/wp-includes/blocks/archives/block.json @@ -42,6 +42,9 @@ "gradient": true } }, + "dimensions": { + "minHeight": true + }, "html": false, "spacing": { "margin": true, diff --git a/src/wp-includes/blocks/avatar.php b/src/wp-includes/blocks/avatar.php index 196d67a4d480c..c257175432685 100644 --- a/src/wp-includes/blocks/avatar.php +++ b/src/wp-includes/blocks/avatar.php @@ -19,6 +19,7 @@ function render_block_core_avatar( $attributes, $content, $block ) { $size = $attributes['size'] ?? 96; $wrapper_attributes = get_block_wrapper_attributes(); $border_attributes = get_block_core_avatar_border_attributes( $attributes ); + $shadow_styles = wp_style_engine_get_styles( array( 'shadow' => $attributes['style']['shadow'] ?? null ) ); // Class gets passed through `esc_attr` via `get_avatar`. $image_classes = ! empty( $border_attributes['class'] ) @@ -26,10 +27,11 @@ function render_block_core_avatar( $attributes, $content, $block ) { : 'wp-block-avatar__image'; // Unlike class, `get_avatar` doesn't filter the styles via `esc_attr`. - // The style engine does pass the border styles through + // The style engine does pass the border and shadow styles through // `safecss_filter_attr` however. - $image_styles = ! empty( $border_attributes['style'] ) - ? sprintf( ' style="%s"', esc_attr( $border_attributes['style'] ) ) + $image_css = ( $border_attributes['style'] ?? '' ) . ( $shadow_styles['css'] ?? '' ); + $image_styles = ! empty( $image_css ) + ? sprintf( ' style="%s"', esc_attr( $image_css ) ) : ''; if ( ! isset( $block->context['commentId'] ) ) { diff --git a/src/wp-includes/blocks/avatar/block.json b/src/wp-includes/blocks/avatar/block.json index 0cb92d81865c7..7d8b2ddee1f10 100644 --- a/src/wp-includes/blocks/avatar/block.json +++ b/src/wp-includes/blocks/avatar/block.json @@ -54,12 +54,16 @@ "filter": { "duotone": true }, + "shadow": { + "__experimentalSkipSerialization": true + }, "interactivity": { "clientNavigation": true } }, "selectors": { "border": ".wp-block-avatar img", + "shadow": ".wp-block-avatar img", "filter": { "duotone": ".wp-block-avatar img" } diff --git a/src/wp-includes/blocks/blocks-json.php b/src/wp-includes/blocks/blocks-json.php index 73e0bebb8fb78..ee655132af3ac 100644 --- a/src/wp-includes/blocks/blocks-json.php +++ b/src/wp-includes/blocks/blocks-json.php @@ -120,6 +120,9 @@ ), 'supports' => array( 'anchor' => true, + 'background' => array( + 'gradient' => true + ), 'color' => array( 'background' => true, 'gradients' => true @@ -152,6 +155,7 @@ 'letterSpacing' ), 'fontSize' => true, + 'lineHeight' => true, '__experimentalFontFamily' => true, '__experimentalFontWeight' => true, '__experimentalFontStyle' => true, @@ -414,6 +418,9 @@ 'gradient' => true ) ), + 'dimensions' => array( + 'minHeight' => true + ), 'html' => false, 'spacing' => array( 'margin' => true, @@ -591,12 +598,16 @@ 'filter' => array( 'duotone' => true ), + 'shadow' => array( + '__experimentalSkipSerialization' => true + ), 'interactivity' => array( 'clientNavigation' => true ) ), 'selectors' => array( 'border' => '.wp-block-avatar img', + 'shadow' => '.wp-block-avatar img', 'filter' => array( 'duotone' => '.wp-block-avatar img' ) @@ -808,6 +819,12 @@ 'splitting' => true, 'align' => false, 'alignWide' => false, + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true, + '__experimentalSkipSerialization' => true + ), 'color' => array( '__experimentalSkipSerialization' => true, 'gradients' => true, @@ -971,6 +988,10 @@ 'fontSize' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -994,7 +1015,8 @@ 'clientNavigation' => true ), 'listView' => true, - 'contentRole' => true + 'contentRole' => true, + 'shadow' => true ), 'editorStyle' => 'wp-block-buttons-editor', 'style' => 'wp-block-buttons' @@ -1196,6 +1218,10 @@ 'gradient' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -1400,6 +1426,10 @@ 'gradient' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'blockGap' => array( '__experimentalDefault' => '2em', @@ -1995,6 +2025,11 @@ 'type' => 'flex' ) ), + 'spacing' => array( + 'padding' => true, + 'margin' => true, + 'blockGap' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -2008,6 +2043,12 @@ 'fontSize' => true ) ), + '__experimentalBorder' => array( + 'color' => true, + 'radius' => true, + 'style' => true, + 'width' => true + ), 'interactivity' => array( 'clientNavigation' => true ) @@ -2064,8 +2105,21 @@ ), 'interactivity' => array( 'clientNavigation' => true + ), + 'spacing' => array( + 'padding' => true, + '__experimentalDefaultControls' => array( + 'padding' => false + ) + ), + '__experimentalBorder' => array( + 'radius' => true, + 'color' => true, + 'width' => true, + 'style' => true ) - ) + ), + 'style' => 'wp-block-comments-pagination-next' ), 'comments-pagination-numbers' => array( '$schema' => 'https://schemas.wp.org/trunk/block.json', @@ -2172,8 +2226,21 @@ ), 'interactivity' => array( 'clientNavigation' => true + ), + 'spacing' => array( + 'padding' => true, + '__experimentalDefaultControls' => array( + 'padding' => false + ) + ), + '__experimentalBorder' => array( + 'radius' => true, + 'color' => true, + 'width' => true, + 'style' => true ) - ) + ), + 'style' => 'wp-block-comments-pagination-previous' ), 'comments-title' => array( '$schema' => 'https://schemas.wp.org/trunk/block.json', @@ -2398,6 +2465,8 @@ 'color' => array( 'heading' => true, 'text' => true, + 'link' => true, + 'button' => true, 'background' => false, '__experimentalSkipSerialization' => array( 'gradients' @@ -2405,7 +2474,8 @@ 'enableContrastChecker' => false ), 'dimensions' => array( - 'aspectRatio' => true + 'aspectRatio' => true, + 'minWidth' => true ), 'typography' => array( 'fontSize' => true, @@ -2424,7 +2494,8 @@ 'allowJustification' => false ), 'interactivity' => array( - 'clientNavigation' => true + 'clientNavigation' => true, + 'interactive' => true ), 'filter' => array( 'duotone' => true @@ -2515,6 +2586,9 @@ 'padding' => false ) ), + 'dimensions' => array( + 'minWidth' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -2584,8 +2658,13 @@ 'supports' => array( 'anchor' => true, 'align' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( - 'margin' => true + 'margin' => true, + 'padding' => true ), 'interactivity' => array( 'clientNavigation' => true @@ -2678,6 +2757,10 @@ 'margin' => true, 'padding' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'color' => array( 'gradients' => true, 'link' => true, @@ -2699,6 +2782,20 @@ 'style' => true ) ), + 'shadow' => true, + 'typography' => array( + 'fontSize' => true, + 'lineHeight' => true, + '__experimentalFontFamily' => true, + '__experimentalFontWeight' => true, + '__experimentalFontStyle' => true, + '__experimentalTextTransform' => true, + '__experimentalTextDecoration' => true, + '__experimentalLetterSpacing' => true, + '__experimentalDefaultControls' => array( + 'fontSize' => true + ) + ), 'interactivity' => true ), 'editorStyle' => 'wp-block-file-editor', @@ -2764,6 +2861,10 @@ 'padding' => false ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -3005,6 +3106,11 @@ 'background' => true, 'gradients' => true ), + 'shadow' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'layout' => array( 'allowSwitching' => false, 'allowInheriting' => false, @@ -3018,11 +3124,19 @@ 'type' => 'flex' ) ), + 'filter' => array( + 'duotone' => true + ), 'interactivity' => array( 'clientNavigation' => true ), 'listView' => true ), + 'selectors' => array( + 'filter' => array( + 'duotone' => '.wp-block-gallery :where(img)' + ) + ), 'editorStyle' => 'wp-block-gallery-editor', 'style' => 'wp-block-gallery' ), @@ -3186,6 +3300,7 @@ 'className' => true, 'splitting' => true, 'background' => array( + 'backgroundClip' => true, 'backgroundImage' => true, 'backgroundSize' => true, 'gradient' => true, @@ -3208,6 +3323,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( 'margin' => true, @@ -3221,6 +3340,7 @@ 'fontSize' => true, 'lineHeight' => true, 'textAlign' => true, + 'textIndent' => true, '__experimentalFontFamily' => true, '__experimentalFontStyle' => true, '__experimentalFontWeight' => true, @@ -3299,6 +3419,10 @@ ) ), 'shadow' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -3311,6 +3435,14 @@ 'width' => false ) ), + 'spacing' => array( + 'margin' => true, + 'padding' => true, + '__experimentalDefaultControls' => array( + 'margin' => false, + 'padding' => false + ) + ), 'interactivity' => array( 'clientNavigation' => true ) @@ -3388,6 +3520,12 @@ 'center', 'right' ), + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true, + '__experimentalSkipSerialization' => true + ), 'html' => false, 'color' => array( '__experimentalSkipSerialization' => true, @@ -3399,6 +3537,9 @@ 'interactivity' => array( 'clientNavigation' => true ), + 'shadow' => array( + '__experimentalSkipSerialization' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -3425,8 +3566,12 @@ ), 'dimensions' => array( 'width' => true, + 'minWidth' => true, + 'minHeight' => true, '__experimentalSkipSerialization' => array( - 'width' + 'width', + 'minWidth', + 'minHeight' ), '__experimentalDefaultControls' => array( 'width' => true @@ -3436,6 +3581,7 @@ 'selectors' => array( 'root' => '.wp-block-icon svg', 'css' => '.wp-block-icon', + 'shadow' => '.wp-block-icon svg', 'spacing' => array( 'margin' => '.wp-block-icon' ) @@ -3674,11 +3820,21 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'html' => false, 'spacing' => array( 'margin' => true, 'padding' => true ), + '__experimentalBorder' => array( + 'color' => true, + 'radius' => true, + 'style' => true, + 'width' => true + ), 'shadow' => true, 'typography' => array( 'fontSize' => true, @@ -3804,6 +3960,10 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -3945,6 +4105,15 @@ 'fontSize' => true ) ), + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true, + '__experimentalDefaultControls' => array( + 'backgroundImage' => true, + 'gradient' => true + ) + ), 'color' => array( 'gradients' => true, 'link' => true, @@ -3953,6 +4122,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -3961,6 +4134,7 @@ 'padding' => false ) ), + 'shadow' => true, '__unstablePasteTextInline' => true, '__experimentalOnMerge' => true, '__experimentalSlashInserter' => true, @@ -3970,7 +4144,9 @@ 'listView' => true ), 'selectors' => array( - 'border' => '.wp-block-list:not(.wp-block-list .wp-block-list)' + 'border' => '.wp-block-list:not(.wp-block-list .wp-block-list)', + 'dimensions' => '.wp-block-list:not(.wp-block-list .wp-block-list)', + 'shadow' => '.wp-block-list:not(.wp-block-list .wp-block-list)' ), 'editorStyle' => 'wp-block-list-editor', 'style' => 'wp-block-list' @@ -4005,6 +4181,11 @@ 'html' => false, 'className' => false, 'splitting' => true, + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -4027,6 +4208,7 @@ 'padding' => false ) ), + 'shadow' => true, 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -4046,7 +4228,8 @@ ), 'selectors' => array( 'root' => '.wp-block-list > li', - 'border' => '.wp-block-list:not(.wp-block-list .wp-block-list) > li' + 'border' => '.wp-block-list:not(.wp-block-list .wp-block-list) > li', + 'shadow' => '.wp-block-list:not(.wp-block-list .wp-block-list) > li' ) ), 'loginout' => array( @@ -4093,6 +4276,10 @@ 'gradients' => true, 'link' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -4123,7 +4310,8 @@ 'shadow' => true, 'interactivity' => array( 'clientNavigation' => true - ) + ), + 'contentRole' => true ), 'style' => 'wp-block-loginout' ), @@ -4166,6 +4354,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( 'margin' => true, @@ -4310,6 +4502,11 @@ 'full' ), 'html' => false, + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -4332,10 +4529,15 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true ), + 'shadow' => true, 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -4349,11 +4551,19 @@ 'fontSize' => true ) ), + 'filter' => array( + 'duotone' => true + ), 'interactivity' => array( 'clientNavigation' => true ), 'allowedBlocks' => true ), + 'selectors' => array( + 'filter' => array( + 'duotone' => '.wp-block-media-text > .wp-block-media-text__media img, .wp-block-media-text > .wp-block-media-text__media video' + ) + ), 'editorStyle' => 'wp-block-media-text-editor', 'style' => 'wp-block-media-text' ), @@ -4741,6 +4951,10 @@ ) ), 'shadow' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, @@ -4749,6 +4963,7 @@ ), 'spacing' => array( 'padding' => true, + 'margin' => true, '__experimentalDefaultControls' => array( 'padding' => true ) @@ -4989,6 +5204,10 @@ ) ), 'shadow' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'contentRole' => true ), 'editorStyle' => 'wp-block-page-list-editor', @@ -5098,12 +5317,19 @@ 'splitting' => true, 'anchor' => true, 'className' => false, + 'background' => array( + 'backgroundClip' => true, + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true + ), '__experimentalBorder' => array( 'color' => true, 'radius' => true, 'style' => true, 'width' => true ), + 'shadow' => true, 'color' => array( 'gradients' => true, 'link' => true, @@ -5112,6 +5338,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -5260,6 +5490,15 @@ 'supports' => array( 'anchor' => true, 'align' => true, + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true, + '__experimentalDefaultControls' => array( + 'backgroundImage' => true, + 'gradient' => true + ) + ), 'color' => array( 'gradients' => true, 'link' => true, @@ -5278,6 +5517,10 @@ 'width' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'interactivity' => true, 'shadow' => true, 'spacing' => array( @@ -5499,6 +5742,7 @@ 'lineHeight' => true, 'textAlign' => true, 'textColumns' => true, + 'textIndent' => true, '__experimentalFontFamily' => true, '__experimentalFontWeight' => true, '__experimentalFontStyle' => true, @@ -5698,6 +5942,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true @@ -5706,6 +5954,7 @@ 'fontSize' => true, 'lineHeight' => true, 'textAlign' => true, + '__experimentalFontFamily' => true, '__experimentalFontStyle' => true, '__experimentalFontWeight' => true, '__experimentalLetterSpacing' => true, @@ -5844,7 +6093,8 @@ ) ), 'dimensions' => array( - 'minHeight' => true + 'minHeight' => true, + 'minWidth' => true ), 'shadow' => true, 'spacing' => array( @@ -6031,6 +6281,10 @@ 'margin' => true, 'padding' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -6151,6 +6405,7 @@ '__experimentalBorder' => array( 'color' => true, 'radius' => true, + 'style' => true, 'width' => true, '__experimentalSkipSerialization' => true, '__experimentalDefaultControls' => array( @@ -6226,6 +6481,12 @@ 'anchor' => true, 'reusable' => false, 'html' => false, + 'background' => array( + '__experimentalSkipSerialization' => true, + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true + ), 'color' => array( 'link' => true ), @@ -6250,6 +6511,11 @@ 'interactivity' => array( 'clientNavigation' => true ), + 'dimensions' => array( + '__experimentalSkipSerialization' => true, + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( '__experimentalSkipSerialization' => true, 'margin' => true, @@ -6274,7 +6540,9 @@ ) ), 'selectors' => array( + 'background' => '.wp-block-post-navigation-link:not(:empty)', 'border' => '.wp-block-post-navigation-link:not(:empty)', + 'dimensions' => '.wp-block-post-navigation-link:not(:empty)', 'shadow' => '.wp-block-post-navigation-link:not(:empty)', 'spacing' => '.wp-block-post-navigation-link:not(:empty)' ), @@ -6354,6 +6622,10 @@ 'margin' => false ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'interactivity' => array( 'clientNavigation' => true ), @@ -6419,6 +6691,10 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( 'margin' => true, @@ -6496,6 +6772,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'html' => false, 'shadow' => true, 'spacing' => array( @@ -6600,6 +6880,10 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( 'margin' => true, @@ -6615,6 +6899,7 @@ '__experimentalTextTransform' => true, '__experimentalTextDecoration' => true, '__experimentalLetterSpacing' => true, + 'fitText' => true, '__experimentalDefaultControls' => array( 'fontSize' => true ) @@ -6669,6 +6954,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( 'padding' => true, @@ -6754,7 +7043,8 @@ ) ), 'dimensions' => array( - 'minHeight' => true + 'minHeight' => true, + 'minWidth' => true ), 'shadow' => true, 'spacing' => array( @@ -6765,12 +7055,14 @@ 'fontSize' => true, 'lineHeight' => true, 'textAlign' => true, + 'textIndent' => true, '__experimentalFontFamily' => true, '__experimentalFontWeight' => true, '__experimentalFontStyle' => true, '__experimentalTextTransform' => true, '__experimentalTextDecoration' => true, '__experimentalLetterSpacing' => true, + '__experimentalWritingMode' => true, '__experimentalDefaultControls' => array( 'fontSize' => true ) @@ -6913,6 +7205,10 @@ 'heading' => true, 'button' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'padding' => true, 'margin' => true, @@ -6998,6 +7294,11 @@ 'link' => true ) ), + 'spacing' => array( + 'padding' => true, + 'margin' => true, + 'blockGap' => true + ), 'layout' => array( 'allowSwitching' => false, 'allowInheriting' => false, @@ -7018,6 +7319,18 @@ 'fontSize' => true ) ), + '__experimentalBorder' => array( + 'color' => true, + 'radius' => true, + 'style' => true, + 'width' => true, + '__experimentalDefaultControls' => array( + 'color' => false, + 'radius' => false, + 'style' => false, + 'width' => false + ) + ), 'interactivity' => array( 'clientNavigation' => true ) @@ -7316,6 +7629,10 @@ 'margin' => true, 'padding' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'color' => array( 'gradients' => true, '__experimentalDefaultControls' => array( @@ -7418,6 +7735,7 @@ ), 'dimensions' => array( 'minHeight' => true, + 'minWidth' => true, '__experimentalDefaultControls' => array( 'minHeight' => false ) @@ -7433,6 +7751,7 @@ '__experimentalTextTransform' => true, '__experimentalTextDecoration' => true, '__experimentalLetterSpacing' => true, + '__experimentalWritingMode' => true, '__experimentalDefaultControls' => array( 'fontSize' => true ) @@ -7441,6 +7760,7 @@ 'gradients' => true, 'heading' => true, 'link' => true, + 'button' => true, '__experimentalDefaultControls' => array( 'background' => true, 'text' => true @@ -7521,6 +7841,10 @@ 'textDecoration' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => array( 'top', @@ -7534,6 +7858,7 @@ '__experimentalBorder' => array( 'color' => true, 'radius' => true, + 'style' => true, 'width' => true, '__experimentalDefaultControls' => array( 'width' => true @@ -7621,6 +7946,10 @@ 'margin' => false ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'background' => array( 'backgroundImage' => true, @@ -7698,6 +8027,10 @@ 'center', 'right' ), + 'background' => array( + 'gradient' => true, + '__experimentalSkipSerialization' => true + ), 'color' => array( 'gradients' => true, '__experimentalSkipSerialization' => true, @@ -7725,6 +8058,7 @@ '__experimentalBorder' => array( 'color' => true, 'radius' => true, + 'style' => true, 'width' => true, '__experimentalSkipSerialization' => true, '__experimentalDefaultControls' => array( @@ -7733,8 +8067,17 @@ 'width' => true ) ), + 'shadow' => array( + '__experimentalSkipSerialization' => true + ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true, + '__experimentalSkipSerialization' => true + ), 'spacing' => array( - 'margin' => true + 'margin' => true, + 'padding' => true ), 'html' => false ), @@ -7742,7 +8085,10 @@ 'style' => 'wp-block-search', 'selectors' => array( 'color' => '.wp-block-search .wp-block-search__button, .wp-block-search.wp-block-search__no-button .wp-block-search__input', - 'border' => '.wp-block-search.wp-block-search__button-outside .wp-block-search__input, .wp-block-search.wp-block-search__button-outside .wp-block-search__button, .wp-block-search.wp-block-search__no-button .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__button, .wp-block-search.wp-block-search__button-inside .wp-block-search__inside-wrapper' + 'background' => '.wp-block-search .wp-block-search__button, .wp-block-search.wp-block-search__no-button .wp-block-search__input', + 'border' => '.wp-block-search.wp-block-search__button-outside .wp-block-search__input, .wp-block-search.wp-block-search__button-outside .wp-block-search__button, .wp-block-search.wp-block-search__no-button .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__button, .wp-block-search.wp-block-search__button-inside .wp-block-search__inside-wrapper', + 'shadow' => '.wp-block-search.wp-block-search__button-outside .wp-block-search__input, .wp-block-search.wp-block-search__button-outside .wp-block-search__button, .wp-block-search.wp-block-search__no-button .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__input, .wp-block-search.wp-block-search__button-only .wp-block-search__button, .wp-block-search.wp-block-search__button-inside .wp-block-search__inside-wrapper', + 'dimensions' => '.wp-block-search .wp-block-search__inside-wrapper' ) ), 'separator' => array( @@ -7976,6 +8322,10 @@ ) ), 'contentRole' => true, + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -8074,6 +8424,10 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'padding' => true, 'margin' => true, @@ -8274,6 +8628,7 @@ 'style' => true ) ), + 'shadow' => true, 'contentRole' => true, 'listView' => true ), @@ -8369,6 +8724,12 @@ 'ariaLabel' => true, 'visibility' => false, 'lock' => false, + 'background' => array( + 'backgroundImage' => true, + 'backgroundSize' => true, + 'gradient' => true, + '__experimentalSkipSerialization' => true + ), 'color' => array( 'background' => true, 'text' => true, @@ -8380,7 +8741,11 @@ ), 'typography' => array( 'fontSize' => true, - '__experimentalFontFamily' => true + 'lineHeight' => true, + '__experimentalFontFamily' => true, + '__experimentalFontWeight' => true, + '__experimentalTextTransform' => true, + '__experimentalLetterSpacing' => true ), '__experimentalBorder' => array( 'color' => true, @@ -8408,10 +8773,15 @@ 'padding' => true, 'blockGap' => true ) + ), + 'shadow' => array( + '__experimentalSkipSerialization' => true ) ), 'selectors' => array( + 'background' => '.wp-block-tab-list button', 'border' => '.wp-block-tab-list button', + 'shadow' => '.wp-block-tab-list button', 'color' => array( 'background' => '.wp-block-tab-list button', 'text' => '.wp-block-tab-list button' @@ -8541,6 +8911,9 @@ 'spacing' => array( 'padding' => true ), + 'dimensions' => array( + 'minHeight' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -8732,9 +9105,18 @@ 'supports' => array( 'anchor' => true, 'align' => true, - 'color' => array( + 'background' => array( '__experimentalSkipSerialization' => true, + 'gradient' => true + ), + 'color' => array( + '__experimentalSkipSerialization' => array( + 'text', + 'background', + 'gradients' + ), 'gradients' => true, + 'link' => true, '__experimentalDefaultControls' => array( 'background' => true, 'text' => true @@ -8749,6 +9131,9 @@ ) ), 'typography' => array( + '__experimentalSkipSerialization' => array( + 'textAlign' + ), 'fontSize' => true, 'lineHeight' => true, '__experimentalFontFamily' => true, @@ -8757,6 +9142,7 @@ '__experimentalLetterSpacing' => true, '__experimentalTextTransform' => true, '__experimentalTextDecoration' => true, + 'textAlign' => true, '__experimentalDefaultControls' => array( 'fontSize' => true ) @@ -8854,6 +9240,9 @@ 'margin' => true, 'padding' => true ), + 'dimensions' => array( + 'minWidth' => true + ), 'shadow' => true, 'typography' => array( 'fontSize' => true, @@ -9010,7 +9399,8 @@ 'textdomain' => 'default', 'usesContext' => array( 'termId', - 'taxonomy' + 'taxonomy', + 'termData' ), 'attributes' => array( 'bracketType' => array( @@ -9042,8 +9432,13 @@ ), 'shadow' => true, 'spacing' => array( + 'margin' => true, 'padding' => true ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'typography' => array( 'fontSize' => true, 'lineHeight' => true, @@ -9109,6 +9504,10 @@ 'text' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'padding' => true, 'margin' => true @@ -9198,9 +9597,14 @@ 'link' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'shadow' => true, 'spacing' => array( - 'padding' => true + 'padding' => true, + 'margin' => true ), 'typography' => array( 'fontSize' => true, @@ -9267,6 +9671,8 @@ 'color' => array( 'gradients' => true, 'link' => true, + 'heading' => true, + 'button' => true, '__experimentalDefaultControls' => array( 'background' => true, 'text' => true @@ -9285,6 +9691,10 @@ 'fontSize' => true ) ), + 'dimensions' => array( + 'minHeight' => true, + 'minWidth' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -9361,6 +9771,11 @@ ), 'html' => false, 'layout' => true, + 'spacing' => array( + 'padding' => true, + 'margin' => true, + 'blockGap' => true + ), 'interactivity' => true ) ), @@ -9456,6 +9871,7 @@ ), 'dimensions' => array( 'minHeight' => true, + 'minWidth' => true, '__experimentalDefaultControls' => array( 'minHeight' => false ) @@ -9465,12 +9881,15 @@ '__experimentalFontFamily' => true, 'lineHeight' => true, 'textAlign' => true, + 'textColumns' => true, + 'textIndent' => true, '__experimentalFontStyle' => true, '__experimentalFontWeight' => true, '__experimentalLetterSpacing' => true, '__experimentalTextTransform' => true, '__experimentalTextDecoration' => true, '__experimentalWritingMode' => true, + 'fitText' => true, '__experimentalDefaultControls' => array( 'fontSize' => true ) @@ -9594,6 +10013,9 @@ 'supports' => array( 'anchor' => true, 'align' => true, + 'filter' => array( + 'duotone' => true + ), 'spacing' => array( 'margin' => true, 'padding' => true, @@ -9603,14 +10025,24 @@ ) ), 'interactivity' => array( - 'clientNavigation' => true + 'clientNavigation' => true, + 'interactive' => true ), 'shadow' => array( '__experimentalSkipSerialization' => true + ), + 'dimensions' => array( + 'minWidth' => true, + 'minHeight' => true, + '__experimentalSkipSerialization' => true ) ), 'selectors' => array( - 'shadow' => '.wp-block-video video' + 'dimensions' => '.wp-block-video video', + 'shadow' => '.wp-block-video video', + 'filter' => array( + 'duotone' => '.wp-block-video video' + ) ), 'editorStyle' => 'wp-block-video-editor', 'style' => 'wp-block-video' diff --git a/src/wp-includes/blocks/breadcrumbs.php b/src/wp-includes/blocks/breadcrumbs.php index 02c038dd057ea..b659afd8259b1 100644 --- a/src/wp-includes/blocks/breadcrumbs.php +++ b/src/wp-includes/blocks/breadcrumbs.php @@ -188,7 +188,7 @@ function render_block_core_breadcrumbs( $attributes, $content, $block ) { $wrapper_attributes = get_block_wrapper_attributes( array( 'style' => '--separator: "' . $separator . '";', - 'aria-label' => _x( 'Breadcrumbs', 'area label' ), + 'aria-label' => _x( 'Breadcrumbs', 'aria label' ), ) ); diff --git a/src/wp-includes/blocks/button.php b/src/wp-includes/blocks/button.php index 7b67ad158aed1..fd117e9436a57 100644 --- a/src/wp-includes/blocks/button.php +++ b/src/wp-includes/blocks/button.php @@ -60,6 +60,53 @@ function render_block_core_button( $attributes, $content ) { return ''; } + // Background skips serialization, so apply it to the link like color. + $background = $attributes['style']['background'] ?? null; + + if ( is_array( $background ) ) { + $background_styles = array( + 'backgroundImage' => $background['backgroundImage'] ?? null, + 'backgroundSize' => $background['backgroundSize'] ?? null, + 'backgroundPosition' => $background['backgroundPosition'] ?? null, + 'backgroundRepeat' => $background['backgroundRepeat'] ?? null, + 'backgroundAttachment' => $background['backgroundAttachment'] ?? null, + 'gradient' => $background['gradient'] ?? null, + ); + + // Mirror the background block support defaults. + if ( ! empty( $background_styles['backgroundImage'] ) ) { + $background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover'; + if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) { + $background_styles['backgroundPosition'] = '50% 50%'; + } + } + + $styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) ); + + if ( ! empty( $styles['css'] ) ) { + $processor = new WP_HTML_Tag_Processor( $content ); + while ( $processor->next_tag() ) { + if ( $tag !== $processor->get_tag() ) { + continue; + } + + $existing_style = $processor->get_attribute( 'style' ); + if ( is_string( $existing_style ) && '' !== $existing_style ) { + $separator = str_ends_with( $existing_style, ';' ) ? '' : ';'; + $processor->set_attribute( 'style', $existing_style . $separator . $styles['css'] ); + } else { + $processor->set_attribute( 'style', $styles['css'] ); + } + + if ( ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] ) ) { + $processor->add_class( 'has-background' ); + } + break; + } + $content = $processor->get_updated_html(); + } + } + $width = $attributes['style']['dimensions']['width'] ?? null; if ( is_string( $width ) && '' !== $width ) { diff --git a/src/wp-includes/blocks/button/block.json b/src/wp-includes/blocks/button/block.json index 2e23a64c8f559..29e3c1f22c50f 100644 --- a/src/wp-includes/blocks/button/block.json +++ b/src/wp-includes/blocks/button/block.json @@ -70,6 +70,12 @@ "splitting": true, "align": false, "alignWide": false, + "background": { + "backgroundImage": true, + "backgroundSize": true, + "gradient": true, + "__experimentalSkipSerialization": true + }, "color": { "__experimentalSkipSerialization": true, "gradients": true, diff --git a/src/wp-includes/blocks/buttons/block.json b/src/wp-includes/blocks/buttons/block.json index 4927a9644837b..03092ff3dc10a 100644 --- a/src/wp-includes/blocks/buttons/block.json +++ b/src/wp-includes/blocks/buttons/block.json @@ -50,6 +50,10 @@ "fontSize": true } }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "__experimentalBorder": { "color": true, "radius": true, @@ -73,7 +77,8 @@ "clientNavigation": true }, "listView": true, - "contentRole": true + "contentRole": true, + "shadow": true }, "editorStyle": "wp-block-buttons-editor", "style": "wp-block-buttons" diff --git a/src/wp-includes/blocks/code/block.json b/src/wp-includes/blocks/code/block.json index 028a64620cac3..4fad135a9706d 100644 --- a/src/wp-includes/blocks/code/block.json +++ b/src/wp-includes/blocks/code/block.json @@ -27,6 +27,10 @@ "gradient": true } }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "typography": { "fontSize": true, "lineHeight": true, diff --git a/src/wp-includes/blocks/columns/block.json b/src/wp-includes/blocks/columns/block.json index dcd1193ca093e..7f742a87931a1 100644 --- a/src/wp-includes/blocks/columns/block.json +++ b/src/wp-includes/blocks/columns/block.json @@ -43,6 +43,10 @@ "gradient": true } }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "spacing": { "blockGap": { "__experimentalDefault": "2em", diff --git a/src/wp-includes/blocks/comments-pagination-next/block.json b/src/wp-includes/blocks/comments-pagination-next/block.json index 3b3c122a9084d..7cbc1bba7cd1c 100644 --- a/src/wp-includes/blocks/comments-pagination-next/block.json +++ b/src/wp-includes/blocks/comments-pagination-next/block.json @@ -42,6 +42,19 @@ }, "interactivity": { "clientNavigation": true + }, + "spacing": { + "padding": true, + "__experimentalDefaultControls": { + "padding": false + } + }, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true } - } + }, + "style": "wp-block-comments-pagination-next" } diff --git a/src/wp-includes/blocks/comments-pagination-previous/block.json b/src/wp-includes/blocks/comments-pagination-previous/block.json index de141ba2bb553..6f857f2030f1d 100644 --- a/src/wp-includes/blocks/comments-pagination-previous/block.json +++ b/src/wp-includes/blocks/comments-pagination-previous/block.json @@ -45,6 +45,19 @@ }, "interactivity": { "clientNavigation": true + }, + "spacing": { + "padding": true, + "__experimentalDefaultControls": { + "padding": false + } + }, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true } - } + }, + "style": "wp-block-comments-pagination-previous" } diff --git a/src/wp-includes/blocks/comments-pagination/block.json b/src/wp-includes/blocks/comments-pagination/block.json index a2b7d9fe0b170..867a29736413d 100644 --- a/src/wp-includes/blocks/comments-pagination/block.json +++ b/src/wp-includes/blocks/comments-pagination/block.json @@ -50,6 +50,11 @@ "type": "flex" } }, + "spacing": { + "padding": true, + "margin": true, + "blockGap": true + }, "typography": { "fontSize": true, "lineHeight": true, @@ -63,6 +68,12 @@ "fontSize": true } }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true + }, "interactivity": { "clientNavigation": true } diff --git a/src/wp-includes/blocks/cover.php b/src/wp-includes/blocks/cover.php index 8bd1c3f5cd38f..bd7ee8b88034a 100644 --- a/src/wp-includes/blocks/cover.php +++ b/src/wp-includes/blocks/cover.php @@ -16,6 +16,15 @@ * @return string Returns the cover block markup, if useFeaturedImage is true. */ function render_block_core_cover( $attributes, $content ) { + // The view module decides whether a background video may play, so it is + // only needed for the two video background types. + if ( + isset( $attributes['backgroundType'] ) && + in_array( $attributes['backgroundType'], array( 'video', 'embed-video' ), true ) + ) { + wp_enqueue_script_module( '@wordpress/block-library/cover/view' ); + } + // Handle embed video background. if ( isset( $attributes['backgroundType'] ) && @@ -30,11 +39,13 @@ function render_block_core_cover( $attributes, $content ) { $oembed_html = wp_oembed_get( $url ); if ( $oembed_html ) { - // Extract iframe src from the oEmbed HTML. - preg_match( '/src=["\']([^"\']+)["\']/', $oembed_html, $src_matches ); - if ( ! empty( $src_matches[1] ) ) { - $iframe_src = $src_matches[1]; - + // Read the iframe src with the HTML API, which returns the attribute + // value decoded, so `&` in the query string becomes `&`. + $oembed_processor = new WP_HTML_Tag_Processor( $oembed_html ); + $iframe_src = $oembed_processor->next_tag( 'IFRAME' ) + ? $oembed_processor->get_attribute( 'src' ) + : null; + if ( is_string( $iframe_src ) && '' !== $iframe_src ) { // Detect provider from iframe src URL. $lower_src = strtolower( $iframe_src ); $provider = null; @@ -50,7 +61,8 @@ function render_block_core_cover( $attributes, $content ) { } // Modify iframe src to add background video parameters based on provider. - $parsed_url = wp_parse_url( $iframe_src ); + $parsed_url = wp_parse_url( $iframe_src ); + $reduced_motion_src = null; if ( $parsed_url && isset( $parsed_url['host'] ) ) { // Parse existing query parameters. $query_params = array(); @@ -58,9 +70,12 @@ function render_block_core_cover( $attributes, $content ) { parse_str( $parsed_url['query'], $query_params ); } - // Add background video parameters based on provider. + // Add background video parameters based on provider. The + // autoplay parameters are kept separate so a source without + // them can also be built, which the view script swaps in for + // a visitor who has asked for reduced motion. + $autoplay_params = array(); if ( 'youtube' === $provider ) { - $query_params['autoplay'] = '1'; $query_params['mute'] = '1'; $query_params['loop'] = '1'; $query_params['controls'] = '0'; @@ -74,32 +89,53 @@ function render_block_core_cover( $attributes, $content ) { if ( $video_id ) { $query_params['playlist'] = $video_id; } + + $autoplay_params['autoplay'] = '1'; } elseif ( 'vimeo' === $provider ) { - $query_params['autoplay'] = '1'; $query_params['muted'] = '1'; $query_params['loop'] = '1'; - $query_params['background'] = '1'; $query_params['controls'] = '0'; $query_params['transparent'] = '0'; + + // Vimeo's background mode always autoplays, so it can only + // be used when autoplay is allowed. + $autoplay_params['autoplay'] = '1'; + $autoplay_params['background'] = '1'; } elseif ( 'videopress' === $provider || 'wordpress-tv' === $provider ) { - $query_params['autoplay'] = '1'; - $query_params['loop'] = '1'; - $query_params['muted'] = '1'; + $query_params['loop'] = '1'; + $query_params['muted'] = '1'; + + $autoplay_params['autoplay'] = '1'; } // Rebuild the URL with new parameters. - $iframe_src = $parsed_url['scheme'] . '://' . $parsed_url['host']; + $iframe_base = $parsed_url['scheme'] . '://' . $parsed_url['host']; if ( isset( $parsed_url['path'] ) ) { - $iframe_src .= $parsed_url['path']; - } - if ( ! empty( $query_params ) ) { - $iframe_src .= '?' . http_build_query( $query_params ); + $iframe_base .= $parsed_url['path']; } + + $reduced_motion_src = empty( $query_params ) + ? $iframe_base + : $iframe_base . '?' . http_build_query( $query_params ); + + $iframe_src = empty( $autoplay_params ) + ? $reduced_motion_src + : $iframe_base . '?' . http_build_query( array_merge( $query_params, $autoplay_params ) ); } - // Build the iframe HTML that will replace the figure. + // Build the iframe HTML that will replace the figure. The server + // renders the autoplaying source, and the view script swaps in the + // one without autoplay for a visitor who prefers reduced motion. $iframe_html = sprintf( - '
', + '
', + esc_attr( + wp_json_encode( + array( + 'src' => $iframe_src, + 'reducedMotionSrc' => $reduced_motion_src, + ) + ) + ), esc_url( $iframe_src ) ); @@ -130,6 +166,31 @@ function render_block_core_cover( $attributes, $content ) { return $content; } + // Handle uploaded video background. The video is saved with `autoplay`, so + // the view module stops it for a visitor who prefers reduced motion. The + // directives are added here rather than in the saved markup so existing + // content does not need a deprecation. + if ( + isset( $attributes['backgroundType'] ) && + 'video' === $attributes['backgroundType'] + ) { + $processor = new WP_HTML_Tag_Processor( $content ); + + if ( $processor->next_tag( + array( + 'tag_name' => 'VIDEO', + 'class_name' => 'wp-block-cover__video-background', + ) + ) ) { + $processor->set_attribute( 'data-wp-interactive', 'core/cover' ); + $processor->set_attribute( 'data-wp-init', 'callbacks.stopBackgroundVideo' ); + + $content = $processor->get_updated_html(); + } + + return $content; + } + if ( 'image' !== $attributes['backgroundType'] || false === $attributes['useFeaturedImage'] ) { return $content; } diff --git a/src/wp-includes/blocks/cover/block.json b/src/wp-includes/blocks/cover/block.json index 1d95f80402e61..5419ecfcd6eb0 100644 --- a/src/wp-includes/blocks/cover/block.json +++ b/src/wp-includes/blocks/cover/block.json @@ -129,12 +129,15 @@ "color": { "heading": true, "text": true, + "link": true, + "button": true, "background": false, "__experimentalSkipSerialization": [ "gradients" ], "enableContrastChecker": false }, "dimensions": { - "aspectRatio": true + "aspectRatio": true, + "minWidth": true }, "typography": { "fontSize": true, @@ -153,7 +156,8 @@ "allowJustification": false }, "interactivity": { - "clientNavigation": true + "clientNavigation": true, + "interactive": true }, "filter": { "duotone": true diff --git a/src/wp-includes/blocks/details/block.json b/src/wp-includes/blocks/details/block.json index f5a2a1a6c3edc..ef2ad1428c39c 100644 --- a/src/wp-includes/blocks/details/block.json +++ b/src/wp-includes/blocks/details/block.json @@ -67,6 +67,9 @@ "padding": false } }, + "dimensions": { + "minWidth": true + }, "typography": { "fontSize": true, "lineHeight": true, diff --git a/src/wp-includes/blocks/embed/block.json b/src/wp-includes/blocks/embed/block.json index d6f7c501d6507..41db597f29949 100644 --- a/src/wp-includes/blocks/embed/block.json +++ b/src/wp-includes/blocks/embed/block.json @@ -43,8 +43,13 @@ "supports": { "anchor": true, "align": true, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "spacing": { - "margin": true + "margin": true, + "padding": true }, "interactivity": { "clientNavigation": true diff --git a/src/wp-includes/blocks/file/block.json b/src/wp-includes/blocks/file/block.json index 24eeba2b2c386..68e389a4d28fb 100644 --- a/src/wp-includes/blocks/file/block.json +++ b/src/wp-includes/blocks/file/block.json @@ -78,6 +78,10 @@ "margin": true, "padding": true }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "color": { "gradients": true, "link": true, @@ -99,6 +103,20 @@ "style": true } }, + "shadow": true, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, "interactivity": true }, "editorStyle": "wp-block-file-editor", diff --git a/src/wp-includes/blocks/footnotes/block.json b/src/wp-includes/blocks/footnotes/block.json index 393f7f702ab99..07fb9171aa243 100644 --- a/src/wp-includes/blocks/footnotes/block.json +++ b/src/wp-includes/blocks/footnotes/block.json @@ -53,6 +53,10 @@ "padding": false } }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "typography": { "fontSize": true, "lineHeight": true, diff --git a/src/wp-includes/blocks/gallery.php b/src/wp-includes/blocks/gallery.php index 5474503702a65..587a6e349f93a 100644 --- a/src/wp-includes/blocks/gallery.php +++ b/src/wp-includes/blocks/gallery.php @@ -469,6 +469,11 @@ function block_core_gallery_render_dynamic_image( $attachment_id, $attributes, $ * @return string The content of the block being rendered. */ function block_core_gallery_render( $attributes, $content, $block ) { + /* + * Reads global styles once per request. wp_get_global_styles() has no cache + * of its own, so without this static each gallery would merge the theme.json + * data again wherever the resolver does not cache the merged result. + */ static $global_styles = null; // Gallery blocks created before layout variations existed do not have an @@ -554,13 +559,6 @@ function block_core_gallery_render( $attributes, $content, $block ) { $style_attr = is_array( $attributes['style'] ?? null ) ? $attributes['style'] : array(); - if ( - defined( 'IS_GUTENBERG_PLUGIN' ) && - IS_GUTENBERG_PLUGIN && - function_exists( 'gutenberg_resolve_style_state_aliases' ) - ) { - $style_attr = gutenberg_resolve_style_state_aliases( $style_attr, 'core/gallery' ); - } $global_settings = wp_get_global_settings(); $viewport_settings = $global_settings['viewport'] ?? null; diff --git a/src/wp-includes/blocks/gallery/block.json b/src/wp-includes/blocks/gallery/block.json index 44158bb0fd0ad..a18adb2573b8c 100644 --- a/src/wp-includes/blocks/gallery/block.json +++ b/src/wp-includes/blocks/gallery/block.json @@ -166,6 +166,11 @@ "background": true, "gradients": true }, + "shadow": true, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "layout": { "allowSwitching": false, "allowInheriting": false, @@ -179,11 +184,19 @@ "type": "flex" } }, + "filter": { + "duotone": true + }, "interactivity": { "clientNavigation": true }, "listView": true }, + "selectors": { + "filter": { + "duotone": ".wp-block-gallery :where(img)" + } + }, "editorStyle": "wp-block-gallery-editor", "style": "wp-block-gallery" } diff --git a/src/wp-includes/blocks/heading/block.json b/src/wp-includes/blocks/heading/block.json index 4498f79781735..a19a6ecdd33a7 100644 --- a/src/wp-includes/blocks/heading/block.json +++ b/src/wp-includes/blocks/heading/block.json @@ -31,6 +31,7 @@ "className": true, "splitting": true, "background": { + "backgroundClip": true, "backgroundImage": true, "backgroundSize": true, "gradient": true, @@ -53,6 +54,10 @@ "text": true } }, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "shadow": true, "spacing": { "margin": true, @@ -66,6 +71,7 @@ "fontSize": true, "lineHeight": true, "textAlign": true, + "textIndent": true, "__experimentalFontFamily": true, "__experimentalFontStyle": true, "__experimentalFontWeight": true, diff --git a/src/wp-includes/blocks/home-link/block.json b/src/wp-includes/blocks/home-link/block.json index 036e967883164..733a3afdd8e0d 100644 --- a/src/wp-includes/blocks/home-link/block.json +++ b/src/wp-includes/blocks/home-link/block.json @@ -52,6 +52,10 @@ } }, "shadow": true, + "dimensions": { + "minHeight": true, + "minWidth": true + }, "__experimentalBorder": { "color": true, "radius": true, @@ -64,6 +68,14 @@ "width": false } }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, "interactivity": { "clientNavigation": true } diff --git a/src/wp-includes/blocks/icon.php b/src/wp-includes/blocks/icon.php index 1b615c8a6dc96..fe5e9dcb41fe4 100644 --- a/src/wp-includes/blocks/icon.php +++ b/src/wp-includes/blocks/icon.php @@ -15,7 +15,7 @@ * @return string Returns the Icon. */ function render_block_core_icon( $attributes ) { - if ( empty( $attributes['icon'] ) ) { + if ( empty( $attributes['icon'] ) || ! is_string( $attributes['icon'] ) ) { return; } @@ -39,6 +39,23 @@ function render_block_core_icon( $attributes ) { $custom_background_color = $attributes['style']['color']['background'] ?? null; $color_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color; + // Background image, size and gradient. + $background_styles = array(); + if ( isset( $attributes['style']['background'] ) && is_array( $attributes['style']['background'] ) ) { + $background_styles = array_intersect_key( + $attributes['style']['background'], + array_flip( array( 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat', 'backgroundAttachment', 'gradient' ) ) + ); + + // Mirror of the defaults in the background block support. + if ( ! empty( $background_styles['backgroundImage'] ) ) { + $background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover'; + if ( 'contain' === $background_styles['backgroundSize'] && empty( $background_styles['backgroundPosition'] ) ) { + $background_styles['backgroundPosition'] = '50% 50%'; + } + } + } + // Border. $border_styles = array(); $sides = array( 'top', 'right', 'bottom', 'left' ); @@ -66,23 +83,30 @@ function render_block_core_icon( $attributes ) { ); } + // Shadow. + $shadow_styles = $attributes['style']['shadow'] ?? null; + // Spacing (Padding). $spacing_styles = array(); if ( isset( $attributes['style']['spacing']['padding'] ) ) { $spacing_styles['padding'] = $attributes['style']['spacing']['padding']; } - // Dimensions (Width). + // Dimensions (Width, Min width, Min height). $dimensions_styles = array(); - if ( isset( $attributes['style']['dimensions']['width'] ) ) { - $dimensions_styles['width'] = $attributes['style']['dimensions']['width']; + foreach ( array( 'width', 'minWidth', 'minHeight' ) as $dimension ) { + if ( isset( $attributes['style']['dimensions'][ $dimension ] ) ) { + $dimensions_styles[ $dimension ] = $attributes['style']['dimensions'][ $dimension ]; + } } // Generate styles and classes. $styles = wp_style_engine_get_styles( array( 'color' => $color_styles, + 'background' => $background_styles, 'border' => $border_styles, + 'shadow' => $shadow_styles, 'spacing' => $spacing_styles, 'dimensions' => $dimensions_styles, ), @@ -118,6 +142,11 @@ function render_block_core_icon( $attributes ) { $processor->set_attribute( 'style', $merged_style ); } + // The style engine only adds `has-background` for a gradient. + if ( ! empty( $background_styles['backgroundImage'] ) ) { + $processor->add_class( 'has-background' ); + } + // Apply flip classes to the SVG. $flip_horizontal = $attributes['flipHorizontal'] ?? false; $flip_vertical = $attributes['flipVertical'] ?? false; diff --git a/src/wp-includes/blocks/icon/block.json b/src/wp-includes/blocks/icon/block.json index de9f71af79e4b..ddc62d6216c12 100644 --- a/src/wp-includes/blocks/icon/block.json +++ b/src/wp-includes/blocks/icon/block.json @@ -31,6 +31,12 @@ "__experimentalSkipSerialization": true }, "align": [ "left", "center", "right" ], + "background": { + "backgroundImage": true, + "backgroundSize": true, + "gradient": true, + "__experimentalSkipSerialization": true + }, "html": false, "color": { "__experimentalSkipSerialization": true, @@ -42,6 +48,9 @@ "interactivity": { "clientNavigation": true }, + "shadow": { + "__experimentalSkipSerialization": true + }, "__experimentalBorder": { "color": true, "radius": true, @@ -66,7 +75,13 @@ }, "dimensions": { "width": true, - "__experimentalSkipSerialization": [ "width" ], + "minWidth": true, + "minHeight": true, + "__experimentalSkipSerialization": [ + "width", + "minWidth", + "minHeight" + ], "__experimentalDefaultControls": { "width": true } @@ -75,6 +90,7 @@ "selectors": { "root": ".wp-block-icon svg", "css": ".wp-block-icon", + "shadow": ".wp-block-icon svg", "spacing": { "margin": ".wp-block-icon" } diff --git a/src/wp-includes/blocks/image.php b/src/wp-includes/blocks/image.php index aef905ef59a64..8035a99782c7f 100644 --- a/src/wp-includes/blocks/image.php +++ b/src/wp-includes/blocks/image.php @@ -369,13 +369,13 @@ class="wp-lightbox-overlay zoom" data-wp-on--touchstart="actions.handleTouchStart" data-wp-on--touchmove="actions.handleTouchMove" data-wp-on--touchend="actions.handleTouchEnd" - data-wp-on--click="actions.hideLightbox" + data-wp-on--touchcancel="actions.handleTouchCancel" data-wp-on-window--resize="callbacks.setOverlayStyles" data-wp-on-window--scroll="actions.handleScroll" data-wp-bind--style="state.overlayStyles" tabindex="-1" > - @@ -383,7 +383,7 @@ class="wp-lightbox-overlay zoom" {$prev_button_icon} {$prev_button_text} -