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

Experiment: Auto-inserting blocks on the frontend #50103

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a9b1993
Insert a string after each comment-content block
ockham Apr 26, 2023
6ac30c1
Use an actual block
ockham Apr 26, 2023
03ebf7e
Start implementing basic last-child mechanism
ockham Apr 26, 2023
9382109
Bit of polish
ockham Apr 26, 2023
01de9dc
More polish
ockham Apr 26, 2023
df6cf73
More polish
ockham Apr 27, 2023
9db02c3
Consistency
ockham Apr 27, 2023
9a46647
Formatting
ockham May 2, 2023
d1b0225
Auto-insert Avatar block under comments
ockham May 2, 2023
6f36442
Use render_block_data filter to insert child block
ockham May 31, 2023
02883ef
Make child block insertion more flexible
ockham May 31, 2023
8bd8c6d
Remove child block insertion from render_block hook
ockham May 31, 2023
e1fde47
Simplify render_block logic
ockham May 31, 2023
9e36ff1
Comment typo
ockham May 31, 2023
1188192
Switch places
ockham May 31, 2023
e8161e8
Insert Social Icon after Post Content
ockham May 31, 2023
05f3bd4
Add comment to render_block hook
ockham May 31, 2023
db0434d
Remove now-obsolete filter args
ockham May 31, 2023
829806e
A bit nicer still
ockham May 31, 2023
da7290d
Manually create inserted block
ockham May 31, 2023
4076c4e
Formatting
ockham May 31, 2023
97fbbae
Fix unit test
ockham May 31, 2023
8b78c34
A bit easier on the eye
ockham Jun 1, 2023
8e9c832
Use null rather than block instance in innerContent
ockham Jun 1, 2023
3666509
A bit nicer to read yet
ockham Jun 5, 2023
2f5f385
Add autoInsert field to block.json schema
ockham Jun 6, 2023
1f7efb4
More precise schema
ockham Jun 6, 2023
d5ab6d1
Just before/after
ockham Jun 6, 2023
9ee00d2
Extract auto-insert settings from metadata
ockham Jun 6, 2023
4495304
Nicer syntax
ockham Jun 6, 2023
4281152
Leverage block-specific render_block hook
ockham Jun 6, 2023
50a63e4
Insert block dynamically
ockham Jun 6, 2023
236dc32
Insert block after Post Content
ockham Jun 6, 2023
2ae6616
Change syntax again
ockham Jun 6, 2023
b3ad57b
Revert "Change syntax again"
ockham Jun 7, 2023
411f160
Add comment about auto-inserted blocks registry
ockham Jun 7, 2023
8bd4f28
Use analog mechanism for child block insertion
ockham Jun 7, 2023
46e30e5
Support block attributes
ockham Jun 7, 2023
023d652
Rename gutenberg_auto_insert_blocks to gutenberg_auto_insert_block
ockham Jun 13, 2023
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
105 changes: 105 additions & 0 deletions lib/experimental/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,108 @@ function gutenberg_register_metadata_attribute( $args ) {
return $args;
}
add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' );

/**
* Return a function that auto-inserts as the first or last inner block of a given block.
*
* @param string $relative_position The position relative to the given block ("first_child" or "last_child").
* @param array $inserted_block The block to insert.
* @return callable A function that accepts a block's content and returns the content with the inserted block.
*/
function gutenberg_auto_insert_child_block( $relative_position, $inserted_block ) {
return function( $parsed_block ) use ( $relative_position, $inserted_block ) {
if ( 'first_child' === $relative_position ) {
array_unshift( $parsed_block['innerBlocks'], $inserted_block );
// Since WP_Block::render() iterates over `inner_content` (rather than `inner_blocks`)
// when rendering blocks, we also need to prepend a value (`null`, to mark a block
// location) to that array.
array_unshift( $parsed_block['innerContent'], null );
} elseif ( 'last_child' === $relative_position ) {
array_push( $parsed_block['innerBlocks'], $inserted_block );
// Since WP_Block::render() iterates over `inner_content` (rather than `inner_blocks`)
// when rendering blocks, we also need to prepend a value (`null`, to mark a block
// location) to that array.
array_push( $parsed_block['innerContent'], null );
}
return $parsed_block;
};
}

/**
* Return a function that auto-inserts blocks relative to a given block.
*
* @param string $relative_position The position relative to the given block.
* @param array $inserted_block The block to insert.
* @return callable A function that accepts a block's content and returns the content with the inserted block.
*/
function gutenberg_auto_insert_block( $relative_position, $inserted_block ) {
// Can we avoid infinite loops?

return function( $block_content ) use ( $relative_position, $inserted_block ) {
$inserted_content = render_block( $inserted_block );

if ( 'before' === $relative_position ) {
$block_content = $inserted_content . $block_content;
} elseif ( 'after' === $relative_position ) {
$block_content = $block_content . $inserted_content;
}
return $block_content;
};
}

