From 5dfd1fbfd4e84a9d86c4f814a9b5b2020bf2fa99 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 25 Nov 2024 12:38:09 +0100 Subject: [PATCH 01/10] Block Hooks: Apply to Post Content (on frontend) --- lib/compat/wordpress-6.8/blocks.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 4d4bdee2bb393b..11f10660ca81c8 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -144,3 +144,12 @@ function gutenberg_stabilize_experimental_block_supports( $args ) { } add_filter( 'register_block_type_args', 'gutenberg_stabilize_experimental_block_supports', PHP_INT_MAX, 1 ); + +function gutenberg_apply_block_hooks_to_post_content( $content ) { + // The `the_content` filter does not provide the post that the content is coming from. + // However, we can infer it by calling `get_post()`, which will return the current post + // if no post ID is provided. + return apply_block_hooks_to_content( $content, get_post(), 'insert_hooked_blocks' ); +} +// We need to apply this filter before `do_blocks` (which is hooked to `the_content` at priority 9). +add_filter( 'the_content', 'gutenberg_apply_block_hooks_to_post_content', 8 ); From e0572091eac3a683436cab980f628292951c3f38 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 25 Nov 2024 13:02:55 +0100 Subject: [PATCH 02/10] Add backport Changelog --- backport-changelog/6.8/7889.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 backport-changelog/6.8/7889.md diff --git a/backport-changelog/6.8/7889.md b/backport-changelog/6.8/7889.md new file mode 100644 index 00000000000000..7b1cc28f4a969e --- /dev/null +++ b/backport-changelog/6.8/7889.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7889 + +* https://github.com/WordPress/gutenberg/pull/67272 From bc4ad0688b97b6bcd0d257d19c0193bea7fc509a Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 14:14:19 +0100 Subject: [PATCH 03/10] Add compat layer to insert into editor --- lib/compat/wordpress-6.8/blocks.php | 66 +++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 11f10660ca81c8..76f616ef7ac627 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -153,3 +153,69 @@ function gutenberg_apply_block_hooks_to_post_content( $content ) { } // We need to apply this filter before `do_blocks` (which is hooked to `the_content` at priority 9). add_filter( 'the_content', 'gutenberg_apply_block_hooks_to_post_content', 8 ); + +/** + * Hooks into the REST API response for the Posts endpoint and adds the first and last inner blocks. + * + * @since 6.6.0 + * @since 6.8.0 Support non-`wp_navigation` post types. + * + * @param WP_REST_Response $response The response object. + * @param WP_Post $post Post object. + * @return WP_REST_Response The response object. + */ +function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { + if ( empty( $response->data['content']['raw'] ) || empty( $response->data['content']['rendered'] ) ) { + return $response; + } + + $attributes = array(); + $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); + if ( ! empty( $ignored_hooked_blocks ) ) { + $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); + $attributes['metadata'] = array( + 'ignoredHookedBlocks' => $ignored_hooked_blocks, + ); + } + + if ( 'wp_navigation' === $post->post_type ) { + $wrapper_block_type = 'core/navigation'; + } else { + $wrapper_block_type = 'core/post-content'; + } + + $content = get_comment_delimited_block_content( + $wrapper_block_type, + $attributes, + $response->data['content']['raw'] + ); + + $content = apply_block_hooks_to_content( + $content, + $post, + 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' + ); + + // Remove mock block wrapper. + $content = remove_serialized_parent_block( $content ); + + $response->data['content']['raw'] = $content; + + // No need to inject hooked blocks twice. + $priority = has_filter( 'the_content', 'apply_block_hooks_to_content' ); + if ( false !== $priority ) { + remove_filter( 'the_content', 'apply_block_hooks_to_content', $priority ); + } + + /** This filter is documented in wp-includes/post-template.php */ + $response->data['content']['rendered'] = apply_filters( 'the_content', $content ); + + // Add back the filter. + if ( false !== $priority ) { + add_filter( 'the_content', 'apply_block_hooks_to_content', $priority ); + } + + return $response; +} +add_filter( 'rest_prepare_page', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); +add_filter( 'rest_prepare_post', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); From 5a78e0c7f8b12936ca9c1da2591f270d98b5c871 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 14:19:57 +0100 Subject: [PATCH 04/10] Allow first/last child insertion --- packages/block-library/src/post-content/index.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/block-library/src/post-content/index.php b/packages/block-library/src/post-content/index.php index 25be880cc47887..97f030095ac4f7 100644 --- a/packages/block-library/src/post-content/index.php +++ b/packages/block-library/src/post-content/index.php @@ -46,10 +46,25 @@ function render_block_core_post_content( $attributes, $content, $block ) { $content .= wp_link_pages( array( 'echo' => 0 ) ); } + // Wrap in Post Content block so the Block Hooks algorithm can insert blocks + // that are hooked as first or last child of `core/post-content`. + $content = get_comment_delimited_block_content( + 'core/post-content', + $attributes, // TODO: Merge ignoredHookedBlocks from post meta. + $content + ); + + // We need to remove the `core/post-content` block wrapper after the Block Hooks algorithm, + // but before `do_blocks` runs, as it would otherwise attempt to render the same block again -- + // thus recursing infinitely. + add_filter( 'the_content', 'remove_serialized_parent_block', 8 ); + /** This filter is documented in wp-includes/post-template.php */ $content = apply_filters( 'the_content', str_replace( ']]>', ']]>', $content ) ); unset( $seen_ids[ $post_id ] ); + remove_filter( 'the_content', 'remove_serialized_parent_block', 8 ); + if ( empty( $content ) ) { return ''; } From 705d3f69d6afb3a65aaf1f5c72e36f017b46e458 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 14:21:25 +0100 Subject: [PATCH 05/10] Update backport changelog --- backport-changelog/6.8/7889.md | 3 --- backport-changelog/6.8/7898.md | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 backport-changelog/6.8/7889.md create mode 100644 backport-changelog/6.8/7898.md diff --git a/backport-changelog/6.8/7889.md b/backport-changelog/6.8/7889.md deleted file mode 100644 index 7b1cc28f4a969e..00000000000000 --- a/backport-changelog/6.8/7889.md +++ /dev/null @@ -1,3 +0,0 @@ -https://github.com/WordPress/wordpress-develop/pull/7889 - -* https://github.com/WordPress/gutenberg/pull/67272 diff --git a/backport-changelog/6.8/7898.md b/backport-changelog/6.8/7898.md new file mode 100644 index 00000000000000..d824c5da82ec1b --- /dev/null +++ b/backport-changelog/6.8/7898.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7898 + +* https://github.com/WordPress/gutenberg/pull/67272 From 7261f2fe77c808607e8d61d1039ddd65682529bc Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 15:12:02 +0100 Subject: [PATCH 06/10] Respect ignoredHookedBlocks post meta when rendering --- packages/block-library/src/post-content/index.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/post-content/index.php b/packages/block-library/src/post-content/index.php index 97f030095ac4f7..e0a06b7217eebe 100644 --- a/packages/block-library/src/post-content/index.php +++ b/packages/block-library/src/post-content/index.php @@ -46,11 +46,19 @@ function render_block_core_post_content( $attributes, $content, $block ) { $content .= wp_link_pages( array( 'echo' => 0 ) ); } + $ignored_hooked_blocks = get_post_meta( $post_id, '_wp_ignored_hooked_blocks', true ); + if ( ! empty( $ignored_hooked_blocks ) ) { + $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); + $attributes['metadata'] = array( + 'ignoredHookedBlocks' => $ignored_hooked_blocks, + ); + } + // Wrap in Post Content block so the Block Hooks algorithm can insert blocks // that are hooked as first or last child of `core/post-content`. $content = get_comment_delimited_block_content( 'core/post-content', - $attributes, // TODO: Merge ignoredHookedBlocks from post meta. + $attributes, $content ); From fbb68663480cb8e8e2e453299543874f2fe8c1f5 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 15:32:14 +0100 Subject: [PATCH 07/10] Backport update_ignored_hooked_blocks_postmeta --- lib/compat/wordpress-6.8/blocks.php | 79 +++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 76f616ef7ac627..4e48b95a5bb04f 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -219,3 +219,82 @@ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { } add_filter( 'rest_prepare_page', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); add_filter( 'rest_prepare_post', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); + +/** + * Updates the wp_postmeta with the list of ignored hooked blocks + * where the inner blocks are stored as post content. + * + * @since 6.6.0 + * @since 6.8.0 Support non-`wp_navigation` post types. + * @access private + * + * @param stdClass $post Post object. + * @return stdClass The updated post object. + */ +function gutenberg_update_ignored_hooked_blocks_postmeta( $post ) { + /* + * In this scenario the user has likely tried to create a new post object via the REST API. + * In which case we won't have a post ID to work with and store meta against. + */ + if ( empty( $post->ID ) ) { + return $post; + } + + /* + * Skip meta generation when consumers intentionally update specific fields + * and omit the content update. + */ + if ( ! isset( $post->post_content ) ) { + return $post; + } + + $attributes = array(); + + $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); + if ( ! empty( $ignored_hooked_blocks ) ) { + $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); + $attributes['metadata'] = array( + 'ignoredHookedBlocks' => $ignored_hooked_blocks, + ); + } + + if ( 'wp_navigation' === $post->post_type ) { + $wrapper_block_type = 'core/navigation'; + } else { + $wrapper_block_type = 'core/post-content'; + } + + $markup = get_comment_delimited_block_content( + $wrapper_block_type, + $attributes, + $post->post_content + ); + + $existing_post = get_post( $post->ID ); + // Merge the existing post object with the updated post object to pass to the block hooks algorithm for context. + $context = (object) array_merge( (array) $existing_post, (array) $post ); + $serialized_block = apply_block_hooks_to_content( $markup, $context, 'set_ignored_hooked_blocks_metadata' ); + $root_block = parse_blocks( $serialized_block )[0]; + + $ignored_hooked_blocks = isset( $root_block['attrs']['metadata']['ignoredHookedBlocks'] ) + ? $root_block['attrs']['metadata']['ignoredHookedBlocks'] + : array(); + + if ( ! empty( $ignored_hooked_blocks ) ) { + $existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); + if ( ! empty( $existing_ignored_hooked_blocks ) ) { + $existing_ignored_hooked_blocks = json_decode( $existing_ignored_hooked_blocks, true ); + $ignored_hooked_blocks = array_unique( array_merge( $ignored_hooked_blocks, $existing_ignored_hooked_blocks ) ); + } + + if ( ! isset( $post->meta_input ) ) { + $post->meta_input = array(); + } + $post->meta_input['_wp_ignored_hooked_blocks'] = json_encode( $ignored_hooked_blocks ); + } + + $post->post_content = remove_serialized_parent_block( $serialized_block ); + return $post; +} +add_filter( 'rest_pre_insert_page', 'gutenberg_update_ignored_hooked_blocks_postmeta' ); +add_filter( 'rest_pre_insert_post', 'gutenberg_update_ignored_hooked_blocks_postmeta' ); From f0908fbd22777a19c0b9cac5f8e62651976ba37d Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 15:32:27 +0100 Subject: [PATCH 08/10] Backport bugfix --- lib/compat/wordpress-6.8/blocks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 4e48b95a5bb04f..9fb0bc75cbaf89 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -273,6 +273,7 @@ function gutenberg_update_ignored_hooked_blocks_postmeta( $post ) { $existing_post = get_post( $post->ID ); // Merge the existing post object with the updated post object to pass to the block hooks algorithm for context. $context = (object) array_merge( (array) $existing_post, (array) $post ); + $context = new WP_Post( $context ); // Convert to WP_Post object. $serialized_block = apply_block_hooks_to_content( $markup, $context, 'set_ignored_hooked_blocks_metadata' ); $root_block = parse_blocks( $serialized_block )[0]; From 9266a18ed6d42097667266532d5ba3b32efaa27c Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 3 Dec 2024 18:29:35 +0100 Subject: [PATCH 09/10] Add back check for post_type --- lib/compat/wordpress-6.8/blocks.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 9fb0bc75cbaf89..d0f0e4b2eabc73 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -248,6 +248,13 @@ function gutenberg_update_ignored_hooked_blocks_postmeta( $post ) { return $post; } + /* + * Skip meta generation if post type is not set. + */ + if ( ! isset( $post->post_type ) ) { + return $post; + } + $attributes = array(); $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); From 87964c120553888036ebf656665e80d0bd4dddaa Mon Sep 17 00:00:00 2001 From: Bernie Reiter <96308+ockham@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:06:26 +0100 Subject: [PATCH 10/10] Tweak PHPDoc --- lib/compat/wordpress-6.8/blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index d0f0e4b2eabc73..22da5373c354dd 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -225,7 +225,7 @@ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { * where the inner blocks are stored as post content. * * @since 6.6.0 - * @since 6.8.0 Support non-`wp_navigation` post types. + * @since 6.8.0 Support other post types. (Previously, it was limited to `wp_navigation` only.) * @access private * * @param stdClass $post Post object.