Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
97 changes: 75 additions & 22 deletions src/Modules/GraphQL/Data/ContentBlocksResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ final class ContentBlocksResolver {
public static function resolve_content_blocks( $node, $args, $allowed_block_names = [] ): array {
global $post_id;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this it's unused and removed in v4.2.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed 🚀


/**
* When this filter returns a non-null value, the content blocks resolver will use that value
*
* @param ?array $content_blocks The content blocks to parse.
* @param \WPGraphQL\Model\Model $node The node we are resolving.
* @param array $args GraphQL query args to pass to the connection resolver.
* @param array $allowed_block_names The list of allowed block names to filter.
*/
$pre_resolved_blocks = apply_filters( 'wpgraphql_content_blocks_pre_resolve_blocks', null, $node, $args, $allowed_block_names ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPGraphQL filter.

if ( null !== $pre_resolved_blocks && is_array( $pre_resolved_blocks ) ) {
return $pre_resolved_blocks;
}

$content = null;
if ( $node instanceof Post ) {

Expand Down Expand Up @@ -66,15 +80,18 @@ public static function resolve_content_blocks( $node, $args, $allowed_block_name
}

// Final level of filtering out blocks not in the allowed list.
if ( ! empty( $allowed_block_names ) ) {
$parsed_blocks = array_filter(
$parsed_blocks,
static function ( $parsed_block ) use ( $allowed_block_names ) {
return in_array( $parsed_block['blockName'], $allowed_block_names, true );
},
ARRAY_FILTER_USE_BOTH
);
}
$parsed_blocks = self::filter_allowed_blocks( $parsed_blocks, $allowed_block_names );

/**
* Filters the content blocks after they have been resolved.
*
* @param array $parsed_blocks The parsed blocks.
* @param \WPGraphQL\Model\Model $node The node we are resolving.
* @param array $args GraphQL query args to pass to the connection resolver.
* @param array $allowed_block_names The list of allowed block names to filter.
*/
$parsed_blocks = apply_filters( 'wpgraphql_content_blocks_resolve_blocks', $parsed_blocks, $node, $args, $allowed_block_names ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPGraphQL filter.

return $parsed_blocks;
}

Expand All @@ -101,13 +118,16 @@ private static function flatten_block_list( $blocks ): array {
* @return array<string,mixed> The flattened block.
*/
private static function flatten_inner_blocks( $block ): array {
$result = [];
$result = [];

// Assign a unique clientId to the block if it doesn't already have one.
$block['clientId'] = isset( $block['clientId'] ) ? $block['clientId'] : uniqid();
array_push( $result, $block );

foreach ( $block['innerBlocks'] as $child ) {
$child['parentClientId'] = $block['clientId'];

// Flatten the child, and merge with the result.
$result = array_merge( $result, self::flatten_inner_blocks( $child ) );
}

Expand All @@ -131,6 +151,8 @@ private static function parse_blocks( $content ): array {
/**
* Recursively process blocks.
*
* This mirrors the `do_blocks` function in WordPress which is responsible for hydrating certain block attributes and supports, but without the forced rendering.
*
* @param array<string,mixed>[] $blocks Blocks data.
*
* @return array<string,mixed>[] The processed blocks.
Expand All @@ -145,7 +167,7 @@ private static function handle_do_blocks( array $blocks ): array {
}

// Remove empty blocks.
return array_filter( $parsed );
return array_values( array_filter( $parsed ) );
}

/**
Expand Down Expand Up @@ -177,6 +199,13 @@ private static function handle_do_block( array $block ): ?array {

$block = self::populate_pattern_inner_blocks( $block );

/**
* Filters the block data after it has been processed.
*
* @param array<string,mixed> $block The block data.
*/
$block = apply_filters( 'wpgraphql_content_blocks_handle_do_block', $block ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPGraphQL filter.

// Prepare innerBlocks.
if ( ! empty( $block['innerBlocks'] ) ) {
$block['innerBlocks'] = self::handle_do_blocks( $block['innerBlocks'] );
Expand All @@ -196,19 +225,12 @@ private static function is_block_empty( array $block ): bool {
return false;
}

if ( ! empty( $block['innerBlocks'] ) || ! empty( trim( $block['innerHTML'] ) ) ) {
return false;
// If there is no innerHTML or innerContent, we can consider it empty.
if ( empty( $block['innerHTML'] ) && empty( $block['innerContent'] ) ) {
return true;
}

// $block['innerContent'] can be an array, we need to check if it's empty, including empty strings.
if ( ! empty( $block['innerContent'] ) ) {
$inner_content = implode( '', $block['innerContent'] );
if ( ! empty( trim( $inner_content ) ) ) {
return false;
}
}

$stripped = preg_replace( '/<!--(.*)-->/Uis', '', render_block( $block ) );
$stripped = preg_replace( '/<!--(.*)-->/Uis', '', $block['innerHTML'] );

return empty( trim( $stripped ?? '' ) );
}
Expand All @@ -221,6 +243,11 @@ private static function is_block_empty( array $block ): bool {
* @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;
}
Expand Down Expand Up @@ -301,6 +328,11 @@ private static function populate_post_content_inner_blocks( array $block ): arra
* @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;
}
Expand All @@ -315,4 +347,25 @@ private static function populate_pattern_inner_blocks( array $block ): array {

return $block;
}

/**
* Filters out disallowed blocks from the list of blocks
*
* @param array<string,mixed> $blocks A list of blocks to filter.
* @param string[] $allowed_block_names The list of allowed block names to filter.
*
* @return array<string,mixed> The filtered list of blocks.
*/
private static function filter_allowed_blocks( array $blocks, array $allowed_block_names ): array {
if ( empty( $allowed_block_names ) ) {
return $blocks;
}

return array_filter(
$blocks,
static function ( $block ) use ( $allowed_block_names ) {
return in_array( $block['blockName'], $allowed_block_names, true );
}
);
}
}
27 changes: 3 additions & 24 deletions src/Modules/GraphQL/SchemaFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ final class SchemaFilters implements Registrable {
*/
public function register_hooks(): void {
// No need to check for dependencies, since missing filters will just be ignored.
add_filter( 'wpgraphql_content_blocks_handle_do_block', [ ContentBlocksResolver::class, 'handle_do_block' ], 10, 1 );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why we're not getting the inner blocks - you got rid of the graphql_object_fields filter which let us overload the entire class, and instead are only overloading the internal handle_do_block().

Revert this and your PR should be working.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Donew ✅

add_filter( 'wpgraphql_content_blocks_resolver_content', [ $this, 'get_content_from_model' ], 10, 2 );
add_filter( 'graphql_object_fields', [ $this, 'overload_content_blocks_resolver' ], 10 );

// Cache rendered blocks.
add_filter( 'pre_render_block', [ $this, 'get_cached_rendered_block' ], 10, 2 ); // @todo: this should be as early priority as possible
add_filter( 'pre_render_block', [ $this, 'get_cached_rendered_block' ], 11, 2 ); // @todo: this should be as early priority as possible
// We want to cache the rendered block as late as possible to ensure we're caching the final output.
add_filter( 'render_block', [ $this, 'cache_rendered_block' ], PHP_INT_MAX - 1, 2 );
}
Expand All @@ -60,27 +60,6 @@ public function get_content_from_model( $content, $model ): string {
return $content;
}

/**
* Overloads the content blocks resolver to use ourn own resolver.
*
* @todo This is necessary because WPGraphQL Content Blocks' resolver is broken.
*
* @param array<string,mixed> $fields The config for the interface type.
*
* @return array<string,mixed>
*/
public function overload_content_blocks_resolver( array $fields ): array {
if ( ! isset( $fields['editorBlocks'] ) ) {
return $fields;
}

$fields['editorBlocks']['resolve'] = static function ( $node, $args ) {
return ContentBlocksResolver::resolve_content_blocks( $node, $args );
};

return $fields;
}

/**
* Get the cached rendered block.
*
Expand All @@ -99,7 +78,7 @@ public function get_cached_rendered_block( $block_content, $parsed_block ) {
}

// Bail if block content is already set.
if ( null !== $block_content ) {
if ( null !== $block_content || empty( $parsed_block ) ) {
return $block_content;
}

Expand Down
Loading