| @@ -259,22 +259,38 @@ | ||
| 259 | 259 | if ( $require_form_post && ! self::is_valid_form_post( $post, $allow_preview ) ) { |
| 260 | 260 | return array(); |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | + return self::get_blocks_from_content( $post->post_content ); | |
| 264 | + } | |
| 265 | + | |
| 266 | + /** | |
| 267 | + * Parses block content and expands reusable (`core/block`) references in exactly the | |
| 268 | + * same way as `get_blocks_by_post()`. | |
| 269 | + * | |
| 270 | + * @param string $post_content | |
| 271 | + * | |
| 272 | + * @return array | |
| 273 | + */ | |
| 274 | + public static function get_blocks_from_content( string $post_content ): array { | |
| 263 | 275 | return array_map( |
| 264 | 276 | function ( $block ) { |
| 265 | - self::walk_by_reusable( $block ); | |
| 277 | + self::walk_by_reusable( $block, array() ); | |
| 266 | 278 | |
| 267 | 279 | return $block; |
| 268 | 280 | }, |
| 269 | - parse_blocks( $post->post_content ) | |
| 281 | + parse_blocks( $post_content ) | |
| 270 | 282 | ); |
| 271 | 283 | } |
| 272 | 284 | |
| 273 | - private static function walk_by_reusable( array &$block ) { | |
| 285 | + /** | |
| 286 | + * @param array $block | |
| 287 | + * @param array $visited_reusable_ids IDs already visited in the current reference path. | |
| 288 | + */ | |
| 289 | + private static function walk_by_reusable( array &$block, array $visited_reusable_ids ) { | |
| 274 | 290 | if ( ! empty( $block['innerBlocks'] ) ) { |
| 275 | 291 | foreach ( $block['innerBlocks'] as &$current ) { |
| 276 | - self::walk_by_reusable( $current ); | |
| 292 | + self::walk_by_reusable( $current, $visited_reusable_ids ); | |
| 277 | 293 | } |
| 278 | 294 | |
| 279 | 295 | return; |
| 280 | 296 | } |
| @@ -282,10 +298,33 @@ | ||
| 282 | 298 | if ( 'core/block' !== $block['blockName'] ) { |
| 283 | 299 | return; |
| 284 | 300 | } |
| 285 | 301 | |
| 286 | - $reusable_id = $block['attrs']['ref'] ?? 0; | |
| 287 | - $block['innerBlocks'] = self::get_blocks_by_post( $reusable_id, false ); | |
| 302 | + $reusable_id = (int) ( $block['attrs']['ref'] ?? 0 ); | |
| 303 | + | |
| 304 | + if ( ! $reusable_id || isset( $visited_reusable_ids[ $reusable_id ] ) ) { | |
| 305 | + $block['innerBlocks'] = array(); | |
| 306 | + | |
| 307 | + return; | |
| 308 | + } | |
| 309 | + | |
| 310 | + $reusable = get_post( $reusable_id ); | |
| 311 | + | |
| 312 | + if ( ! $reusable instanceof \WP_Post ) { | |
| 313 | + $block['innerBlocks'] = array(); | |
| 314 | + | |
| 315 | + return; | |
| 316 | + } | |
| 317 | + | |
| 318 | + $visited_reusable_ids[ $reusable_id ] = true; | |
| 319 | + $block['innerBlocks'] = array_map( | |
| 320 | + function ( $inner_block ) use ( $visited_reusable_ids ) { | |
| 321 | + self::walk_by_reusable( $inner_block, $visited_reusable_ids ); | |
| 322 | + | |
| 323 | + return $inner_block; | |
| 324 | + }, | |
| 325 | + parse_blocks( $reusable->post_content ) | |
| 326 | + ); | |
| 288 | 327 | } |
| 289 | 328 | |
| 290 | 329 | public static function delete_namespace( $block ): string { |
| 291 | 330 | if ( is_array( $block ) ) { |