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 API: Add pre_render and post_render block filters #10108

Closed
wants to merge 7 commits into from
Closed
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
159 changes: 154 additions & 5 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,93 @@ function get_dynamic_blocks_regex() {
*
* @since 1.9.0
* @since 4.4.0 renders full nested tree of blocks before reassembling into HTML string
* @since 4.8.0 filters blocks structurally before rendering and as text afterwards
* @global WP_Post $post The post to edit.
*
* @param array $block A single parsed block object.
* @param array $source_block A single parsed block object.
* @return string String of rendered HTML.
*/
function gutenberg_render_block( $block ) {
function gutenberg_render_block( $source_block ) {
global $post;

$global_post = $post;
/**
* Filter to process a block structurally before rendering.
*
* Use this filter if you want to modify a block's attributes or rearrange
* its inner blocks or change its block type - anything which might govern
* the way the block gets rendered in the next step.
*
* @example
* function replace_with_translation( $prev, $source_block ) {
* $block = $prev ?: $source_block;
*
* if ( ! isset( $block['attrs']['translation_id'] ) ) {
* return $prev;
* }
*
* $locale = get_current_locale();
* $translation = get_translated_block( $block['attrs']['translation_id'], $locale );
*
* return $translation ?: $prev;
* }
* add_filter( 'block_pre_render', 'replace_with_translation' );
*
* @example
* function hide_hidden_inner_blocks( $prev, $source_block ) {
* if ( 'my-plugin/hider' !== $source_block['blockName'] ) {
* return $prev;
* }
*
* $block = $prev ?: $source_block;
* $block['innerBlocks'] = array();
* $inner_content = array();
* $block_index = 0;
* $html = '';
* foreach ( $block['innerContent'] as $chunk ) {
* if ( is_string( $chunk ) ) {
* $html .= $chunk;
* continue;
* }
*
* if ( ! can_see_block( $inner ) ) {
* continue;
* }
*
* $inner = $block['innerBlocks'][$block_index++];
* $block['innerBlocks'][] = $inner;
* if ( ! empty( $html ) ) {
* $inner_content[] = $html;
* }
* $inner_content[] = null;
* }
*
* return $block;
* }
* add_filter( 'block_pre_render', 'hide_hidden_inner_blocks' );
*
* @example
* function un_markdownify_block( $prev, $source_block ) {
* $block = $prev ?: $source_block;
* return 'my-plugin/markdown' === $block['blockName']
* ? array_merge( $block, array( 'blockName' => 'core/paragraph' ) )
* : $prev;
* }
* if ( show_markdown_source() ) {
* add_filter( 'block_pre_render', 'un_markdownify_block' );
* }
*
* @since 4.8.0
*
* @param array|null $prev transformed block from previous filter or null
* @param array $source_block original block passed through all filters
*
* @return array|null transformed version of block or previous $block if not transformation is needed
*/
$pre_render = apply_filters( 'block_pre_render', null, $source_block );
$post = $global_post;
$block = isset( $pre_render ) ? $pre_render : $source_block;

$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$is_dynamic = $block['blockName'] && null !== $block_type && $block_type->is_dynamic();
$inner_content = '';
Expand All @@ -156,11 +235,81 @@ function gutenberg_render_block( $block ) {
$global_post = $post;
$output = $block_type->render( $attributes, $inner_content );
$post = $global_post;

return $output;
} else {
$output = $inner_content;
}

return $inner_content;
$global_post = $post;
/**
* Filter to process a block textually after rendering.
*
* Use this filter if you want to apply string or HTML transformations
* on a block after it and its inner blocks have already been rendered
* and into HTML. Inner blocks will have been fully rendered into the
* parent block by this point, so if you want to process the inner blocks
* themselves you should look at `block_pre_render`.
*
* This filter is particularly useful if you want to process the output
* from other blocks which might be substantially different from their
* original raw `post_content` content.
*
* @example
* function rot13_block( $output, $block ) {
* return isset( $block['attrs']['do_rot13'] )
* ? str_rot13( $output )
* : $output;
* }
* add_filter( 'block_post_render', 'rot13_block' );
*
* @example
* function add_return_to_top_link( $output ) {
* return $output . '<a class="return-to-top">Top</a>';
* }
* add_filter( 'block_post_render', 'add_return_to_top_link' );
*
* @example
* class Matcher {
* public $count = 0;
* public $pattern;
*
* function __construct( $pattern ) {
* $this->pattern = $pattern;
* }
*
* function block_post_render( $output ) {
* if ( 1 !== preg_match( $this->pattern, $output ) ) {
* return $output;
* }
*
* $this->count++;
*
* return '<div class="matched-block">' . $output . '</div>'
* }
*
* function the_content( $html ) {
* $count = $this->count;
* $this->count = 0;
*
* return $count > 0
* ? $html . '<div>Found ' . $count . ' blocks with a match!</div>'
* : $html;
* }
* }
* $matcher = new Matcher( '/new editor/i' );
* add_filter( 'block_post_render', array( $matcher, 'block_post_render' ) );
* add_filter( 'the_content', array( $matcher, 'the_content' ), 10 );
*
* @since 4.8.0
*
* @param string $output rendered HTML from block or previous filters
* @param array $block original block that was rendered
*
* @return string processed HTML to display
*/
$post_render = apply_filters( 'block_post_render', $output, $block );
$post = $global_post;

return $post_render;
}

if ( ! function_exists( 'do_blocks' ) ) {
Expand Down