From 7b90f22573d4a58dddf6f0d521d2079b9eeda2f3 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 14 Sep 2023 13:23:13 +0000 Subject: [PATCH] General: Add `block_hooks` field to block type registration, REST API. In order to implement Block Hooks, we need to add a new `block_hooks` field to the `WP_Block_Type` class, as well as to block registration related functions, the block types REST API controller, etc. Props gziolo. Fixes #59346. See #59313. git-svn-id: https://develop.svn.wordpress.org/trunk@56587 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/post.php | 2 ++ src/wp-includes/blocks.php | 34 +++++++++++++++++++ src/wp-includes/class-wp-block-type.php | 14 ++++++++ .../class-wp-rest-block-types-controller.php | 14 ++++++++ tests/phpunit/data/blocks/notice/block.json | 3 ++ tests/phpunit/tests/admin/includesPost.php | 2 ++ tests/phpunit/tests/blocks/register.php | 6 ++++ .../rest-api/rest-block-type-controller.php | 12 ++++++- 8 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index ed73b8ff5fd14..3709ded67fdd5 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -2207,6 +2207,7 @@ function taxonomy_meta_box_sanitize_cb_input( $taxonomy, $terms ) { * * @since 5.0.0 * @since 6.3.0 Added `selectors` field. + * @since 6.4.0 Added `block_hooks` field. * * @return array An associative array of registered block data. */ @@ -2221,6 +2222,7 @@ function get_block_editor_server_block_settings() { 'attributes' => 'attributes', 'provides_context' => 'providesContext', 'uses_context' => 'usesContext', + 'block_hooks' => 'blockHooks', 'selectors' => 'selectors', 'supports' => 'supports', 'category' => 'category', diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 7795a6969b113..bef59a21dbb8c 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -342,6 +342,7 @@ function get_block_metadata_i18n_schema() { * @since 5.9.0 Added support for `variations` and `viewScript` fields. * @since 6.1.0 Added support for `render` field. * @since 6.3.0 Added `selectors` field. + * @since 6.4.0 Added support for `blockHooks` field. * * @param string $file_or_folder Path to the JSON file with metadata definition for * the block or path to the folder where the `block.json` file is located. @@ -513,6 +514,39 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) { } } + if ( ! empty( $metadata['blockHooks'] ) ) { + /** + * Map camelCased position string (from block.json) to snake_cased block type position. + * + * @var array + */ + $position_mappings = array( + 'before' => 'before', + 'after' => 'after', + 'firstChild' => 'first_child', + 'lastChild' => 'last_child', + ); + + $settings['block_hooks'] = array(); + foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) { + // Avoid infinite recursion (hooking to itself). + if ( $metadata['name'] === $anchor_block_name ) { + _doing_it_wrong( + __METHOD__, + __( 'Cannot hook block to itself.', 'gutenberg' ), + '6.4.0' + ); + continue; + } + + if ( ! isset( $position_mappings[ $position ] ) ) { + continue; + } + + $settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ]; + } + } + if ( ! empty( $metadata['render'] ) ) { $template_path = wp_normalize_path( realpath( diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php index e28bacd3b211c..b8ffb92e559b2 100644 --- a/src/wp-includes/class-wp-block-type.php +++ b/src/wp-includes/class-wp-block-type.php @@ -173,6 +173,18 @@ class WP_Block_Type { */ public $provides_context = null; + /** + * Block hooks for this block type. + * + * A block hook is specified by a block type and a relative position. + * The hooked block will be automatically inserted in the given position + * next to the "anchor" block whenever the latter is encountered. + * + * @since 6.4.0 + * @var array[] + */ + public $block_hooks = array(); + /** * Block type editor only script handles. * @@ -254,6 +266,7 @@ class WP_Block_Type { * `editor_style_handles`, and `style_handles` properties. * Deprecated the `editor_script`, `script`, `view_script`, `editor_style`, and `style` properties. * @since 6.3.0 Added the `selectors` property. + * @since 6.4.0 Added the `block_hooks` property. * * @see register_block_type() * @@ -284,6 +297,7 @@ class WP_Block_Type { * @type array|null $attributes Block type attributes property schemas. * @type string[] $uses_context Context values inherited by blocks of this type. * @type string[]|null $provides_context Context provided by blocks of this type. + * @type array[] $block_hooks Block hooks. * @type string[] $editor_script_handles Block type editor only script handles. * @type string[] $script_handles Block type front end and editor script handles. * @type string[] $view_script_handles Block type front end only script handles. diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php index aaecc0dcac258..cd174e4aafecf 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php @@ -291,6 +291,7 @@ public function prepare_item_for_response( $item, $request ) { 'editor_style_handles', 'style_handles', 'variations', + 'block_hooks', ), $deprecated_fields ); @@ -706,6 +707,19 @@ public function get_item_schema() { ), 'keywords' => $keywords_definition, 'example' => $example_definition, + 'block_hooks' => array( + 'description' => __( 'This block is automatically inserted near any occurence of the block types used as keys of this map, into a relative position given by the corresponding value.' ), + 'type' => 'object', + 'patternProperties' => array( + '^[a-zA-Z0-9-]+/[a-zA-Z0-9-]+$' => array( + 'type' => 'string', + 'enum' => array( 'before', 'after', 'first_child', 'last_child' ), + ), + ), + 'default' => array(), + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), ), ); diff --git a/tests/phpunit/data/blocks/notice/block.json b/tests/phpunit/data/blocks/notice/block.json index c3a3b4dde9513..b6aa36e52e86c 100644 --- a/tests/phpunit/data/blocks/notice/block.json +++ b/tests/phpunit/data/blocks/notice/block.json @@ -30,6 +30,9 @@ "selectors": { "root": ".wp-block-notice" }, + "blockHooks": { + "core/post-content": "before" + }, "supports": { "align": true, "lightBlockWrapper": true diff --git a/tests/phpunit/tests/admin/includesPost.php b/tests/phpunit/tests/admin/includesPost.php index 102449cc79899..f4eaa0f5137eb 100644 --- a/tests/phpunit/tests/admin/includesPost.php +++ b/tests/phpunit/tests/admin/includesPost.php @@ -918,6 +918,7 @@ public function test_get_block_editor_server_block_settings() { 'render_callback' => 'foo', 'ancestor' => array( 'core/test-ancestor' ), 'selectors' => array( 'root' => '.wp-block-test' ), + 'block_hooks' => array( 'core/post-content' => 'before' ), ); register_block_type( $name, $settings ); @@ -937,6 +938,7 @@ public function test_get_block_editor_server_block_settings() { 'lock' => array( 'type' => 'object' ), ), 'usesContext' => array(), + 'blockHooks' => array( 'core/post-content' => 'before' ), 'selectors' => array( 'root' => '.wp-block-test' ), 'category' => 'common', 'styles' => array(), diff --git a/tests/phpunit/tests/blocks/register.php b/tests/phpunit/tests/blocks/register.php index eb61f3c307bea..55faf297a5b84 100644 --- a/tests/phpunit/tests/blocks/register.php +++ b/tests/phpunit/tests/blocks/register.php @@ -644,6 +644,12 @@ public function test_block_registers_with_metadata_fixture() { $result->selectors, 'Block type should contain selectors from metadata.' ); + // @ticket 59346 + $this->assertSame( + array( 'core/post-content' => 'before' ), + $result->block_hooks, + 'Block type should contain block hooks from metadata.' + ); $this->assertSame( array( 'align' => true, diff --git a/tests/phpunit/tests/rest-api/rest-block-type-controller.php b/tests/phpunit/tests/rest-api/rest-block-type-controller.php index af6fa73a3ad64..51e702730f905 100644 --- a/tests/phpunit/tests/rest-api/rest-block-type-controller.php +++ b/tests/phpunit/tests/rest-api/rest-block-type-controller.php @@ -195,6 +195,7 @@ public function test_get_block_invalid_name() { /** * @ticket 47620 * @ticket 57585 + * @ticket 59346 */ public function test_get_item_invalid() { $block_type = 'fake/invalid'; @@ -205,6 +206,7 @@ public function test_get_item_invalid() { 'attributes' => 'invalid_attributes', 'provides_context' => 'invalid_provides_context', 'uses_context' => 'invalid_uses_context', + 'block_hooks' => 'invalid_block_hooks', 'category' => true, 'editor_script' => true, 'script' => true, @@ -244,6 +246,7 @@ public function test_get_item_invalid() { $data['attributes'] ); $this->assertSameSets( array( 'invalid_uses_context' ), $data['uses_context'] ); + $this->assertSameSets( array(), $data['block_hooks'], 'invalid block_hooks defaults to empty array' ); $this->assertSameSets( array( 'invalid_keywords' ), $data['keywords'] ); $this->assertSameSets( array( 'invalid_parent' ), $data['parent'] ); $this->assertSameSets( array( 'invalid_ancestor' ), $data['ancestor'] ); @@ -266,6 +269,7 @@ public function test_get_item_invalid() { /** * @ticket 47620 * @ticket 57585 + * @ticket 59346 */ public function test_get_item_defaults() { $block_type = 'fake/false'; @@ -276,6 +280,7 @@ public function test_get_item_defaults() { 'attributes' => false, 'provides_context' => false, 'uses_context' => false, + 'block_hooks' => false, 'category' => false, 'editor_script' => false, 'script' => false, @@ -314,6 +319,7 @@ public function test_get_item_defaults() { $data['attributes'] ); $this->assertSameSets( array(), $data['provides_context'] ); + $this->assertSameSets( array(), $data['block_hooks'], 'block_hooks defaults to empty array' ); $this->assertSameSets( array(), $data['uses_context'] ); $this->assertSameSets( array(), $data['keywords'] ); $this->assertSameSets( array(), $data['parent'] ); @@ -538,6 +544,7 @@ public function test_get_variation() { /** * @ticket 47620 * @ticket 57585 + * @ticket 59346 */ public function test_get_item_schema() { wp_set_current_user( self::$admin_id ); @@ -545,7 +552,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 29, $properties ); + $this->assertCount( 30, $properties ); $this->assertArrayHasKey( 'api_version', $properties ); $this->assertArrayHasKey( 'title', $properties ); $this->assertArrayHasKey( 'icon', $properties ); @@ -568,6 +575,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'example', $properties ); $this->assertArrayHasKey( 'uses_context', $properties ); $this->assertArrayHasKey( 'provides_context', $properties ); + $this->assertArrayHasKey( 'block_hooks', $properties ); $this->assertArrayHasKey( 'variations', $properties ); $this->assertArrayHasKey( 'ancestor', $properties ); // Deprecated properties. @@ -664,6 +672,7 @@ public function test_prepare_item_limit_fields() { * Util check block type object against. * * @since 5.5.0 + * @since 6.4.0 Added the `block_hooks` extra field. * * @param WP_Block_Type $block_type Sample block type. * @param array $data Data to compare against. @@ -690,6 +699,7 @@ protected function check_block_type_object( $block_type, $data, $links ) { 'parent', 'provides_context', 'uses_context', + 'block_hooks', 'supports', 'styles', 'textdomain',