wp-graphql-content-blocks
wp-graphql-content-blocks copied to clipboard
Bug: Synced pattern wrappers are stripped.
What
Currently, when querying for synced patterns the patterns are stripped just like unsynced patterns.
This makes it impossible to query the pattern data - for example the attributes?.slug that could allow someone to target and "upgrade" the block with a frontend component.
To replicate:
-
Create a synced pattern on a post.
-
Query the post and note the inner blocks are there, but the pattern isnt.
(The first 3 blocks are part of the pattern)
Additional Notes
- Assumedly the issue is with https://github.com/wpengine/wp-graphql-content-blocks/blob/e9354071642aa78368ec7adfbe8badc495e9931b/includes/Data/ContentBlocksResolver.php#L281 merging everything instead of populating inner blocks w. it.
- Partially-synced patterns also dont work but one step at a time.
@justlevine thanks for the report. We will take a look.
Hey @justlevine I just took a look at it. It seems that with the Synced patterns the contents of the block are replaced by a ref block type
Example with Synced pattern block
<!-- wp:block {"ref":64} /-->
now what is this ref?
This is basically a new post type with the name of the pattern:
Now in case the pattern is unsynced then the editor just saves the contents of the block directly into the post content like this:
Here it just copy pasted the contents of the pattern there.
Now looking at the code for the reusable blocks populate
private static function populate_reusable_blocks( array $block ): array {
if ( 'core/block' !== $block['blockName'] || ! isset( $block['attrs']['ref'] ) ) {
return $block;
}
$reusable_block = get_post( $block['attrs']['ref'] );
if ( ! $reusable_block ) {
return $block;
}
$parsed_blocks = ! empty( $reusable_block->post_content ) ? self::parse_blocks( $reusable_block->post_content ) : null;
if ( empty( $parsed_blocks ) ) {
return $block;
}
return array_merge( ...$parsed_blocks );
}
Its does indeed replaces the core/block with a list of blocks so there is no wrapper block that has inner blocks making it hard to access attributes like slug. Instead of merging the pattern’s inner blocks directly, we can return a core/pattern block, which keeps the structure intact and allows querying by slug.
Proposed Fix
Modify populate_reusable_blocks in ContentBlocksResolver.php into soemthing like this:
private static function populate_reusable_blocks( array $block ): array {
if ( 'core/block' !== $block['blockName'] || ! isset( $block['attrs']['ref'] ) ) {
return $block;
}
$reusable_block = get_post( $block['attrs']['ref'] );
if ( ! $reusable_block ) {
return $block;
}
$parsed_blocks = ! empty( $reusable_block->post_content ) ? self::parse_blocks( $reusable_block->post_content ) : null;
if ( empty( $parsed_blocks ) ) {
return $block;
}
// Wrap it in a core/pattern block instead of flattening
return [
'blockName' => 'core/pattern',
'attrs' => [
'slug' => $reusable_block->post_name, // Use the pattern's post_name as slug
],
'innerBlocks' => $parsed_blocks,
];
}
WDYT?
@theodesp we arrived at the same conclusion on our sync today: better to coerce CoreBlock into the user-expected CorePattern.
However, I'm assuming we need to do a bit more than coerce the type in ::populate_reusable_blocks() - we need to deregister CoreBlock.
Alternatively we can blind rename CoreBlock to CoreSyncedPattern, and then keep the internal core/block the same. This is the best from a futurecompat/tech-debt POV, but understanding when to use CorePattern and CoreSyncedPattern is not the most intuitive from a self-documenting POV.