[wp-trac] [WordPress Trac] #59313: Implement Block Hooks
WordPress Trac
noreply at wordpress.org
Tue Sep 19 18:47:31 UTC 2023
#59313: Implement Block Hooks
-------------------------------------------------+-------------------------
Reporter: Bernhard Reiter | Owner: Bernhard
| Reiter
Type: enhancement | Status: assigned
Priority: high | Milestone: 6.4
Component: Editor | Version:
Severity: normal | Resolution:
Keywords: has-patch gutenberg-merge has-unit- | Focuses:
tests |
-------------------------------------------------+-------------------------
Description changed by Bernhard Reiter:
Old description:
> This ticket is about porting Gutenberg PHP code that implemented Block
> Hooks to Core. Skip to the bottom of this ticket for a TODO list.
>
> == Synopsis
>
> First implemented in Gutenberg under the name
> [https://make.wordpress.org/core/2023/08/10/whats-new-in-
> gutenberg-16-4-9-august/#auto-inserting-blocks "Auto-inserting Blocks"]
> and recently [https://github.com/WordPress/gutenberg/pull/54147 renamed]
> to "Block Hooks", this feature is meant to provide an extensibility
> mechanism for Block Themes, in analogy to WordPress'
> [https://developer.wordpress.org/plugins/hooks/ Hooks] concept that has
> allowed extending Classic Themes through filters and actions.
>
> Specifically, Block Hooks allow a third-party block to specify a position
> relative to a given block into which it will then be automatically
> inserted (e.g. a "Like" button block can ask to be inserted after the
> Post Content block, or an eCommerce shopping cart block can ask to be
> inserted after the Navigation block).
>
> The two core tenets for block hooks are:
>
> 1. Insertion into the frontend should happen right after a plugin
> containing a hooked block is activated (i.e. the user isn't required to
> insert the block manually in the editor first); similarly, disabling the
> plugin should remove the hooked block from the frontend.
> 2. The user has the ultimate power to customize that automatic insertion:
> The hooked block is also visible in the editor, and the user's decision
> to persist, dismiss, customize, or move it will be respected (and
> reflected on the frontend).
>
> To account for both tenets, we've made the **tradeoff** that automatic
> block insertion only works for unmodified templates (and template parts,
> respectively). The reason for this is that the simplest way of storing
> the information whether a block has been persisted to (or dismissed from)
> a given template (or part) is right in the template markup. This was
> first suggested
> [https://github.com/WordPress/gutenberg/issues/39439#issuecomment-1150278043
> here].
>
> To accommodate for that tradeoff, we've
> [https://github.com/WordPress/gutenberg/pull/52969 added UI controls
> (toggles)] to increase visibility of hooked blocks, and to allow their
> later insertion into templates (or parts) that already have been modified
> by the user.
>
> == Implementation
>
> Since we wanted hooked blocks to appear both in the frontend and in the
> editor (see tenet number 2), we had to make sure they were inserted into
> both the frontend markup and the REST API (templates and patterns
> endpoints) equally. As a consequence, this means that automatic insertion
> couldn't only be implemented at block ''render'' stage, as for the
> editor, we needed to modify the ''serialized'' (but ''unrendered'')
> markup.
>
> However, thanks to the tradeoff we made (see above), we could at least
> limit ourselves to only inserting hooked blocks into unmodified templates
> (and parts), i.e. those that were coming directly from a Block Theme's
> template (or parts) file, rather than the database.
>
> It turns out that there's a rather natural stage for automatic insertion
> of hooked blocks to happen, which is during template file retrieval.
>
> While we had to leverage the `get_block_templates` and
> `get_block_file_template` hooks in Gutenberg to insert those blocks,
> we'll be able to directly modify the
> `_build_block_template_result_from_file` function in Core.
>
> Furthermore, hooked blocks also have to be inserted into block patterns.
> Since almost no filters exist for the patterns registry, this was done in
> the patterns REST API controller in the Gutenberg codebase; for Core,
> we'll likely want to this at a lower level.
>
> == Core merge
>
> Based on an early exploration in this [https://github.com/WordPress
> /wordpress-develop/pull/5158 experimental PR], I believe that we can
> break down the implementation of Block Hooks in Core into the following
> steps. Note that due to the nature of the existing code, the first steps
> might appear somewhat surprising and unrelated; however, they are very
> much relevant and will allow us to extend existing functionality through
> a series of self-contained steps.
>
> - #59325: Add proper unit test coverage to verify that
> `_build_block_template_result_from_file` injects the `theme` attribute.
> - While we have coverage for
> `_inject_theme_attribute_in_block_template_content`, those tests only
> verify that ''that'' function does what is supposed to do; there's
> however no guarantee that `_build_block_template_result_from_file` uses
> that function (or whatever other technique) to actually inject the theme
> attribute ([https://github.com/WordPress/wordpress-develop/pull/5155
> see]).
> - #59327: Add a `$callback` argument to `serialize_block()`
> ([https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/b926b86f1585bebcc71efae4819cb8b4593e8714 see])
> and `serialize_blocks()` ([https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/7fda6f56ef2c49ccdc687e15c3c5bb914290d94c see]).
> - In order to insert our hooked blocks, we need to traverse the parsed
> block tree (before it is eventually serialized). In order to minimize the
> number of tree traversals (which is a potentially costly operation), it
> seems that adding a callback function argument to `serialize_block()` --
> which inevitable has to traverse the entire tree anyway -- is a natural
> fit.
> - #59338: Use the latter to replace the call to
> `_inject_theme_attribute_in_block_template_content` in
> `_build_block_template_result_from_file` with a newly written
> `_inject_theme_attribute_in_template_part_block`
> ([https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/f7465b97001313ec525215b1e768f9142f252d56 see]).
> - Note that `_inject_theme_attribute_in_block_template_content` is
> still used in another spot: The Template Part block’s render.php (i.e. in
> GB). We might also want to replace that with
> `_inject_theme_attribute_in_template_part_block` so that we can deprecate
> `_inject_theme_attribute_in_block_template_content`.
> - #59346: Add `block_hooks` field to `WP_Block_Type`, `block.json`
> loading, REST API endpoint, etc (see [https://github.com/WordPress
> /wordpress-
> develop/pull/5158/commits/3ad1fd7caac337e10d385fee2619b53ceb4f7c0a here]
> and [https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/37496a0e982fe48b86cdbecbb4c06a3eff5e3ec4
> here]).
> - #59383: Implement `get_hooked_blocks()` to receive a list of blocks
> hooked into a given "anchor" block ([https://github.com/WordPress
> /wordpress-
> develop/pull/5158/commits/cdcfbcc1117566df0b608f1875d50480b92111ad see]).
> - We probably want to [https://github.com/WordPress/wordpress-
> develop/pull/5158/files#r1326013492 cache] this.
> - #59385: Implement block insertion functions.
> - Implement `insert_hooked_blocks` (to insert all hooked blocks for a
> given anchor block).
> - Call the latter alongside
> `_inject_theme_attribute_in_template_part_block` in a newly written
> visitor function that's passed as `$callback` to `serialize_blocks`
> ([https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/c897d86393c2b434421894a776c6e2b75209a186 see]).
> - Apply automatic insertion of hooked blocks to block patterns (see
> [https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/600e55de0513c27e1625f87f3a23435f8d8e0a03 here]
> and [https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/b1a82fe1177a4a8bf253db2aa42250b5f7025536
> here]).
> - Add a filter to allow people to e.g. limit automatic insertion of
> hooked blocks to a certain template or template part type only
> ([https://github.com/WordPress/wordpress-
> develop/pull/5158/commits/c897d86393c2b434421894a776c6e2b75209a186 also
> see]).
> - This somewhat informs the architecture of the code (e.g. function
> signatures, passing of `$block_template` context) and goes beyond what is
> currently in Gutenberg.
> - Note that this hasn't been implemented in the code sync PR yet and is
> thus still a bit more tentative.
>
> ''The TODO list is based on my notes from a call with @gziolo earlier
> today.''
New description:
This ticket is about porting Gutenberg PHP code that implemented Block
Hooks to Core. Skip to the bottom of this ticket for a TODO list.
== Synopsis
First implemented in Gutenberg under the name
[https://make.wordpress.org/core/2023/08/10/whats-new-in-
gutenberg-16-4-9-august/#auto-inserting-blocks "Auto-inserting Blocks"]
and recently [https://github.com/WordPress/gutenberg/pull/54147 renamed]
to "Block Hooks", this feature is meant to provide an extensibility
mechanism for Block Themes, in analogy to WordPress'
[https://developer.wordpress.org/plugins/hooks/ Hooks] concept that has
allowed extending Classic Themes through filters and actions.
Specifically, Block Hooks allow a third-party block to specify a position
relative to a given block into which it will then be automatically
inserted (e.g. a "Like" button block can ask to be inserted after the Post
Content block, or an eCommerce shopping cart block can ask to be inserted
after the Navigation block).
The two core tenets for block hooks are:
1. Insertion into the frontend should happen right after a plugin
containing a hooked block is activated (i.e. the user isn't required to
insert the block manually in the editor first); similarly, disabling the
plugin should remove the hooked block from the frontend.
2. The user has the ultimate power to customize that automatic insertion:
The hooked block is also visible in the editor, and the user's decision to
persist, dismiss, customize, or move it will be respected (and reflected
on the frontend).
To account for both tenets, we've made the **tradeoff** that automatic
block insertion only works for unmodified templates (and template parts,
respectively). The reason for this is that the simplest way of storing the
information whether a block has been persisted to (or dismissed from) a
given template (or part) is right in the template markup. This was first
suggested
[https://github.com/WordPress/gutenberg/issues/39439#issuecomment-1150278043
here].
To accommodate for that tradeoff, we've
[https://github.com/WordPress/gutenberg/pull/52969 added UI controls
(toggles)] to increase visibility of hooked blocks, and to allow their
later insertion into templates (or parts) that already have been modified
by the user.
== Implementation
Since we wanted hooked blocks to appear both in the frontend and in the
editor (see tenet number 2), we had to make sure they were inserted into
both the frontend markup and the REST API (templates and patterns
endpoints) equally. As a consequence, this means that automatic insertion
couldn't only be implemented at block ''render'' stage, as for the editor,
we needed to modify the ''serialized'' (but ''unrendered'') markup.
However, thanks to the tradeoff we made (see above), we could at least
limit ourselves to only inserting hooked blocks into unmodified templates
(and parts), i.e. those that were coming directly from a Block Theme's
template (or parts) file, rather than the database.
It turns out that there's a rather natural stage for automatic insertion
of hooked blocks to happen, which is during template file retrieval.
While we had to leverage the `get_block_templates` and
`get_block_file_template` hooks in Gutenberg to insert those blocks, we'll
be able to directly modify the `_build_block_template_result_from_file`
function in Core.
Furthermore, hooked blocks also have to be inserted into block patterns.
Since almost no filters exist for the patterns registry, this was done in
the patterns REST API controller in the Gutenberg codebase; for Core,
we'll likely want to this at a lower level.
== Core merge
Based on an early exploration in this [https://github.com/WordPress
/wordpress-develop/pull/5158 experimental PR], I believe that we can break
down the implementation of Block Hooks in Core into the following steps.
Note that due to the nature of the existing code, the first steps might
appear somewhat surprising and unrelated; however, they are very much
relevant and will allow us to extend existing functionality through a
series of self-contained steps.
- #59325: Add proper unit test coverage to verify that
`_build_block_template_result_from_file` injects the `theme` attribute.
- While we have coverage for
`_inject_theme_attribute_in_block_template_content`, those tests only
verify that ''that'' function does what is supposed to do; there's however
no guarantee that `_build_block_template_result_from_file` uses that
function (or whatever other technique) to actually inject the theme
attribute ([https://github.com/WordPress/wordpress-develop/pull/5155
see]).
- #59327: Add a `$callback` argument to `serialize_block()`
([https://github.com/WordPress/wordpress-
develop/pull/5158/commits/b926b86f1585bebcc71efae4819cb8b4593e8714 see])
and `serialize_blocks()` ([https://github.com/WordPress/wordpress-
develop/pull/5158/commits/7fda6f56ef2c49ccdc687e15c3c5bb914290d94c see]).
- In order to insert our hooked blocks, we need to traverse the parsed
block tree (before it is eventually serialized). In order to minimize the
number of tree traversals (which is a potentially costly operation), it
seems that adding a callback function argument to `serialize_block()` --
which inevitable has to traverse the entire tree anyway -- is a natural
fit.
- #59338: Use the latter to replace the call to
`_inject_theme_attribute_in_block_template_content` in
`_build_block_template_result_from_file` with a newly written
`_inject_theme_attribute_in_template_part_block`
([https://github.com/WordPress/wordpress-
develop/pull/5158/commits/f7465b97001313ec525215b1e768f9142f252d56 see]).
- Note that `_inject_theme_attribute_in_block_template_content` is still
used in another spot: The Template Part block’s render.php (i.e. in GB).
We might also want to replace that with
`_inject_theme_attribute_in_template_part_block` so that we can deprecate
`_inject_theme_attribute_in_block_template_content`.
- #59346: Add `block_hooks` field to `WP_Block_Type`, `block.json`
loading, REST API endpoint, etc (see [https://github.com/WordPress
/wordpress-
develop/pull/5158/commits/3ad1fd7caac337e10d385fee2619b53ceb4f7c0a here]
and [https://github.com/WordPress/wordpress-
develop/pull/5158/commits/37496a0e982fe48b86cdbecbb4c06a3eff5e3ec4 here]).
- #59383: Implement `get_hooked_blocks()` to receive a list of blocks
hooked into a given "anchor" block ([https://github.com/WordPress
/wordpress-
develop/pull/5158/commits/cdcfbcc1117566df0b608f1875d50480b92111ad see]).
- We probably want to [https://github.com/WordPress/wordpress-
develop/pull/5158/files#r1326013492 cache] this.
- #59385: Implement block insertion functions.
- #59399: Implement `insert_hooked_blocks` (to insert all hooked blocks
for a given anchor block).
- Call the latter alongside
`_inject_theme_attribute_in_template_part_block` in a newly written
visitor function that's passed as `$callback` to `serialize_blocks`
([https://github.com/WordPress/wordpress-
develop/pull/5158/commits/c897d86393c2b434421894a776c6e2b75209a186 see]).
- Apply automatic insertion of hooked blocks to block patterns (see
[https://github.com/WordPress/wordpress-
develop/pull/5158/commits/600e55de0513c27e1625f87f3a23435f8d8e0a03 here]
and [https://github.com/WordPress/wordpress-
develop/pull/5158/commits/b1a82fe1177a4a8bf253db2aa42250b5f7025536 here]).
- Add a filter to allow people to e.g. limit automatic insertion of hooked
blocks to a certain template or template part type only
([https://github.com/WordPress/wordpress-
develop/pull/5158/commits/c897d86393c2b434421894a776c6e2b75209a186 also
see]).
- This somewhat informs the architecture of the code (e.g. function
signatures, passing of `$block_template` context) and goes beyond what is
currently in Gutenberg.
- Note that this hasn't been implemented in the code sync PR yet and is
thus still a bit more tentative.
''The TODO list is based on my notes from a call with @gziolo earlier
today.''
--
--
Ticket URL: <https://core.trac.wordpress.org/ticket/59313#comment:81>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list