Make WordPress Core

Changeset 44118


Ignore:
Timestamp:
12/13/2018 06:11:10 PM (6 years ago)
Author:
desrosj
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.

Props pento.

Merges [43752] to trunk.

See #45109.

Location:
trunk
Files:
12 edited
55 copied

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/wp-includes/blocks.php

    r44116 r44118  
    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        } elseif ( $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}
  • trunk/src/wp-includes/class-wp-block-parser.php

    r44117 r44118  
    311311                 * block and add it as a new innerBlock to the parent
    312312                 */
    313                 $stack_top                    = array_pop( $this->stack );
    314                 $stack_top->block->innerHTML .= substr( $this->document, $stack_top->prev_offset, $start_offset - $stack_top->prev_offset );
    315                 $stack_top->prev_offset       = $start_offset + $token_length;
     313                $stack_top = array_pop( $this->stack );
     314
     315                $html = substr( $this->document, $stack_top->prev_offset, $start_offset - $stack_top->prev_offset );
     316                if ( $stack_top->block->innerBlocks ) {
     317                    $stack_top->block->innerBlocks[] = (array) $this->freeform( $html );
     318                } else {
     319                    $stack_top->block->innerHTML = $html;
     320                }
     321
     322                $stack_top->prev_offset = $start_offset + $token_length;
    316323
    317324                $this->add_inner_block(
     
    446453    function add_inner_block( WP_Block_Parser_Block $block, $token_start, $token_length, $last_offset = null ) {
    447454        $parent                       = $this->stack[ count( $this->stack ) - 1 ];
    448         $parent->block->innerBlocks[] = $block;
    449         $parent->block->innerHTML    .= substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset );
     455        $parent->block->innerBlocks[] = (array) $this->freeform( substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset ) );
     456        $parent->block->innerBlocks[] = (array) $block;
    450457        $parent->prev_offset          = $last_offset ? $last_offset : $token_start + $token_length;
    451458    }
     
    462469        $prev_offset = $stack_top->prev_offset;
    463470
    464         $stack_top->block->innerHTML .= isset( $end_offset )
     471        $html = isset( $end_offset )
    465472            ? substr( $this->document, $prev_offset, $end_offset - $prev_offset )
    466473            : substr( $this->document, $prev_offset );
     474
     475        if ( $stack_top->block->innerBlocks ) {
     476            $stack_top->block->innerBlocks[] = (array) $this->freeform( $html );
     477        } else {
     478            $stack_top->block->innerHTML = $html;
     479        }
    467480
    468481        if ( isset( $stack_top->leading_html_start ) ) {
  • trunk/src/wp-includes/default-filters.php

    r44115 r44118  
    182182add_filter( 'the_title', 'trim' );
    183183
     184add_filter( 'the_content', 'do_blocks', 9 );
    184185add_filter( 'the_content', 'wptexturize' );
    185186add_filter( 'the_content', 'convert_smilies', 20 );
  • trunk/src/wp-includes/formatting.php

    r43571 r44118  
    457457    if ( trim( $pee ) === '' ) {
    458458        return '';
     459    }
     460
     461    // We don't need to autop posts with blocks in them.
     462    if ( has_blocks( $pee ) ) {
     463        return $pee;
    459464    }
    460465
     
    36363641
    36373642        $text = strip_shortcodes( $text );
     3643        $text = strip_dynamic_blocks( $text );
    36383644
    36393645        /** This filter is documented in wp-includes/post-template.php */
  • trunk/src/wp-settings.php

    r44116 r44118  
    249249require( ABSPATH . WPINC . '/class-wp-block-parser.php' );
    250250require( ABSPATH . WPINC . '/blocks.php' );
     251require( ABSPATH . WPINC . '/blocks/archives.php' );
     252require( ABSPATH . WPINC . '/blocks/block.php' );
     253require( ABSPATH . WPINC . '/blocks/categories.php' );
     254require( ABSPATH . WPINC . '/blocks/latest-comments.php' );
     255require( ABSPATH . WPINC . '/blocks/latest-posts.php' );
     256require( ABSPATH . WPINC . '/blocks/shortcode.php' );
    251257
    252258$GLOBALS['wp_embed'] = new WP_Embed();
  • trunk/tests/phpunit/data/blocks/do-blocks-expected.html

    r43743 r44118  
    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>
  • trunk/tests/phpunit/data/blocks/fixtures/core__column.parsed.json

    r43751 r44118  
    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    {
  • trunk/tests/phpunit/data/blocks/fixtures/core__columns.parsed.json

    r43751 r44118  
    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    {
  • trunk/tests/phpunit/includes/functions.php

    r43555 r44118  
    213213// Cannot modify header information - headers already sent by ...
    214214tests_add_filter( 'send_auth_cookies', '__return_false' );
     215
     216/**
     217 * After the init action has been run once, trying to re-register block types can cause
     218 * _doing_it_wrong warnings. To avoid this, unhook the block registration functions.
     219 *
     220 * @since 5.0.0
     221 */
     222function _unhook_block_registration() {
     223    remove_action( 'init', 'register_block_core_archives' );
     224    remove_action( 'init', 'register_block_core_categories' );
     225    remove_action( 'init', 'register_block_core_latest_posts' );
     226    remove_action( 'init', 'register_block_core_shortcode' );
     227}
     228tests_add_filter( 'init', '_unhook_block_registration', 1000 );
  • trunk/tests/phpunit/tests/blocks/block-parser.php

    r44116 r44118  
    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}
  • trunk/tests/phpunit/tests/blocks/render.php

    r43752 r44118  
    106106            array( $this, 'pass_parser_fixture_filenames' ),
    107107            $fixture_filenames
    108         );  }
     108        );  }
    109109
    110110    /**
     
    113113     */
    114114    public function test_do_block_output( $html_filename, $server_html_filename ) {
    115         $html_path         = self::$fixtures_dir . '/' . $html_filename;
     115        $html_path        = self::$fixtures_dir . '/' . $html_filename;
    116116        $server_html_path = self::$fixtures_dir . '/' . $server_html_filename;
    117117
     
    286286    public function render_test_block_wp_query() {
    287287        $content = '';
    288         $recent  = new WP_Query( array(
    289             'numberposts'      => 10,
    290             'orderby'          => 'ID',
    291             'order'            => 'DESC',
    292             'post_type'        => 'post',
    293             'post_status'      => 'draft, publish, future, pending, private',
    294             'suppress_filters' => true,
    295         ) );
     288        $recent  = new WP_Query(
     289            array(
     290                'numberposts'      => 10,
     291                'orderby'          => 'ID',
     292                'order'            => 'DESC',
     293                'post_type'        => 'post',
     294                'post_status'      => 'draft, publish, future, pending, private',
     295                'suppress_filters' => true,
     296            )
     297        );
    296298
    297299        while ( $recent->have_posts() ) {
  • trunk/tools/webpack/packages.js

    r44116 r44118  
    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.