Skip to content

Commit

Permalink
Template Part: Prevent infinite recursion
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gziolo committed Jan 25, 2021
1 parent 956ba00 commit d1f22bd
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
23 changes: 19 additions & 4 deletions packages/block-library/src/template-part/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BlockControls,
InspectorAdvancedControls,
useBlockProps,
__experimentalUseNoRecursiveRenders as useNoRecursiveRenders,
Warning,
} from '@wordpress/block-editor';
import {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -63,9 +68,18 @@ export default function TemplatePartEdit( {
);

const blockProps = useBlockProps();
const isPlaceholder = ! slug;
const isEntityAvailable = ! isPlaceholder && ! isMissing;

if ( hasAlreadyRendered ) {
return (
<TagName { ...blockProps }>
<Warning>
{ __( 'Block cannot be rendered inside itself.' ) }
</Warning>
</TagName>
);
}

const isPlaceholder = ! slug;
if ( ! isPlaceholder && isMissing ) {
return (
<TagName { ...blockProps }>
Expand All @@ -78,6 +92,7 @@ export default function TemplatePartEdit( {
);
}

const isEntityAvailable = ! isPlaceholder && ! isMissing;
const inspectorAdvancedControls = (
<InspectorAdvancedControls>
<SelectControl
Expand All @@ -98,7 +113,7 @@ export default function TemplatePartEdit( {
);

return (
<>
<RecursionProvider>
{ inspectorAdvancedControls }
<TagName { ...blockProps }>
{ isPlaceholder && (
Expand Down Expand Up @@ -146,6 +161,6 @@ export default function TemplatePartEdit( {
) }
{ ! isPlaceholder && ! isResolved && <Spinner /> }
</TagName>
</>
</RecursionProvider>
);
}
27 changes: 27 additions & 0 deletions packages/block-library/src/template-part/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'] ) ) {
Expand Down Expand Up @@ -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 <strong>%s</strong>: 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 );
Expand Down

0 comments on commit d1f22bd

Please sign in to comment.