From d1f22bd4d43fdb78d86e0d03616c8f69ab771ee3 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Mon, 25 Jan 2021 10:43:06 +0100 Subject: [PATCH] Template Part: Prevent infinite recursion Based on #28405 from @mcsf. Tries to apply the same technique used for Reusable block to prevent infinite recursion when the same block is inserted into itself at any level of nesting. --- .../src/template-part/edit/index.js | 23 +++++++++++++--- .../block-library/src/template-part/index.php | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index da36745fd4b3ce..35819cc3752060 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -6,6 +6,7 @@ import { BlockControls, InspectorAdvancedControls, useBlockProps, + __experimentalUseNoRecursiveRenders as useNoRecursiveRenders, Warning, } from '@wordpress/block-editor'; import { @@ -33,6 +34,10 @@ export default function TemplatePartEdit( { } ) { const templatePartId = theme && slug ? theme + '//' + slug : null; + const [ hasAlreadyRendered, RecursionProvider ] = useNoRecursiveRenders( + templatePartId + ); + // Set the postId block attribute if it did not exist, // but wait until the inner blocks have loaded to allow // new edits to trigger this. @@ -63,9 +68,18 @@ export default function TemplatePartEdit( { ); const blockProps = useBlockProps(); - const isPlaceholder = ! slug; - const isEntityAvailable = ! isPlaceholder && ! isMissing; + if ( hasAlreadyRendered ) { + return ( + + + { __( 'Block cannot be rendered inside itself.' ) } + + + ); + } + + const isPlaceholder = ! slug; if ( ! isPlaceholder && isMissing ) { return ( @@ -78,6 +92,7 @@ export default function TemplatePartEdit( { ); } + const isEntityAvailable = ! isPlaceholder && ! isMissing; const inspectorAdvancedControls = ( + { inspectorAdvancedControls } { isPlaceholder && ( @@ -146,6 +161,6 @@ export default function TemplatePartEdit( { ) } { ! isPlaceholder && ! isResolved && } - + ); } diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index a960aa969bc906..c999b931e21b42 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -13,6 +13,8 @@ * @return string The render. */ function render_block_core_template_part( $attributes ) { + static $seen_content = array(); + $content = null; if ( ! empty( $attributes['postId'] ) && get_post_status( $attributes['postId'] ) ) { @@ -55,6 +57,31 @@ function render_block_core_template_part( $attributes ) { return 'Template Part Not Found'; } + if ( in_array( $content, $seen_content, true ) ) { + if ( ! is_admin() ) { + trigger_error( + sprintf( + // translators: %s is the user-provided title of the reusable block. + __( 'Could not render Template Part with the slug %s: blocks cannot be rendered inside themselves.' ), + $attributes['slug'] + ), + E_USER_WARNING + ); + } + + // WP_DEBUG_DISPLAY must only be honored when WP_DEBUG. This precedent + // is set in `wp_debug_mode()`. + $is_debug = defined( 'WP_DEBUG' ) && WP_DEBUG && + defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY; + + return $is_debug ? + // translators: Visible only in the front end, this warning takes the place of a faulty block. + __( '[block rendering halted]' ) : + ''; + } + + $seen_content[] = $content; + // Run through the actions that are typically taken on the_content. $content = do_blocks( $content ); $content = wptexturize( $content );