Make WordPress Core

Changeset 44118


Ignore:
Timestamp:
12/13/2018 06:11:10 PM (8 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.