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

feat : backport populate_post_content_inner_blocks #34

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/itchy-mugs-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wpengine/wp-graphql-content-blocks": minor
---

feat: add support for resolving PostContent blocks
5 changes: 5 additions & 0 deletions .changeset/silver-cups-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wpengine/wp-graphql-content-blocks": minor
---

feat: add support for resolving Template Part blocks
5 changes: 5 additions & 0 deletions .changeset/tiny-news-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wpengine/wp-graphql-content-blocks": minor
---

feat: add support for resolving Block Patterns
89 changes: 88 additions & 1 deletion includes/Data/ContentBlocksResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,10 @@ 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_post_content_inner_blocks( $block );
$block = self::populate_reusable_blocks( $block );
$block = self::populate_pattern_inner_blocks( $block );

// Prepare innerBlocks.
if ( ! empty( $block['innerBlocks'] ) ) {
Expand Down Expand Up @@ -181,6 +183,65 @@ 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<string,mixed> $block The block to populate.
*
* @return array<string,mixed> 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 the innerBlocks of a core/post-content block with the blocks from the post content.
*
* @param array<string,mixed> $block The block to populate.
*
* @return array<string,mixed> 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.
*
Expand Down Expand Up @@ -208,6 +269,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<string,mixed> $block The block to populate.
* @return array<string,mixed> 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
*
Expand Down
195 changes: 195 additions & 0 deletions tests/unit/ContentBlocksResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use WPGraphQL\ContentBlocks\Data\ContentBlocksResolver;
use WPGraphQL\Model\Post;
use WPGraphQL\Model\Term;

final class ContentBlocksResolverTest extends PluginTestCase {
public $instance;
Expand Down Expand Up @@ -349,4 +350,198 @@ protected function assertEqualBlocks( $expected, $actual, $message = '' ) {
$this->assertEqualBlocks( $expected_inner_block, $actual_inner_blocks[ $index ], $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 = '
<!-- wp:columns -->
<div class="wp-block-columns">
<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:paragraph -->
<p>Column 1 Paragraph</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:column -->
<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:heading -->
<h2>Column 2 Heading</h2>
<!-- /wp:heading -->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
';
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' => '<!-- wp:post-content /-->',
]
);
$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.
*/
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 = '
<!-- wp:paragraph -->
<p>Pattern Paragraph</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2>Pattern Heading</h2>
<!-- /wp: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:pattern {"slug":"test/pattern-blocks"} /-->
';

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 );
}

/**
* 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' => '<!-- wp:paragraph /--><!-- wp:heading /-->',
];

add_filter(
'get_block_templates',
function () use ( $mock_template ) {
return [ $mock_template ];
}
);

// Update post content to include template part block
$post_content = '
<!-- wp:template-part {"slug":"test-template-part"} /-->
';

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'] );
}
}