From 4c548c3846f770b0293a89c43cc314c28a44a2e1 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Wed, 18 Dec 2024 18:04:18 +0200 Subject: [PATCH 1/3] feat: resolve block patterns (#323) Co-authored-by: Ta5r --- .changeset/tiny-news-warn.md | 5 ++ includes/Data/ContentBlocksResolver.php | 28 +++++++++++ tests/unit/ContentBlocksResolverTest.php | 64 ++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 .changeset/tiny-news-warn.md diff --git a/.changeset/tiny-news-warn.md b/.changeset/tiny-news-warn.md new file mode 100644 index 00000000..0c106145 --- /dev/null +++ b/.changeset/tiny-news-warn.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wp-graphql-content-blocks": minor +--- + +feat: add support for resolving Block Patterns diff --git a/includes/Data/ContentBlocksResolver.php b/includes/Data/ContentBlocksResolver.php index 2a61e6c1..fbf07786 100644 --- a/includes/Data/ContentBlocksResolver.php +++ b/includes/Data/ContentBlocksResolver.php @@ -151,6 +151,8 @@ private static function handle_do_block( array $block ): ?array { $block = self::populate_reusable_blocks( $block ); + $block = self::populate_pattern_inner_blocks( $block ); + // Prepare innerBlocks. if ( ! empty( $block['innerBlocks'] ) ) { $block['innerBlocks'] = self::handle_do_blocks( $block['innerBlocks'] ); @@ -208,6 +210,32 @@ private static function populate_reusable_blocks( array $block ): array { return array_merge( ...$parsed_blocks ); } + /** + * Populates the pattern innerBlocks with the blocks from the pattern. + * + * @param array $block The block to populate. + * @return array The populated block. + */ + private static function populate_pattern_inner_blocks( array $block ): array { + // Bail if not WP 6.6 or later. + if ( ! function_exists( 'resolve_pattern_blocks' ) ) { + return $block; + } + + if ( 'core/pattern' !== $block['blockName'] || ! isset( $block['attrs']['slug'] ) ) { + return $block; + } + + $resolved_patterns = resolve_pattern_blocks( [ $block ] ); + + if ( empty( $resolved_patterns ) ) { + return $block; + } + + $block['innerBlocks'] = $resolved_patterns; + return $block; + } + /** * Flattens a list blocks into a single array * diff --git a/tests/unit/ContentBlocksResolverTest.php b/tests/unit/ContentBlocksResolverTest.php index e9331a02..ad509119 100644 --- a/tests/unit/ContentBlocksResolverTest.php +++ b/tests/unit/ContentBlocksResolverTest.php @@ -349,4 +349,68 @@ protected function assertEqualBlocks( $expected, $actual, $message = '' ) { $this->assertEqualBlocks( $expected_inner_block, $actual_inner_blocks[ $index ], $message ); } } + + /** + * Tests that pattern inner blocks are resolved correctly. + */ + public function test_resolve_content_blocks_resolves_pattern_inner_blocks() { + // Skip if pattern resolution functionality is not supported. + if ( ! function_exists( 'resolve_pattern_blocks' ) ) { + $this->markTestSkipped( 'Pattern block resolution not supported in this WordPress version.' ); + } + + // Create a pattern + $pattern_name = 'test/pattern-blocks'; + $pattern_content = ' + +

Pattern Paragraph

+ + +

Pattern Heading

+ + '; + + // Register the pattern. + register_block_pattern( + $pattern_name, + [ + 'title' => 'Test Pattern', + 'content' => $pattern_content, + ] + ); + + // Update post content to include pattern block. + $post_content = ' + + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $post_content, + ] + ); + + $post_model = new Post( get_post( $this->post_id ) ); + + // Resolve blocks as nested. + $resolved_blocks = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => false ] ); + + $this->assertCount( 1, $resolved_blocks, 'There should be only one top-level block (pattern).' ); + $this->assertEquals( 'core/pattern', $resolved_blocks[0]['blockName'] ); + $this->assertCount( 2, $resolved_blocks[0]['innerBlocks'], 'There should be two inner blocks in the pattern.' ); + $this->assertEquals( 'core/paragraph', $resolved_blocks[0]['innerBlocks'][0]['blockName'] ); + $this->assertEquals( 'core/heading', $resolved_blocks[0]['innerBlocks'][1]['blockName'] ); + + // Resolve blocks as flat. + $resolved_flat_blocks = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ] ); + + $this->assertCount( 3, $resolved_flat_blocks, 'There should be three blocks when flattened.' ); + $this->assertEquals( 'core/pattern', $resolved_flat_blocks[0]['blockName'] ); + $this->assertEquals( 'core/paragraph', $resolved_flat_blocks[1]['blockName'] ); + $this->assertEquals( 'core/heading', $resolved_flat_blocks[2]['blockName'] ); + + // Cleanup: Unregistering the pattern. + unregister_block_pattern( $pattern_name ); + } } From 19f6e272d8ae9b35a2fe18ac005cb4d3ed8e9e65 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Thu, 19 Dec 2024 19:28:20 +0200 Subject: [PATCH 2/3] feat: add support for resolving Template Part blocks (#324) Co-authored-by: Ta5r --- .changeset/silver-cups-explode.md | 5 +++ includes/Data/ContentBlocksResolver.php | 32 +++++++++++++- tests/unit/ContentBlocksResolverTest.php | 54 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 .changeset/silver-cups-explode.md diff --git a/.changeset/silver-cups-explode.md b/.changeset/silver-cups-explode.md new file mode 100644 index 00000000..a23860d9 --- /dev/null +++ b/.changeset/silver-cups-explode.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wp-graphql-content-blocks": minor +--- + +feat: add support for resolving Template Part blocks diff --git a/includes/Data/ContentBlocksResolver.php b/includes/Data/ContentBlocksResolver.php index fbf07786..dcf6c43c 100644 --- a/includes/Data/ContentBlocksResolver.php +++ b/includes/Data/ContentBlocksResolver.php @@ -148,7 +148,7 @@ private static function handle_do_block( array $block ): ?array { $block['clientId'] = uniqid(); // @todo apply more hydrations. - + $block = self::populate_template_part_inner_blocks( $block ); $block = self::populate_reusable_blocks( $block ); $block = self::populate_pattern_inner_blocks( $block ); @@ -183,6 +183,36 @@ private static function is_block_empty( array $block ): bool { return empty( trim( $stripped ?? '' ) ); } + /** + * Populates the innerBlocks of a template part block with the blocks from the template part. + * + * @param array $block The block to populate. + * + * @return array The populated block. + */ + private static function populate_template_part_inner_blocks( array $block ): array { + // Bail if not WP 5.8 or later. + if ( ! function_exists( 'get_block_templates' ) ) { + return $block; + } + + if ( 'core/template-part' !== $block['blockName'] || ! isset( $block['attrs']['slug'] ) ) { + return $block; + } + + $matching_templates = get_block_templates( [ 'slug__in' => [ $block['attrs']['slug'] ] ], 'wp_template_part' ); + + $template_blocks = ! empty( $matching_templates[0]->content ) ? self::parse_blocks( $matching_templates[0]->content ) : null; + + if ( empty( $template_blocks ) ) { + return $block; + } + + $block['innerBlocks'] = $template_blocks; + + return $block; + } + /** * Populates reusable blocks with the blocks from the reusable ref ID. * diff --git a/tests/unit/ContentBlocksResolverTest.php b/tests/unit/ContentBlocksResolverTest.php index ad509119..dea2faae 100644 --- a/tests/unit/ContentBlocksResolverTest.php +++ b/tests/unit/ContentBlocksResolverTest.php @@ -413,4 +413,58 @@ public function test_resolve_content_blocks_resolves_pattern_inner_blocks() { // Cleanup: Unregistering the pattern. unregister_block_pattern( $pattern_name ); } + + /** + * Tests that template part inner blocks are resolved correctly. + */ + public function test_resolve_content_blocks_resolves_template_part_inner_blocks() { + // Skip if template part functionality is not supported + if ( ! function_exists( 'get_block_templates' ) ) { + $this->markTestSkipped( 'Template part functionality not supported in this WordPress version.' ); + } + + // Mock the get_block_templates function to control the output. + $mock_template = (object) [ + 'content' => '', + ]; + + add_filter( + 'get_block_templates', + function () use ( $mock_template ) { + return [ $mock_template ]; + } + ); + + // Update post content to include template part block + $post_content = ' + + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $post_content, + ] + ); + + $post_model = new Post( get_post( $this->post_id ) ); + + // Resolve blocks as nested + $resolved_blocks = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => false ] ); + + // Assertions + $this->assertCount( 1, $resolved_blocks, 'There should be only one top-level block (template-part).' ); + $this->assertEquals( 'core/template-part', $resolved_blocks[0]['blockName'] ); + $this->assertCount( 2, $resolved_blocks[0]['innerBlocks'], 'There should be two inner blocks in the template part.' ); + $this->assertEquals( 'core/paragraph', $resolved_blocks[0]['innerBlocks'][0]['blockName'] ); + $this->assertEquals( 'core/heading', $resolved_blocks[0]['innerBlocks'][1]['blockName'] ); + + // Resolve blocks as flat + $resolved_flat_blocks = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ] ); + + $this->assertCount( 3, $resolved_flat_blocks, 'There should be three blocks when flattened.' ); + $this->assertEquals( 'core/template-part', $resolved_flat_blocks[0]['blockName'] ); + $this->assertEquals( 'core/paragraph', $resolved_flat_blocks[1]['blockName'] ); + $this->assertEquals( 'core/heading', $resolved_flat_blocks[2]['blockName'] ); + } } From 5381c84f0483412b8ebb3cf25ca65794e691353a Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Thu, 19 Dec 2024 00:37:55 +0200 Subject: [PATCH 3/3] feat: add support for resolving PostContent blocks Co-authored-by: Ta5r --- .changeset/itchy-mugs-sniff.md | 5 ++ includes/Data/ContentBlocksResolver.php | 31 +++++++++- tests/unit/ContentBlocksResolverTest.php | 77 ++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 .changeset/itchy-mugs-sniff.md diff --git a/.changeset/itchy-mugs-sniff.md b/.changeset/itchy-mugs-sniff.md new file mode 100644 index 00000000..4a457962 --- /dev/null +++ b/.changeset/itchy-mugs-sniff.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wp-graphql-content-blocks": minor +--- + +feat: add support for resolving PostContent blocks diff --git a/includes/Data/ContentBlocksResolver.php b/includes/Data/ContentBlocksResolver.php index dcf6c43c..e4c589ad 100644 --- a/includes/Data/ContentBlocksResolver.php +++ b/includes/Data/ContentBlocksResolver.php @@ -149,8 +149,8 @@ private static function handle_do_block( array $block ): ?array { // @todo apply more hydrations. $block = self::populate_template_part_inner_blocks( $block ); + $block = self::populate_post_content_inner_blocks( $block ); $block = self::populate_reusable_blocks( $block ); - $block = self::populate_pattern_inner_blocks( $block ); // Prepare innerBlocks. @@ -213,6 +213,35 @@ private static function populate_template_part_inner_blocks( array $block ): arr return $block; } + /** + * Populates the innerBlocks of a core/post-content block with the blocks from the post content. + * + * @param array $block The block to populate. + * + * @return array The populated block. + */ + private static function populate_post_content_inner_blocks( array $block ): array { + if ( 'core/post-content' !== $block['blockName'] ) { + return $block; + } + + $post = get_post(); + + if ( ! $post ) { + return $block; + } + + $parsed_blocks = ! empty( $post->post_content ) ? self::parse_blocks( $post->post_content ) : null; + + if ( empty( $parsed_blocks ) ) { + return $block; + } + + $block['innerBlocks'] = $parsed_blocks; + + return $block; + } + /** * Populates reusable blocks with the blocks from the reusable ref ID. * diff --git a/tests/unit/ContentBlocksResolverTest.php b/tests/unit/ContentBlocksResolverTest.php index dea2faae..0bf575c4 100644 --- a/tests/unit/ContentBlocksResolverTest.php +++ b/tests/unit/ContentBlocksResolverTest.php @@ -4,6 +4,7 @@ use WPGraphQL\ContentBlocks\Data\ContentBlocksResolver; use WPGraphQL\Model\Post; +use WPGraphQL\Model\Term; final class ContentBlocksResolverTest extends PluginTestCase { public $instance; @@ -350,6 +351,82 @@ protected function assertEqualBlocks( $expected, $actual, $message = '' ) { } } + /** + * Tests that post content inner blocks are resolved correctly. + */ + public function test_resolve_content_blocks_resolves_post_content_inner_blocks() { + // The post content holds what will be the inner blocks. + $post_content = ' + +
+ +
+ +

