Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Hooks: Add field to block registration, REST API #5203

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions src/wp-admin/includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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',
Expand Down
34 changes: 34 additions & 0 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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' ),
gziolo marked this conversation as resolved.
Show resolved Hide resolved
'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(
Expand Down
14 changes: 14 additions & 0 deletions src/wp-includes/class-wp-block-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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()
*
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public function prepare_item_for_response( $item, $request ) {
'editor_style_handles',
'style_handles',
'variations',
'block_hooks',
),
$deprecated_fields
);
Expand Down Expand Up @@ -705,6 +706,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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ockham How something like this.

$properties = array();
$registry   = WP_Block_Type_Registry::get_instance();
foreach( $registry->get_all_registered() as $block ){
   $properties[ $block ] = array(
       'type' => 'string',
       'enum' => array( 'before', 'after', 'first_child', 'last_child' ),
   );
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that there might be hundreds of registered blocks, but the block author might still list the block type name that isn't present on the site and we would consider it valid. What's the reasoning behind listing all available blocks?

'type' => 'string',
'enum' => array( 'before', 'after', 'first_child', 'last_child' ),
),
),
'default' => array(),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
Comment on lines +709 to +721
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that is correct and should changed in gutenberg. This pattern is not needed and make it extremely hard to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment 😄

),
);

Expand Down
3 changes: 3 additions & 0 deletions tests/phpunit/data/blocks/notice/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"selectors": {
"root": ".wp-block-notice"
},
"blockHooks": {
"core/post-content": "before"
},
"supports": {
"align": true,
"lightBlockWrapper": true
Expand Down
2 changes: 2 additions & 0 deletions tests/phpunit/tests/admin/includesPost.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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(),
Expand Down
6 changes: 6 additions & 0 deletions tests/phpunit/tests/blocks/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 11 additions & 1 deletion tests/phpunit/tests/rest-api/rest-block-type-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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'] );
Expand All @@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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'] );
Expand Down Expand Up @@ -538,14 +544,15 @@ public function test_get_variation() {
/**
* @ticket 47620
* @ticket 57585
* @ticket 59346
*/
public function test_get_item_schema() {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/block-types' );
$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 );
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -690,6 +699,7 @@ protected function check_block_type_object( $block_type, $data, $links ) {
'parent',
'provides_context',
'uses_context',
'block_hooks',
'supports',
'styles',
'textdomain',
gziolo marked this conversation as resolved.
Show resolved Hide resolved
Expand Down