diff --git a/docs/designers-developers/developers/backward-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md index 9947d262b243cb..143b449b544b9c 100644 --- a/docs/designers-developers/developers/backward-compatibility/deprecations.md +++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md @@ -2,6 +2,10 @@ For features included in the Gutenberg plugin, the deprecation policy is intended to support backward compatibility for two minor plugin releases, when possible. Features and code included in a stable release of WordPress are not included in this deprecation timeline, and are instead subject to the [versioning policies of the WordPress project](https://make.wordpress.org/core/handbook/about/release-cycle/version-numbering/). The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version. +## 8.3.0 + +- The PHP function `gutenberg_get_post_from_context` has been removed. Use [Block Context](https://github.com/WordPress/gutenberg/blob/master/docs/designers-developers/developers/block-api/block-context.md) instead. + ## 5.5.0 - The PHP function `gutenberg_init` has been removed. diff --git a/lib/compat.php b/lib/compat.php index 48f3c7db728cf7..38e33289738e80 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -169,6 +169,9 @@ function gutenberg_get_post_from_context() { if ( is_admin() || defined( 'REST_REQUEST' ) ) { return null; } + + _deprecated_function( __FUNCTION__, '8.1.0' ); + if ( ! in_the_loop() ) { rewind_posts(); the_post(); @@ -204,7 +207,8 @@ function gutenberg_render_block_with_assigned_block_context( $pre_render, $parse /** This filter is documented in src/wp-includes/blocks.php */ $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block ); - $context = array( + + $context = array( 'postId' => $post->ID, /* @@ -215,7 +219,16 @@ function gutenberg_render_block_with_assigned_block_context( $pre_render, $parse */ 'postType' => $post->post_type, ); - $block = new WP_Block( $parsed_block, $context ); + + /** + * Filters the default context provided to a rendered block. + * + * @param array $context Default context. + * @param array $parsed_block Block being rendered, filtered by `render_block_data`. + */ + $context = apply_filters( 'render_block_context', $context, $parsed_block ); + + $block = new WP_Block( $parsed_block, $context ); return $block->render(); } diff --git a/lib/template-loader.php b/lib/template-loader.php index c4df3a2a69ff59..0e31542e3fb700 100644 --- a/lib/template-loader.php +++ b/lib/template-loader.php @@ -383,3 +383,28 @@ function gutenberg_template_loader_filter_block_editor_settings( $settings ) { return $settings; } add_filter( 'block_editor_settings', 'gutenberg_template_loader_filter_block_editor_settings' ); + +/** + * Removes post details from block context when rendering a block template. + * + * @param array $context Default context. + * + * @return array Filtered context. + */ +function gutenberg_template_render_without_post_block_context( $context ) { + /* + * When loading a template or template part directly and not through a page + * that resolves it, the top-level post ID and type context get set to that + * of the template part. Templates are just the structure of a site, and + * they should not be available as post context because blocks like Post + * Content would recurse infinitely. + */ + if ( isset( $context['postType'] ) && + ( 'wp_template' === $context['postType'] || 'wp_template_part' === $context['postType'] ) ) { + unset( $context['postId'] ); + unset( $context['postType'] ); + } + + return $context; +} +add_filter( 'render_block_context', 'gutenberg_template_render_without_post_block_context' ); diff --git a/packages/block-library/src/post-author/block.json b/packages/block-library/src/post-author/block.json index f197674f8beb13..432d2f35508a6f 100644 --- a/packages/block-library/src/post-author/block.json +++ b/packages/block-library/src/post-author/block.json @@ -29,5 +29,6 @@ "customTextColor": { "type": "string" } - } + }, + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-author/index.php b/packages/block-library/src/post-author/index.php index 376acae89a0e64..d629b4267d012c 100644 --- a/packages/block-library/src/post-author/index.php +++ b/packages/block-library/src/post-author/index.php @@ -88,23 +88,27 @@ function post_author_build_css_font_sizes( $attributes ) { /** * Renders the `core/post-author` block on the server. * - * @param array $attributes Block attributes. - * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the rendered author block. */ -function render_block_core_post_author( $attributes ) { +function render_block_core_post_author( $attributes, $content, $block ) { if ( empty( $attributes ) ) { return ''; } - $post = gutenberg_get_post_from_context(); + if ( ! isset( $block->context['postId'] ) ) { + return ''; + } - if ( ! $post ) { + $author_id = get_post_field( 'post_author', $block->context['postId'] ); + if ( empty( $author_id ) ) { return ''; } $avatar = ! empty( $attributes['avatarSize'] ) ? get_avatar( - $post->post_author, + $author_id, $attributes['avatarSize'] ) : null; @@ -129,8 +133,8 @@ function render_block_core_post_author( $attributes ) { ( $attributes['showAvatar'] ? '
' : '' ) . '' . ''; } diff --git a/packages/block-library/src/post-comments-count/block.json b/packages/block-library/src/post-comments-count/block.json index e9becd828a0664..579642ee54dd45 100644 --- a/packages/block-library/src/post-comments-count/block.json +++ b/packages/block-library/src/post-comments-count/block.json @@ -1,4 +1,5 @@ { "name": "core/post-comments-count", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-comments-count/index.php b/packages/block-library/src/post-comments-count/index.php index a7e811b8157324..34cf7a2fae1faf 100644 --- a/packages/block-library/src/post-comments-count/index.php +++ b/packages/block-library/src/post-comments-count/index.php @@ -8,23 +8,25 @@ /** * Renders the `core/post-comments-count` block on the server. * - * @param array $attributes The block attributes. - * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the filtered post comments count for the current post. */ -function render_block_core_post_comments_count( $attributes ) { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_comments_count( $attributes, $content, $block ) { + if ( ! isset( $block->context['postId'] ) ) { return ''; } + $class = 'wp-block-post-comments-count'; if ( isset( $attributes['className'] ) ) { $class .= ' ' . $attributes['className']; } + return sprintf( '%2$s', esc_attr( $class ), - get_comments_number( $post ) + get_comments_number( $block->context['postId'] ) ); } diff --git a/packages/block-library/src/post-comments-form/block.json b/packages/block-library/src/post-comments-form/block.json index d126a4e8fb8244..f5cf116ec47fdc 100644 --- a/packages/block-library/src/post-comments-form/block.json +++ b/packages/block-library/src/post-comments-form/block.json @@ -1,4 +1,5 @@ { "name": "core/post-comments-form", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-comments-form/index.php b/packages/block-library/src/post-comments-form/index.php index 9d5e78cffa5cfa..78ac2b6cb3312c 100644 --- a/packages/block-library/src/post-comments-form/index.php +++ b/packages/block-library/src/post-comments-form/index.php @@ -8,15 +8,18 @@ /** * Renders the `core/post-comments-form` block on the server. * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the filtered post comments form for the current post. */ -function render_block_core_post_comments_form() { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_comments_form( $attributes, $content, $block ) { + if ( ! isset( $block->context['postId'] ) ) { return ''; } + ob_start(); - comment_form( array(), $post->ID ); + comment_form( array(), $block->context['postId'] ); $form = ob_get_clean(); return $form; diff --git a/packages/block-library/src/post-comments/block.json b/packages/block-library/src/post-comments/block.json index fdd930fb3d26c8..5dd415e52f047d 100644 --- a/packages/block-library/src/post-comments/block.json +++ b/packages/block-library/src/post-comments/block.json @@ -1,4 +1,5 @@ { "name": "core/post-comments", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-comments/index.php b/packages/block-library/src/post-comments/index.php index 85780230704ecd..0675c9237482da 100644 --- a/packages/block-library/src/post-comments/index.php +++ b/packages/block-library/src/post-comments/index.php @@ -8,18 +8,28 @@ /** * Renders the `core/post-comments` block on the server. * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the filtered post comments for the current post wrapped inside "p" tags. */ -function render_block_core_post_comments() { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_comments( $attributes, $content, $block ) { + global $post; + + if ( ! isset( $block->context['postId'] ) ) { return ''; } + $post_before = $post; + + $post = get_post( $block->context['postId'] ); + setup_postdata( $post ); + // This generates a deprecate message. // Ideally this deprecation is removed. ob_start(); comments_template(); + $post = $post_before; return ob_get_clean(); } diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index 5c598d0970f490..d04c4d474093e6 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -1,4 +1,5 @@ { "name": "core/post-content", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-content/index.php b/packages/block-library/src/post-content/index.php index dc7eebdf00ffa2..cca80b41dc9226 100644 --- a/packages/block-library/src/post-content/index.php +++ b/packages/block-library/src/post-content/index.php @@ -8,16 +8,19 @@ /** * Renders the `core/post-content` block on the server. * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the filtered post content of the current post. */ -function render_block_core_post_content() { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_content( $attributes, $content, $block ) { + if ( ! isset( $block->context['postId'] ) ) { return ''; } + return ( '' . get_the_excerpt( $post ); + $output = '
' . get_the_excerpt( $block->context['postId'] ); if ( ! isset( $attributes['showMoreOnNewLine'] ) || $attributes['showMoreOnNewLine'] ) { $output .= '
' . '' . $more_text . '
'; } else { diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json index 7cab18de41c3ae..27aefc247ee499 100644 --- a/packages/block-library/src/post-featured-image/block.json +++ b/packages/block-library/src/post-featured-image/block.json @@ -1,4 +1,5 @@ { "name": "core/post-featured-image", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-featured-image/index.php b/packages/block-library/src/post-featured-image/index.php index 9a80c2525fa3ee..ed1c5609be8995 100644 --- a/packages/block-library/src/post-featured-image/index.php +++ b/packages/block-library/src/post-featured-image/index.php @@ -8,14 +8,17 @@ /** * Renders the `core/post-featured-image` block on the server. * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the featured image for the current post. */ -function render_block_core_post_featured_image() { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_featured_image( $attributes, $content, $block ) { + if ( ! isset( $block->context['postId'] ) ) { return ''; } - return '' . get_the_post_thumbnail( $post ) . '
'; + + return '' . get_the_post_thumbnail( $block->context['postId'] ) . '
'; } /** diff --git a/packages/block-library/src/post-tags/block.json b/packages/block-library/src/post-tags/block.json index 29db26b80c7679..edc4ef6ae65d75 100644 --- a/packages/block-library/src/post-tags/block.json +++ b/packages/block-library/src/post-tags/block.json @@ -1,4 +1,5 @@ { "name": "core/post-tags", - "category": "layout" + "category": "layout", + "context": [ "postId" ] } diff --git a/packages/block-library/src/post-tags/index.php b/packages/block-library/src/post-tags/index.php index ed03b6055f7cfa..5a5e35debc382e 100644 --- a/packages/block-library/src/post-tags/index.php +++ b/packages/block-library/src/post-tags/index.php @@ -8,14 +8,17 @@ /** * Renders the `core/post-tags` block on the server. * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. * @return string Returns the filtered post tags for the current post wrapped inside "a" tags. */ -function render_block_core_post_tags() { - $post = gutenberg_get_post_from_context(); - if ( ! $post ) { +function render_block_core_post_tags( $attributes, $content, $block ) { + if ( ! isset( $block->context['postId'] ) ) { return ''; } - $post_tags = get_the_tags(); + + $post_tags = get_the_tags( $block->context['postId'] ); if ( ! empty( $post_tags ) ) { $output = ''; foreach ( $post_tags as $tag ) { diff --git a/packages/e2e-tests/plugins/block-context.php b/packages/e2e-tests/plugins/block-context.php index 8f3ca467d50043..06bb1102c3daca 100644 --- a/packages/e2e-tests/plugins/block-context.php +++ b/packages/e2e-tests/plugins/block-context.php @@ -47,15 +47,19 @@ function gutenberg_test_register_context_blocks() { register_block_type( 'gutenberg/test-context-consumer', array( - 'context' => array( 'gutenberg/recordId' ), + 'context' => array( + 'gutenberg/recordId', + 'postId', + 'postType', + ), 'render_callback' => function( $attributes, $content, $block ) { - $record_id = $block->context['gutenberg/recordId']; - - if ( ! is_int( $record_id ) ) { - throw new Exception( 'Expected numeric recordId' ); - } + $ordered_context = array( + $block->context['gutenberg/recordId'], + $block->context['postId'], + $block->context['postType'], + ); - return 'The record ID is: ' . filter_var( $record_id, FILTER_VALIDATE_INT ); + return implode( ',', $ordered_context ); }, ) ); diff --git a/packages/e2e-tests/specs/editor/plugins/block-context.test.js b/packages/e2e-tests/specs/editor/plugins/block-context.test.js index 9f933f8150cf6e..3a96eb3e141f6a 100644 --- a/packages/e2e-tests/specs/editor/plugins/block-context.test.js +++ b/packages/e2e-tests/specs/editor/plugins/block-context.test.js @@ -70,12 +70,7 @@ describe( 'Block context', () => { expect( innerBlockText ).toBe( 'The record ID is: 123' ); } ); - // Disable reason: Block context PHP implementation is temporarily reverted. - // This will be unskipped once the implementation is restored. Skipping was - // the most direct option for revert given time constraints. - - /* eslint-disable-next-line jest/no-disabled-tests */ - test.skip( 'Block context is reflected in the preview', async () => { + test( 'Block context is reflected in the preview', async () => { await insertBlock( 'Test Context Provider' ); const editorPage = page; const previewPage = await openPreviewPage( editorPage ); @@ -85,7 +80,7 @@ describe( 'Block context', () => { '.entry-content', ( contentWrapper ) => contentWrapper.textContent.trim() ); - expect( content ).toBe( 'The record ID is: 0' ); + expect( content ).toMatch( /^0,\d+,post$/ ); // Return to editor to change context value to non-default. await editorPage.bringToFront(); @@ -104,7 +99,7 @@ describe( 'Block context', () => { '.entry-content', ( contentWrapper ) => contentWrapper.textContent.trim() ); - expect( content ).toBe( 'The record ID is: 123' ); + expect( content ).toMatch( /^123,\d+,post$/ ); // Clean up await editorPage.bringToFront(); diff --git a/phpunit/class-block-context-test.php b/phpunit/class-block-context-test.php index a990d882e5fb6f..f3a7fc1011a757 100644 --- a/phpunit/class-block-context-test.php +++ b/phpunit/class-block-context-test.php @@ -163,4 +163,38 @@ function test_provides_default_context() { ); } + /** + * Tests that default block context can be filtered. + */ + function test_default_context_is_filterable() { + $provided_context = array(); + + $this->register_block_type( + 'gutenberg/test-context-consumer', + array( + 'context' => array( 'example' ), + 'render_callback' => function( $attributes, $content, $block ) use ( &$provided_context ) { + $provided_context[] = $block->context; + + return ''; + }, + ) + ); + + $filter_block_context = function( $context ) { + $context['example'] = 'ok'; + return $context; + }; + + $parsed_blocks = parse_blocks( '' ); + + add_filter( 'render_block_context', $filter_block_context ); + + render_block( $parsed_blocks[0] ); + + remove_filter( 'render_block_context', $filter_block_context ); + + $this->assertEquals( array( 'example' => 'ok' ), $provided_context[0] ); + } + }