Column 1 Paragraph

+ +
+ + +
+ +

Column 2 Heading

+ +
+ +
+ + '; + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $post_content, + ] + ); + + // The term holds the PostContent block. + $term_id = $this->factory()->term->create( + [ + 'taxonomy' => 'category', + 'name' => 'Post Content Term', + 'description' => '', + ] + ); + $model_with_blocks = new Term( get_term( $term_id ) ); + + add_filter( 'wpgraphql_content_blocks_resolver_content' , function( $content, $node ) { + if ( $node instanceof Term ) { + // The PostContent Block resolves based on the global. + global $post; + $post = get_post( $this->post_id ); + + return $node->description; + } + + return $content; + }, 10, 2 ); + + // Resolve blocks as nested. + $resolved_blocks = $this->instance->resolve_content_blocks( $model_with_blocks, [ 'flat' => false ] ); + + // Assertions for nested blocks. + $this->assertCount( 1, $resolved_blocks, 'There should be only one top-level block (post-content).' ); + $this->assertEquals( 'core/post-content', $resolved_blocks[0]['blockName'] ); + // $this->assertCount( 1, $resolved_blocks[0]['innerBlocks'], 'There should be one top-level block in post content.' ); + $this->assertEquals( 'core/columns', $resolved_blocks[0]['innerBlocks'][0]['blockName'] ); + + // Resolve blocks as flat + $resolved_flat_blocks = $this->instance->resolve_content_blocks( $model_with_blocks, [ 'flat' => true ] ); + + // Assertions for flat blocks + $this->assertCount( 6, $resolved_flat_blocks, 'There should be five blocks when flattened.' ); + $this->assertEquals( 'core/post-content', $resolved_flat_blocks[0]['blockName'] ); + $this->assertEquals( 'core/columns', $resolved_flat_blocks[1]['blockName'] ); + $this->assertEquals( 'core/column', $resolved_flat_blocks[2]['blockName'] ); + $this->assertEquals( 'core/paragraph', $resolved_flat_blocks[3]['blockName'] ); + $this->assertEquals( 'core/column', $resolved_flat_blocks[4]['blockName'] ); + $this->assertEquals( 'core/heading', $resolved_flat_blocks[5]['blockName'] ); + } + /** * Tests that pattern inner blocks are resolved correctly. */