wp-graphql-content-blocks icon indicating copy to clipboard operation
wp-graphql-content-blocks copied to clipboard

Bug: Synced pattern wrappers are stripped.

Open justlevine opened this issue 9 months ago • 3 comments

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:

  1. Create a synced pattern on a post.

    Image

  2. Query the post and note the inner blocks are there, but the pattern isnt.

    (The first 3 blocks are part of the pattern) Image

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 avatar Feb 11 '25 17:02 justlevine

@justlevine thanks for the report. We will take a look.

theodesp avatar Feb 12 '25 12:02 theodesp

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:

Image

Now in case the pattern is unsynced then the editor just saves the contents of the block directly into the post content like this:

Image

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 avatar Feb 13 '25 11:02 theodesp

@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.

justlevine avatar Feb 13 '25 22:02 justlevine