Skip to content
Merged
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
98 changes: 75 additions & 23 deletions src/Modules/GraphQL/Data/ContentBlocksResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ final class ContentBlocksResolver {
* @return array<string,mixed> The list of content blocks.
*/
public static function resolve_content_blocks( $node, $args, $allowed_block_names = [] ): array {
global $post_id;

/**
* 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 +79,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 +117,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 +150,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 +166,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 +198,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 +224,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 +242,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 +327,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 +346,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 );
}
);
}
}
6 changes: 3 additions & 3 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_resolver_content', [ $this, 'get_content_from_model' ], 10, 2 );
add_filter( 'graphql_object_fields', [ $this, 'overload_content_blocks_resolver' ], 10 );
add_filter( 'wpgraphql_content_blocks_resolver_content', [ $this, 'get_content_from_model' ], 10, 2 );

// 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 Down Expand Up @@ -99,7 +99,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