diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 6e7507b0a333c7..263e92f570dee9 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -5,6 +5,7 @@ import { useSelect } from '@wordpress/data'; import { BlockControls, useBlockProps, + __experimentalUseNoRecursiveRenders as useNoRecursiveRenders, Warning, store as blockEditorStore, } from '@wordpress/block-editor'; @@ -34,6 +35,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. @@ -70,8 +75,7 @@ export default function TemplatePartEdit( { ); const blockProps = useBlockProps(); - const isPlaceholder = ! slug; - const isEntityAvailable = ! isPlaceholder && ! isMissing; + const isEntityAvailable = slug && ! isMissing; const TagName = tagName || getTagBasedOnArea( area ); // We don't want to render a missing state if we have any inner blocks. @@ -91,8 +95,18 @@ export default function TemplatePartEdit( { ); } + if ( hasAlreadyRendered ) { + return ( + + + { __( 'Block cannot be rendered inside itself.' ) } + + + ); + } + return ( - <> + } - + ); } diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index ff88b452587b0b..229a3c0ebfa7bb 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; $area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; @@ -60,6 +62,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 );