function gutenberg_register_auto_inserted_blocks( $settings, $metadata ) {
if ( ! isset( $metadata['autoInsert'] ) ) {
return $settings;
}

$property_mappings = array(
'before' => 'before',
'after' => 'after',
'firstChild' => 'first_child',
'lastChild' => 'last_child',
);

$auto_insert = $metadata['autoInsert'];
foreach ( $auto_insert as $block_name => $block_data ) {
$position = $block_data['position'];
if ( ! isset( $property_mappings[ $position ] ) ) {
continue;
}

$mapped_position = $property_mappings[ $position ];

$inserted_block = array(
'blockName' => $metadata['name'],
'attrs' => $block_data['attrs'],
);
// TODO: In the long run, we'd likely want some sort of registry for auto-inserted blocks.
if ( 'before' === $mapped_position || 'after' === $mapped_position ) {
$inserter = gutenberg_auto_insert_block( $mapped_position, $inserted_block );
add_filter( "render_block_$block_name", $inserter, 10, 2 );
} elseif ( 'first_child' === $mapped_position || 'last_child' === $mapped_position ) {
$inserter = gutenberg_auto_insert_child_block( $mapped_position, $inserted_block );
add_filter( "render_block_data_$block_name", $inserter, 10, 2 );
}
$settings['auto_insert'][ $block_name ] = $mapped_position;
}

return $settings;
}
add_filter( 'block_type_metadata_settings', 'gutenberg_register_auto_inserted_blocks', 10, 2 );

function gutenberg_apply_render_block_data_block_type_filter( $parsed_block, $source_block, $parent_block ) {
$block_name = $parsed_block['blockName'];
/**
* Filters the block being rendered in render_block(), before it's processed.
*
* The dynamic portion of the hook name, `$name`, refers to
* the block name, e.g. "core/paragraph".
*
* @param array $parsed_block The block being rendered.
* @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content.
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
*/
$parsed_block = apply_filters( "render_block_data_$block_name", $parsed_block, $source_block, $parent_block );
return $parsed_block;
}
add_filter( 'render_block_data', 'gutenberg_apply_render_block_data_block_type_filter', 15, 3 );
15 changes: 14 additions & 1 deletion packages/block-library/src/avatar/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,18 @@
}
},
"editorStyle": "wp-block-avatar",
"style": "wp-block-avatar"
"style": "wp-block-avatar",
"autoInsert": {
"core/comment-template": {
"position": "lastChild",
"attrs": {
"size": 40,
"style": {
"border": {
"radius": "10px"
}
}
}
}
}
}
11 changes: 10 additions & 1 deletion packages/block-library/src/social-link/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,14 @@
"reusable": false,
"html": false
},
"editorStyle": "wp-block-social-link-editor"
"editorStyle": "wp-block-social-link-editor",
"autoInsert": {
"core/post-content": {
"position": "after",
"attrs": {
"service": "wordpress",
"url": "https://wordpress.org/"
}
}
}
}
5 changes: 5 additions & 0 deletions phpunit/blocks/render-comment-template-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ public function test_inner_block_inserted_by_render_block_data_is_retained() {
return $parsed_block;
};

// Remove auto-insertion filter so it won't collide.
remove_filter( 'render_block_data', 'gutenberg_auto_insert_child_block' );

add_filter( 'render_block_data', $render_block_data_callback, 10, 1 );
$parsed_blocks = parse_blocks(
'<!-- wp:comments --><!-- wp:comment-template --><!-- wp:comment-content /--><!-- /wp:comment-template --><!-- /wp:comments -->'
Expand All @@ -154,6 +157,8 @@ public function test_inner_block_inserted_by_render_block_data_is_retained() {
);
$block->render();
remove_filter( 'render_block_data', $render_block_data_callback );
// Add back auto-insertion filter.
add_filter( 'render_block_data', 'gutenberg_auto_insert_child_block', 10, 1 );

$this->assertSame( 5, $render_block_callback->get_call_count() );

Expand Down
26 changes: 26 additions & 0 deletions schemas/json/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,32 @@
"render": {
"type": "string",
"description": "Template file loaded on the server when rendering a block."
},
"autoInsert": {
"type": "object",
"description": "Blocks to auto-insert this block next to.",
"patternProperties": {
"[a-zA-Z]": {
"type": "object",
"description": "Position relative to the block to auto-insert this block next to.",
"properties": {
"position": {
"type": "string",
"description": "Position relative to the block to auto-insert this block next to.",
"enum": [
"before",
"after",
"firstChild",
"lastChild"
]
},
"attrs": {
"type": "object",
"description": "Attributes for the auto-inserted block."
}
}
}
}
}
},
"required": [ "name", "title" ],
Expand Down