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

Block Hooks: Respect "multiple": false #7443

59 changes: 57 additions & 2 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -1056,9 +1056,64 @@ function apply_block_hooks_to_content( $content, $context, $callback = 'insert_h
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $context, $callback );
}

$blocks = parse_blocks( $content );
$block_allows_multiple_instances = array();
/*
* Remove hooked blocks from `$hooked_block_types` if they have `multiple` set to false and
* are already present in `$content`.
*/
foreach ( $hooked_blocks as &$relative_positions ) {
foreach ( $relative_positions as &$hooked_block_types ) {
foreach ( $hooked_block_types as &$hooked_block_type ) {
$hooked_block_type_definition =
WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type );
if ( block_has_support( $hooked_block_type_definition, 'multiple', true ) ) {
$block_allows_multiple_instances[ $hooked_block_type ] = true;
} elseif ( has_block( $hooked_block_type, $content ) ) {
unset( $hooked_block_type );
if ( empty( $hooked_block_types ) ) {
unset( $hooked_block_types );
}
if ( empty( $relative_positions ) ) {
unset( $relative_positions );
}
ockham marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

return traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
/*
* We also need to cover the case where there the hooked block is not present in
* `$content` at first and we're allowed to insert it once -- but not again.
*/
$suppress_single_instance_blocks = static function ( $hooked_block_types, $relative_position, $anchor_block_name ) use ( &$block_allows_multiple_instances, $content ) {
foreach ( $hooked_block_types as $index => $hooked_block_type ) {
if ( ! isset( $block_allows_multiple_instances[ $hooked_block_type ] ) ) {
$hooked_block_type_definition =
WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type );

$block_allows_multiple_instances[ $hooked_block_type ] =
block_has_support( $hooked_block_type_definition, 'multiple', true );

if ( ! $block_allows_multiple_instances[ $hooked_block_type ] && has_block( $hooked_block_type, $content ) ) {
unset( $hooked_block_types[ $index ] );
}
} elseif ( ! $block_allows_multiple_instances[ $hooked_block_type ] ) {
// An instance of the hooked block was previously encountered, and its `multiple` support is false.
unset( $hooked_block_types[ $index ] );
}
}

return $hooked_block_types;
};
add_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX, 3 );
$content = traverse_and_serialize_blocks(
parse_blocks( $content ),
$before_block_visitor,
$after_block_visitor
);
remove_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX );

return $content;
}

/**
Expand Down
101 changes: 101 additions & 0 deletions tests/phpunit/tests/blocks/applyBlockHooksToContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ public static function wpSetUpBeforeClass() {
),
)
);

register_block_type(
'tests/hooked-block-with-multiple-false',
array(
'block_hooks' => array(
'tests/other-anchor-block' => 'after',
),
'supports' => array(
'multiple' => false,
),
)
);

register_block_type(
'tests/dynamically-hooked-block-with-multiple-false',
array(
'supports' => array(
'multiple' => false,
),
)
);
}

/**
Expand All @@ -38,6 +59,8 @@ public static function wpTearDownAfterClass() {
$registry = WP_Block_Type_Registry::get_instance();

$registry->unregister( 'tests/hooked-block' );
$registry->unregister( 'tests/hooked-block-with-multiple-false' );
$registry->unregister( 'tests/dynamically-hooked-block-with-multiple-false' );
}

/**
Expand Down Expand Up @@ -67,4 +90,82 @@ public function test_apply_block_hooks_to_content_inserts_hooked_block() {
$actual
);
}

/**
* @ticket 61902
*/
public function test_apply_block_hooks_to_content_respect_multiple_false() {
$context = new WP_Block_Template();
$context->content = '<!-- wp:tests/hooked-block-with-multiple-false /--><!-- wp:tests/other-anchor-block /-->';

$actual = apply_block_hooks_to_content( $context->content, $context, 'insert_hooked_blocks' );
$this->assertSame(
'<!-- wp:tests/hooked-block-with-multiple-false /--><!-- wp:tests/other-anchor-block /-->',
$actual
);
}

/**
* @ticket 61902
*/
public function test_apply_block_hooks_to_content_respect_multiple_false_after_inserting_once() {
$context = new WP_Block_Template();
$context->content = '<!-- wp:tests/other-anchor-block /--><!-- wp:tests/other-block /--><!-- wp:tests/other-anchor-block /-->';

$actual = apply_block_hooks_to_content( $context->content, $context, 'insert_hooked_blocks' );
$this->assertSame(
'<!-- wp:tests/other-anchor-block /--><!-- wp:tests/hooked-block-with-multiple-false /--><!-- wp:tests/other-block /--><!-- wp:tests/other-anchor-block /-->',
$actual
);
}

/**
* @ticket 61902
*/
public function test_apply_block_hooks_to_content_respect_multiple_false_with_filter() {
$filter = function ( $hooked_block_types, $relative_position, $anchor_block_type ) {
if ( 'tests/yet-another-anchor-block' === $anchor_block_type && 'after' === $relative_position ) {
$hooked_block_types[] = 'tests/dynamically-hooked-block-with-multiple-false';
}

return $hooked_block_types;
};

$context = new WP_Block_Template();
$context->content = '<!-- wp:tests/dynamically-hooked-block-with-multiple-false /--><!-- wp:tests/yet-another-anchor-block /-->';

add_filter( 'hooked_block_types', $filter, 10, 3 );
$actual = apply_block_hooks_to_content( $context->content, $context, 'insert_hooked_blocks' );
remove_filter( 'hooked_block_types', $filter, 10 );

$this->assertSame(
'<!-- wp:tests/dynamically-hooked-block-with-multiple-false /--><!-- wp:tests/yet-another-anchor-block /-->',
$actual
);
}

/**
* @ticket 61902
*/
public function test_apply_block_hooks_to_content_respect_multiple_false_after_inserting_once_with_filter() {
$filter = function ( $hooked_block_types, $relative_position, $anchor_block_type ) {
if ( 'tests/yet-another-anchor-block' === $anchor_block_type && 'after' === $relative_position ) {
$hooked_block_types[] = 'tests/dynamically-hooked-block-with-multiple-false';
}

return $hooked_block_types;
};

$context = new WP_Block_Template();
$context->content = '<!-- wp:tests/yet-another-anchor-block /--><!-- wp:tests/other-block /--><!-- wp:tests/yet-another-anchor-block /-->';

add_filter( 'hooked_block_types', $filter, 10, 3 );
$actual = apply_block_hooks_to_content( $context->content, $context, 'insert_hooked_blocks' );
remove_filter( 'hooked_block_types', $filter, 10 );

$this->assertSame(
'<!-- wp:tests/yet-another-anchor-block /--><!-- wp:tests/dynamically-hooked-block-with-multiple-false /--><!-- wp:tests/other-block /--><!-- wp:tests/yet-another-anchor-block /-->',
$actual
);
}
}
Loading