WordPress.org

Make WordPress Core

Changeset 43752


Ignore:
Timestamp:
10/18/2018 11:53:49 AM (14 months ago)
Author:
pento
Message:

Blocks: Parse blocks when displaying posts.

Posts containing blocks are now correctly handled when displaying on the front end, including dynamic blocks and nested blocks.

See #45109.

Location:
branches/5.0
Files:
61 added
11 edited

Legend:

Unmodified
Added
Removed
  • branches/5.0/src/wp-includes/blocks.php

    r43751 r43752  
    112112
    113113    return $dynamic_block_names;
     114}
     115
     116/**
     117 * Remove all dynamic blocks from the given content.
     118 *
     119 * @since 5.0.0
     120 *
     121 * @param string $content Content of the current post.
     122 * @return string
     123 */
     124function strip_dynamic_blocks( $content ) {
     125    return _recurse_strip_dynamic_blocks( parse_blocks( $content ) );
     126}
     127
     128/**
     129 * Helper function for strip_dynamic_blocks(), to recurse through the block tree.
     130 *
     131 * @since 5.0.0
     132 * @access private
     133 *
     134 * @param array $blocks Array of blocks from parse_blocks().
     135 * @return string HTML from the non-dynamic blocks.
     136 */
     137function _recurse_strip_dynamic_blocks( $blocks ) {
     138    $clean_content  = '';
     139    $dynamic_blocks = get_dynamic_block_names();
     140
     141    foreach ( $blocks as $block ) {
     142        if ( ! in_array( $block['blockName'], $dynamic_blocks ) ) {
     143            if ( $block['innerBlocks'] ) {
     144                $clean_content .= _recurse_strip_dynamic_blocks( $block['innerBlocks'] );
     145            } else {
     146                $clean_content .= $block['innerHTML'];
     147            }
     148        }
     149    }
     150
     151    return $clean_content;
    114152}
    115153
     
    143181     * @since 5.0.0
    144182     *
    145      * @param string $parser_class Name of block parser class
     183     * @param string $parser_class Name of block parser class.
    146184     */
    147185    $parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' );
     
    150188    return $parser->parse( $content );
    151189}
     190
     191/**
     192 * Parses dynamic blocks out of `post_content` and re-renders them.
     193 *
     194 * @since 5.0.0
     195 * @global WP_Post $post The post to edit.
     196 *
     197 * @param  string $content Post content.
     198 * @return string Updated post content.
     199 */
     200function do_blocks( $content ) {
     201    $blocks = parse_blocks( $content );
     202    return _recurse_do_blocks( $blocks, $blocks );
     203}
     204
     205/**
     206 * Helper function for do_blocks(), to recurse through the block tree.
     207 *
     208 * @since 5.0.0
     209 * @access private
     210 *
     211 * @param array $blocks     Array of blocks from parse_blocks().
     212 * @param array $all_blocks The top level array of blocks.
     213 * @return string The block HTML.
     214 */
     215function _recurse_do_blocks( $blocks, $all_blocks ) {
     216    global $post;
     217
     218    /*
     219     * Back up global post, to restore after render callback.
     220     * Allows callbacks to run new WP_Query instances without breaking the global post.
     221     */
     222    $global_post = $post;
     223
     224    $rendered_content = '';
     225    $dynamic_blocks   = get_dynamic_block_names();
     226
     227    foreach ( $blocks as $block ) {
     228        $block = (array) $block;
     229        if ( in_array( $block['blockName'], $dynamic_blocks ) ) {
     230            // Find registered block type. We can assume it exists since we use the
     231            // `get_dynamic_block_names` function as a source for pattern matching.
     232            $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
     233
     234            // Replace dynamic block with server-rendered output.
     235            $block_content = $block_type->render( (array) $block['attrs'], $block['innerHTML'] );
     236        } else if ( $block['innerBlocks'] ) {
     237            $block_content = _recurse_do_blocks( $block['innerBlocks'], $blocks );
     238        } else {
     239            $block_content = $block['innerHTML'];
     240        }
     241
     242        /**
     243         * Filters the content of a single block.
     244         *
     245         * During the_content, each block is parsed and added to the output individually. This filter allows
     246         * that content to be altered immediately before it's appended.
     247         *
     248         * @since 5.0.0
     249         *
     250         * @param string $block_content The block content about to be appended.
     251         * @param array  $block         The full block, including name and attributes.
     252         * @param array  $all_blocks    The array of all blocks being processed.
     253         */
     254        $rendered_content .= apply_filters( 'do_block', $block_content, $block, $all_blocks );
     255
     256        // Restore global $post.
     257        $post = $global_post;
     258    }
     259
     260    // Strip remaining block comment demarcations.
     261    $rendered_content = preg_replace( '/<!--\s+\/?wp:.*?-->/m', '', $rendered_content );
     262
     263    return $rendered_content;
     264}
  • branches/5.0/src/wp-includes/class-wp-block-parser.php

    r43751 r43752  
    307307                 */
    308308                $stack_top = array_pop( $this->stack );
    309                 $stack_top->block->innerHTML .= substr( $this->document, $stack_top->prev_offset, $start_offset - $stack_top->prev_offset );
     309
     310                $html = substr( $this->document, $stack_top->prev_offset, $start_offset - $stack_top->prev_offset );
     311                if ( $stack_top->block->innerBlocks ) {
     312                    $stack_top->block->innerBlocks[] = (array) $this->freeform( $html );
     313                } else {
     314                    $stack_top->block->innerHTML = $html;
     315                }
     316
    310317                $stack_top->prev_offset = $start_offset + $token_length;
    311318
     
    441448    function add_inner_block( WP_Block_Parser_Block $block, $token_start, $token_length, $last_offset = null ) {
    442449        $parent = $this->stack[ count( $this->stack ) - 1 ];
    443         $parent->block->innerBlocks[] = $block;
    444         $parent->block->innerHTML .= substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset );
     450        $parent->block->innerBlocks[] = (array) $this->freeform( substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset ) );
     451        $parent->block->innerBlocks[] = (array) $block;
    445452        $parent->prev_offset = $last_offset ? $last_offset : $token_start + $token_length;
    446453    }
     
    457464        $prev_offset = $stack_top->prev_offset;
    458465
    459         $stack_top->block->innerHTML .= isset( $end_offset )
     466        $html = isset( $end_offset )
    460467            ? substr( $this->document, $prev_offset, $end_offset - $prev_offset )
    461468            : substr( $this->document, $prev_offset );
     469
     470        if ( $stack_top->block->innerBlocks ) {
     471            $stack_top->block->innerBlocks[] = (array) $this->freeform( $html );
     472        } else {
     473            $stack_top->block->innerHTML = $html;
     474        }
    462475
    463476        if ( isset( $stack_top->leading_html_start ) ) {
  • branches/5.0/src/wp-includes/default-filters.php

    r43738 r43752  
    172172add_filter( 'the_title', 'trim'          );
    173173
     174add_filter( 'the_content', 'do_blocks',                      9 );
    174175add_filter( 'the_content', 'wptexturize'                       );
    175176add_filter( 'the_content', 'convert_smilies',               20 );
  • branches/5.0/src/wp-includes/formatting.php

    r43732 r43752  
    442442    if ( trim($pee) === '' )
    443443        return '';
     444
     445    // We don't need to autop posts with blocks in them.
     446    if ( has_blocks( $pee ) ) {
     447        return $pee;
     448    }
    444449
    445450    // Just to make things a little easier, pad the end.
     
    33723377
    33733378        $text = strip_shortcodes( $text );
     3379        $text = strip_dynamic_blocks( $text );
    33743380
    33753381        /** This filter is documented in wp-includes/post-template.php */
  • branches/5.0/src/wp-settings.php

    r43751 r43752  
    248248require( ABSPATH . WPINC . '/class-wp-block-parser.php' );
    249249require( ABSPATH . WPINC . '/blocks.php' );
     250require( ABSPATH . WPINC . '/blocks/archives.php' );
     251require( ABSPATH . WPINC . '/blocks/block.php' );
     252require( ABSPATH . WPINC . '/blocks/categories.php' );
     253require( ABSPATH . WPINC . '/blocks/latest-comments.php' );
     254require( ABSPATH . WPINC . '/blocks/latest-posts.php' );
     255require( ABSPATH . WPINC . '/blocks/shortcode.php' );
    250256
    251257$GLOBALS['wp_embed'] = new WP_Embed();
  • branches/5.0/tests/phpunit/data/blocks/do-blocks-expected.html

    r43743 r43752  
    33<!--more-->
    44
     5
    56<p>First Gutenberg Paragraph</p>
     7
    68
    79<p>Second Auto Paragraph</p>
    810
    911
     12
     13
    1014<p>Third Gutenberg Paragraph</p>
     15
    1116
    1217<p>Third Auto Paragraph</p>
  • branches/5.0/tests/phpunit/data/blocks/fixtures/core__column.parsed.json

    r43751 r43752  
    44        "attrs": {},
    55        "innerBlocks": [
     6            {
     7                "blockName": null,
     8                "attrs": {},
     9                "innerBlocks": [],
     10                "innerHTML": "\n<div class=\"wp-block-column\">\n\t"
     11            },
    612            {
    713                "blockName": "core/paragraph",
     
    1016                "innerHTML": "\n\t<p>Column One, Paragraph One</p>\n\t"
    1117            },
     18            {
     19                "blockName": null,
     20                "attrs": {},
     21                "innerBlocks": [],
     22                "innerHTML": "\n\t"
     23            },
    1224            {
    1325                "blockName": "core/paragraph",
     
    1527                "innerBlocks": [],
    1628                "innerHTML": "\n\t<p>Column One, Paragraph Two</p>\n\t"
    17             }
    18         ],
    19         "innerHTML": "\n<div class=\"wp-block-column\">\n\t\n\t\n</div>\n"
     29            },
     30            {
     31                "blockName": null,
     32                "attrs": {},
     33                "innerBlocks": [],
     34                "innerHTML": "\n</div>\n"
     35            }
     36        ],
     37        "innerHTML": ""
    2038    },
    2139    {
  • branches/5.0/tests/phpunit/data/blocks/fixtures/core__columns.parsed.json

    r43751 r43752  
    66        },
    77        "innerBlocks": [
     8            {
     9                "blockName": null,
     10                "attrs": {},
     11                "innerBlocks": [],
     12                "innerHTML": "\n<div class=\"wp-block-columns has-3-columns\">\n\t"
     13            },
    814            {
    915                "blockName": "core/column",
    1016                "attrs": {},
    1117                "innerBlocks": [
    12                     {
     18                    {
     19                        "blockName": null,
     20                        "attrs": {},
     21                        "innerBlocks": [],
     22                        "innerHTML": "\n\t<div class=\"wp-block-column\">\n\t\t"
     23                    },
     24                    {
    1325                        "blockName": "core/paragraph",
    1426                        "attrs": {},
     
    1628                        "innerHTML": "\n\t\t<p>Column One, Paragraph One</p>\n\t\t"
    1729                    },
    18                     {
     30                    {
     31                        "blockName": null,
     32                        "attrs": {},
     33                        "innerBlocks": [],
     34                        "innerHTML": "\n\t\t"
     35                    },
     36                    {
    1937                        "blockName": "core/paragraph",
    2038                        "attrs": {},
    2139                        "innerBlocks": [],
    2240                        "innerHTML": "\n\t\t<p>Column One, Paragraph Two</p>\n\t\t"
    23                     }
     41                    },
     42                    {
     43                        "blockName": null,
     44                        "attrs": {},
     45                        "innerBlocks": [],
     46                        "innerHTML": "\n\t</div>\n\t"
     47                    }
    2448                ],
    25                 "innerHTML": "\n\t<div class=\"wp-block-column\">\n\t\t\n\t\t\n\t</div>\n\t"
     49                "innerHTML": ""
    2650            },
     51            {
     52                "blockName": null,
     53                "attrs": {},
     54                "innerBlocks": [],
     55                "innerHTML": "\n\t"
     56            },
    2757            {
    2858                "blockName": "core/column",
    2959                "attrs": {},
    30                 "innerBlocks": [
    31                     {
     60                "innerBlocks": [
     61                    {
     62                        "blockName": null,
     63                        "attrs": {},
     64                        "innerBlocks": [],
     65                        "innerHTML": "\n\t<div class=\"wp-block-column\">\n\t\t"
     66                    },
     67                    {
    3268                        "blockName": "core/paragraph",
    3369                        "attrs": {},
     
    3571                        "innerHTML": "\n\t\t<p>Column Two, Paragraph One</p>\n\t\t"
    3672                    },
    37                     {
     73                    {
     74                        "blockName": null,
     75                        "attrs": {},
     76                        "innerBlocks": [],
     77                        "innerHTML": "\n\t\t"
     78                    },
     79                    {
    3880                        "blockName": "core/paragraph",
    3981                        "attrs": {},
    4082                        "innerBlocks": [],
    4183                        "innerHTML": "\n\t\t<p>Column Three, Paragraph One</p>\n\t\t"
    42                     }
     84                    },
     85                    {
     86                        "blockName": null,
     87                        "attrs": {},
     88                        "innerBlocks": [],
     89                        "innerHTML": "\n\t</div>\n\t"
     90                    }
    4391                ],
    44                 "innerHTML": "\n\t<div class=\"wp-block-column\">\n\t\t\n\t\t\n\t</div>\n\t"
    45             }
     92                "innerHTML": ""
     93            },
     94            {
     95                "blockName": null,
     96                "attrs": {},
     97                "innerBlocks": [],
     98                "innerHTML": "\n</div>\n"
     99            }
    46100        ],
    47         "innerHTML": "\n<div class=\"wp-block-columns has-3-columns\">\n\t\n\t\n</div>\n"
     101        "innerHTML": ""
    48102    },
    49103    {
  • branches/5.0/tests/phpunit/includes/functions.php

    r41966 r43752  
    183183// Cannot modify header information - headers already sent by ...
    184184tests_add_filter( 'send_auth_cookies', '__return_false' );
     185
     186/**
     187 * After the init action has been run once, trying to re-register block types can cause
     188 * _doing_it_wrong warnings. To avoid this, unhook the block registration functions.
     189 *
     190 * @since 5.0.0
     191 */
     192function _unhook_block_registration() {
     193    remove_action( 'init', 'register_block_core_archives' );
     194    remove_action( 'init', 'register_block_core_categories' );
     195    remove_action( 'init', 'register_block_core_latest_posts' );
     196    remove_action( 'init', 'register_block_core_shortcode' );
     197}
     198tests_add_filter( 'init', '_unhook_block_registration', 1000 );
  • branches/5.0/tests/phpunit/tests/blocks/block-parser.php

    r43751 r43752  
    6060        foreach ( array( $html_path, $parsed_json_path ) as $filename ) {
    6161            if ( ! file_exists( $filename ) ) {
    62                 //throw new Exception( "Missing fixture file: '$filename'" );
     62                throw new Exception( "Missing fixture file: '$filename'" );
    6363            }
    6464        }
     
    117117        return str_replace( "\r", '', $input );
    118118    }
    119 
    120119}
  • branches/5.0/tools/webpack/packages.js

    r43751 r43752  
    118118
    119119    const phpFiles = {
    120         'block-serialization-default-parser/parser.php': 'wp-includes/class-wp-block-parser.php',
     120        // Parser shouldn't be copied until nested block issues are resolved.
     121        // 'block-serialization-default-parser/parser.php': 'wp-includes/class-wp-block-parser.php',
     122        'block-library/src/archives/index.php': 'wp-includes/blocks/archives.php',
     123        'block-library/src/block/index.php': 'wp-includes/blocks/block.php',
     124        'block-library/src/categories/index.php': 'wp-includes/blocks/categories.php',
     125        'block-library/src/latest-comments/index.php': 'wp-includes/blocks/latest-comments.php',
     126        'block-library/src/latest-posts/index.php': 'wp-includes/blocks/latest-posts.php',
     127        'block-library/src/shortcode/index.php': 'wp-includes/blocks/shortcode.php',
    121128    };
    122129
Note: See TracChangeset for help on using the changeset viewer.