Skip to content
Open
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
127 changes: 127 additions & 0 deletions src/wp-includes/block-supports/auto-register.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,130 @@ function wp_mark_auto_generate_control_attributes( array $args ): array {

// Priority 5 to mark original attributes before other filters (priority 10+) might add their own.
add_filter( 'register_block_type_args', 'wp_mark_auto_generate_control_attributes', 5 );

/**
* Configures server-side rendering for auto-registered blocks with a pattern.
*
* @since 7.1.0
* @access private
*
* @param array<string, mixed> $args Arguments passed to `register_block_type()`.
* @param string $block_name Block type name.
* @return array<string, mixed> Filtered arguments.
*/
function wp_apply_pattern_block_rendering( array $args, string $block_name ): array {
if (
empty( $args['supports']['autoRegister'] ) ||
! is_string( $args['pattern'] ?? null ) ||
'' === $args['pattern']
) {
return $args;
}

if ( ! empty( $args['render_callback'] ) ) {
_doing_it_wrong(
'register_block_type',
sprintf(
/* translators: %s: Block name. */
__( 'Block type "%s" was registered with both a pattern and a render callback. The pattern takes precedence, so the render callback is ignored.' ),
$block_name
),
'7.1.0'
);
}

// Pattern overrides use `content`, so replace any existing schema.
$args['attributes']['content'] = array( 'type' => 'object' );
if ( ! isset( $args['provides_context'] ) ) {
$args['provides_context'] = array();
}
$args['provides_context']['pattern/overrides'] = 'content';

// Disable HTML editing by default because the pattern is not saved with the block.
if ( ! isset( $args['supports']['html'] ) ) {
$args['supports']['html'] = false;
}

// Ignore inner blocks from saved content because only the registered pattern should render.
$args['skip_inner_blocks'] = true;
$args['render_callback'] = static function ( $attributes, $content, $block ) {
// A pattern can contain another instance of the same block. Render that nested
// instance as empty, matching `core/block`, to avoid infinite recursion.
static $rendering = array();
if ( isset( $rendering[ $block->name ] ) ) {
return '';
}
$rendering[ $block->name ] = true;

// Read the current pattern at render time, then rebuild its children so they
// inherit the host block's override context.
$pattern = $block->block_type->pattern;
if ( ! is_string( $pattern ) || '' === $pattern ) {
$pattern = '';
}

// `WP_Embed` processes embeds on `the_content` before `do_blocks()` runs,
// so the pattern, injected during `do_blocks()`, never goes through those
// filters and its embed URLs would render as plain links.
global $wp_embed;
$pattern = $wp_embed->run_shortcode( $pattern );
$pattern = $wp_embed->autoembed( $pattern );

$block->parsed_block['innerBlocks'] = parse_blocks( $pattern );
$block->parsed_block['innerContent'] = array_fill(
0,
count( $block->parsed_block['innerBlocks'] ),
null
);
// `refresh_context_dependents()` does not clear existing inner blocks
// when the new parsed list is empty.
$block->inner_blocks = array();
$block->refresh_context_dependents();

// Apply the same child filters as `WP_Block::render()` without rendering the
// host block or applying its `render_block` filters a second time.
$output = '';
foreach ( $block->inner_blocks as $inner_block ) {
/** This filter is documented in wp-includes/blocks.php */
$pre_render = apply_filters( 'pre_render_block', null, $inner_block->parsed_block, $block );
if ( null !== $pre_render ) {
$output .= $pre_render;
continue;
}

$source_block = $inner_block->parsed_block;
$inner_block_context = $inner_block->context;

/** This filter is documented in wp-includes/blocks.php */
$inner_block->parsed_block = apply_filters(
'render_block_data',
$inner_block->parsed_block,
$source_block,
$block
);

/** This filter is documented in wp-includes/blocks.php */
$inner_block->context = apply_filters(
'render_block_context',
$inner_block->context,
$inner_block->parsed_block,
$block
);

if ( $inner_block->context !== $inner_block_context ) {
$inner_block->refresh_context_dependents();
} elseif ( $inner_block->parsed_block !== $source_block ) {
$inner_block->refresh_parsed_block_dependents();
}

$output .= $inner_block->render();
}

unset( $rendering[ $block->name ] );

return sprintf( '<div %1$s>%2$s</div>', get_block_wrapper_attributes(), $output );
};

return $args;
}
add_filter( 'register_block_type_args', 'wp_apply_pattern_block_rendering', 10, 2 );
28 changes: 23 additions & 5 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -3183,22 +3183,32 @@ function _wp_footnotes_force_filtered_html_on_import_filter( $arg ) {
}

/**
* Exposes blocks with autoRegister flag for ServerSideRender in the editor.
* Exposes auto-registered blocks to the editor.
*
* Detects blocks that have the autoRegister flag set in their supports
* and passes them to JavaScript for auto-registration with ServerSideRender.
* Passes the names of blocks with render callbacks and the markup for blocks
* registered with patterns to JavaScript.
*
* @access private
* @since 7.0.0
* @since 7.1.0 Added pattern markup to the auto-registration data.
*/
function _wp_enqueue_auto_register_blocks() {
$auto_register_blocks = array();
$registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();
$auto_register_blocks = array();
$auto_register_block_patterns = array();
$registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();

foreach ( $registered_blocks as $block_name => $block_type ) {
if ( ! empty( $block_type->supports['autoRegister'] ) && ! empty( $block_type->render_callback ) ) {
$auto_register_blocks[] = $block_name;
}

if (
! empty( $block_type->supports['autoRegister'] ) &&
is_string( $block_type->pattern ) &&
'' !== $block_type->pattern
) {
$auto_register_block_patterns[ $block_name ] = $block_type->pattern;
}
}

if ( ! empty( $auto_register_blocks ) ) {
Expand All @@ -3208,4 +3218,12 @@ function _wp_enqueue_auto_register_blocks() {
'before'
);
}

if ( ! empty( $auto_register_block_patterns ) ) {
wp_add_inline_script(
'wp-block-library',
sprintf( 'window.__unstableAutoRegisterBlockPatterns = %s;', wp_json_encode( $auto_register_block_patterns ) ),
'before'
);
}
}
10 changes: 10 additions & 0 deletions src/wp-includes/class-wp-block-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ class WP_Block_Type {
*/
public $render_callback = null;

/**
* Serialized block pattern used to render the block.
*
* @since 7.1.0
* @var string|null
*/
public $pattern = null;

/**
* Block type attributes property schemas.
*
Expand Down Expand Up @@ -303,6 +311,7 @@ class WP_Block_Type {
* @since 6.3.0 Added the `selectors` property.
* @since 6.4.0 Added the `block_hooks` property.
* @since 6.5.0 Added the `allowed_blocks`, `variation_callback`, and `view_style_handles` properties.
* @since 7.1.0 Added the `pattern` property.
*
* @see register_block_type()
*
Expand Down Expand Up @@ -331,6 +340,7 @@ class WP_Block_Type {
* @type array|null $supports Supported features.
* @type array|null $example Structured data for the block preview.
* @type callable|null $render_callback Block type render callback.
* @type string|null $pattern Serialized block pattern used to render the block.
* @type callable|null $variation_callback Block type variations callback.
* @type array|null $attributes Block type attributes property schemas.
* @type string[] $uses_context Context values inherited by blocks of this type.
Expand Down
Loading
Loading