From 7a55d997863968cc16e0eb162c990e482ea2ebab Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Fri, 20 Jul 2018 04:51:55 -0700 Subject: [PATCH 1/5] Run `wp.oldEditor.removep()` when transforming shortcodes Multi-line shortcodes transformed to Shortcode Blocks end up with `

` in awkward places. Thanks to @pento's quick thinking, we can use some existing WordPress magic to solve the immediate problem without falling into the deeper rabbit hole. --- core-blocks/shortcode/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-blocks/shortcode/index.js b/core-blocks/shortcode/index.js index 3f8d0b049fc7dc..829f34d48953e6 100644 --- a/core-blocks/shortcode/index.js +++ b/core-blocks/shortcode/index.js @@ -46,7 +46,7 @@ export const settings = { text: { type: 'string', shortcode: ( attrs, { content } ) => { - return content; + return wp.oldEditor.removep( wp.oldEditor.autop( content ) ); }, }, }, From 70790bd78f3875d94900bdef9e72dbb4a79d58db Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Fri, 20 Jul 2018 06:02:14 -0700 Subject: [PATCH 2/5] Avoid dependency on `wp` global --- core-blocks/shortcode/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core-blocks/shortcode/index.js b/core-blocks/shortcode/index.js index 829f34d48953e6..c3350733e6c33e 100644 --- a/core-blocks/shortcode/index.js +++ b/core-blocks/shortcode/index.js @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { removep, autop } from '@wordpress/autop'; import { RawHTML } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Dashicon } from '@wordpress/components'; @@ -46,7 +47,7 @@ export const settings = { text: { type: 'string', shortcode: ( attrs, { content } ) => { - return wp.oldEditor.removep( wp.oldEditor.autop( content ) ); + return removep( autop( content ) ); }, }, }, From b9429e95794233eb1c12dcb43068f90984492979 Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Wed, 25 Jul 2018 16:53:54 +1000 Subject: [PATCH 3/5] Run removep() on serialised content when we're only saving a single freeform block. --- editor/store/selectors.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/editor/store/selectors.js b/editor/store/selectors.js index 16ade0c8ca4fc8..222233e2252305 100644 --- a/editor/store/selectors.js +++ b/editor/store/selectors.js @@ -22,10 +22,11 @@ import createSelector from 'rememo'; /** * WordPress dependencies */ -import { serialize, getBlockType, getBlockTypes, hasBlockSupport, hasChildBlocks } from '@wordpress/blocks'; +import { serialize, getBlockType, getBlockTypes, hasBlockSupport, hasChildBlocks, getUnknownTypeHandlerName } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { moment } from '@wordpress/date'; import deprecated from '@wordpress/deprecated'; +import { removep } from '@wordpress/autop'; /*** * Module constants @@ -1352,7 +1353,13 @@ export const getEditedPostContent = createSelector( return edits.content; } - return serialize( getBlocks( state ) ); + const blocks = getBlocks( state ); + + if ( blocks.length === 1 && blocks[ 0 ].name === getUnknownTypeHandlerName() ) { + return removep( serialize( blocks ) ); + } + + return serialize( blocks ); }, ( state ) => [ state.editor.present.edits.content, From 7deaecd389912f269d5fbb4b6ebb31150cdb7426 Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Thu, 26 Jul 2018 17:27:32 +1000 Subject: [PATCH 4/5] Pass the block content to dynamic block render functions. --- docs/blocks/creating-dynamic-blocks.md | 4 ++-- lib/blocks.php | 9 ++++++--- lib/class-wp-block-type.php | 7 ++++--- phpunit/class-block-type-test.php | 17 +++++++++++++++++ phpunit/fixtures/do-blocks-expected.html | 5 +++++ phpunit/fixtures/do-blocks-original.html | 8 ++++++++ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/docs/blocks/creating-dynamic-blocks.md b/docs/blocks/creating-dynamic-blocks.md index c6e2db60e9be0d..b40755e3f9883c 100644 --- a/docs/blocks/creating-dynamic-blocks.md +++ b/docs/blocks/creating-dynamic-blocks.md @@ -89,7 +89,7 @@ Because it is a dynamic block it also needs a server component. The rendering ca 1, 'post_status' => 'publish', @@ -115,7 +115,7 @@ There are a few things to notice: * The edit function still shows a representation of the block in the editor's context (this could be very different from the rendered version, it's up to the block's author) * The save function just returns null because the rendering is performed server-side. -* The server-side rendering is a function taking the block attributes as an argument and returning the markup (quite similar to shortcodes) +* The server-side rendering is a function taking the block attributes and the block inner content as arguments, and returning the markup (quite similar to shortcodes) ## Live rendering in Gutenberg editor diff --git a/lib/blocks.php b/lib/blocks.php index e28971bad032c2..f00a11dd93b48b 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -177,8 +177,7 @@ function do_blocks( $content ) { } } - // Replace dynamic block with server-rendered output. - $rendered_content .= $block_type->render( $attributes ); + $inner_content = ''; if ( ! $is_self_closing ) { $end_tag_pattern = '//'; @@ -192,8 +191,12 @@ function do_blocks( $content ) { $end_tag = $block_match_end[0][0]; $end_offset = $block_match_end[0][1]; - $content = substr( $content, $end_offset + strlen( $end_tag ) ); + $inner_content = substr( $content, 0, $end_offset ); + $content = substr( $content, $end_offset + strlen( $end_tag ) ); } + + // Replace dynamic block with server-rendered output. + $rendered_content .= $block_type->render( $attributes, $inner_content ); } // Append remaining unmatched content. diff --git a/lib/class-wp-block-type.php b/lib/class-wp-block-type.php index bc7f2e55cb6749..633388d112fb8f 100644 --- a/lib/class-wp-block-type.php +++ b/lib/class-wp-block-type.php @@ -94,17 +94,18 @@ public function __construct( $block_type, $args = array() ) { * * @since 0.6.0 * - * @param array $attributes Optional. Block attributes. Default empty array. + * @param array $attributes Optional. Block attributes. Default empty array. + * @param string $content Optional. Block content. Default empty string. * @return string Rendered block type output. */ - public function render( $attributes = array() ) { + public function render( $attributes = array(), $content = '' ) { if ( ! $this->is_dynamic() ) { return ''; } $attributes = $this->prepare_attributes_for_render( $attributes ); - return (string) call_user_func( $this->render_callback, $attributes ); + return (string) call_user_func( $this->render_callback, $attributes, $content ); } /** diff --git a/phpunit/class-block-type-test.php b/phpunit/class-block-type-test.php index 8a08cb112dcf28..f9e5f032ec36c5 100644 --- a/phpunit/class-block-type-test.php +++ b/phpunit/class-block-type-test.php @@ -36,6 +36,23 @@ function test_render() { $this->assertEquals( $attributes, json_decode( $output, true ) ); } + function test_render_with_content() { + $attributes = array( + 'foo' => 'bar', + 'bar' => 'foo', + ); + + $content = 'baz'; + + $expected = array_merge( $attributes, array( '_content' => $content ) ); + + $block_type = new WP_Block_Type( 'core/dummy', array( + 'render_callback' => array( $this, 'render_dummy_block_with_content' ), + ) ); + $output = $block_type->render( $attributes, $content ); + $this->assertEquals( $expected, json_decode( $output, true ) ); + } + function test_render_for_static_block() { $block_type = new WP_Block_Type( 'core/dummy', array() ); $output = $block_type->render(); diff --git a/phpunit/fixtures/do-blocks-expected.html b/phpunit/fixtures/do-blocks-expected.html index 89781ba3d31874..4a3dc379ef48f9 100644 --- a/phpunit/fixtures/do-blocks-expected.html +++ b/phpunit/fixtures/do-blocks-expected.html @@ -10,3 +10,8 @@

Third Gutenberg Paragraph

Third Auto Paragraph

+ +

[someshortcode]

+

And some content?!

+

[/someshortcode]

+ diff --git a/phpunit/fixtures/do-blocks-original.html b/phpunit/fixtures/do-blocks-original.html index 3707e7a857910d..bbd2eb4bb1457d 100644 --- a/phpunit/fixtures/do-blocks-original.html +++ b/phpunit/fixtures/do-blocks-original.html @@ -15,3 +15,11 @@

Third Auto Paragraph

+ + +[someshortcode] + +And some content?! + +[/someshortcode] + From e75c8ce1f4309cde0b7043d2f13ad0d951ff8d10 Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Thu, 26 Jul 2018 17:28:04 +1000 Subject: [PATCH 5/5] Register core/shortcode as a dynamic block, so it can be autop-ed correctly. --- core-blocks/shortcode/index.php | 29 +++++++++++++++++++++++++++++ phpunit/class-do-blocks-test.php | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 core-blocks/shortcode/index.php diff --git a/core-blocks/shortcode/index.php b/core-blocks/shortcode/index.php new file mode 100644 index 00000000000000..3635c050805397 --- /dev/null +++ b/core-blocks/shortcode/index.php @@ -0,0 +1,29 @@ + 'render_block_core_shortcode', + ) ); +} + +add_action( 'init', 'register_block_core_shortcode' ); diff --git a/phpunit/class-do-blocks-test.php b/phpunit/class-do-blocks-test.php index ec7cd97c39bbdf..9da7ddc0fcb27d 100644 --- a/phpunit/class-do-blocks-test.php +++ b/phpunit/class-do-blocks-test.php @@ -22,4 +22,26 @@ function test_do_blocks_removes_comments() { $this->assertEquals( $expected_html, $actual_html ); } + + /** + * Test that shortcode blocks get the same HTML as shortcodes in Classic content. + */ + function test_the_content() { + add_shortcode( 'someshortcode', array( $this, 'handle_shortcode' ) ); + + $classic_content = "Foo\n\n[someshortcode]\n\nBar\n\n[/someshortcode]\n\nBaz"; + $block_content = "\n

Foo

\n\n\n[someshortcode]\n\nBar\n\n[/someshortcode]\n\n\n

Baz

\n"; + + $classic_filtered_content = apply_filters( 'the_content', $classic_content ); + $block_filtered_content = apply_filters( 'the_content', $block_content ); + + // Block rendering add some extra blank lines, but we're not worried about them. + $block_filtered_content = preg_replace( "/\n{2,}/", "\n", $block_filtered_content ); + + $this->assertEquals( $classic_filtered_content, $block_filtered_content ); + } + + function handle_shortcode( $atts, $content ) { + return $content; + } }