diff --git a/packages/block-library/src/post-excerpt/index.php b/packages/block-library/src/post-excerpt/index.php index 09d6b6f003d34b..51e92698c9900e 100644 --- a/packages/block-library/src/post-excerpt/index.php +++ b/packages/block-library/src/post-excerpt/index.php @@ -82,14 +82,39 @@ function register_block_core_post_excerpt() { * override the filter in the editor, otherwise * the excerpt length block setting has no effect. * Returns 100 because 100 is the max length in the setting. + * + * @return int Filtered excerpt length. */ -if ( is_admin() || - defined( 'REST_REQUEST' ) && REST_REQUEST ) { - add_filter( - 'excerpt_length', - static function () { - return 100; - }, - PHP_INT_MAX - ); +function register_block_core_post_excerpt_length_filter() { + return 100; } + +// This needs to be wrapped in the rest_dispatch_request hook to ensure +// that the code works when using REST API preloading or batch requests. +add_filter( + 'rest_dispatch_request', + static function ( $dispatch_result, $request, $route, $handler ) { + if ( 'edit' !== $request['context'] ) { + return; + } + + if ( empty( $handler['callback'][0] ) || ! is_object( $handler['callback'][0] ) ) { + return; + } + + $controller = $handler['callback'][0]; + + if ( ! is_callable( array( $controller, 'get_fields_for_response' ) ) ) { + return; + } + + $fields = $controller->get_fields_for_response( $request ); + if ( ! rest_is_field_included( 'excerpt', $fields ) ) { + return; + } + + add_filter( 'excerpt_length', 'register_block_core_post_excerpt_length_filter', 20 ); + }, + 10, + 4 +); diff --git a/phpunit/tests/blocks/registerBlockCorePostExcerptLengthFilter.php b/phpunit/tests/blocks/registerBlockCorePostExcerptLengthFilter.php new file mode 100644 index 00000000000000..0d7d063f521d82 --- /dev/null +++ b/phpunit/tests/blocks/registerBlockCorePostExcerptLengthFilter.php @@ -0,0 +1,90 @@ +post->create( + array( + 'post_excerpt' => '', + ) + ); + + self::$admin_id = $factory->user->create( + array( + 'role' => 'administrator', + 'user_login' => 'superadmin', + ) + ); + } + + /** + * Unit test to ensure the correct length of the post excerpt in the REST API context. + * + * @dataProvider data_register_block_core_post_excerpt_length_filter + * + * @param int $expeceted_excerpt_length Expected excerpt length. + * @param string $context Current context. + */ + public function test_register_block_core_post_excerpt_length_filter( $expeceted_excerpt_length, $context ) { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id ); + if ( '' !== $context ) { + $request->set_param( 'context', $context ); + $_REQUEST['context'] = $context; + } + + $request->set_param( '_locale', 'user' ); + $mock = $this->getMockBuilder( stdClass::class ); + if ( method_exists( $mock, 'addMethods' ) ) { + $mock->addMethods( array( 'excerpt_length_callback' ) ); + } else { + // Ensure compatibility with older PHPUnit versions. + $mock->setMethods( array( 'excerpt_length_callback' ) ); + } + $mock = $mock->getMock(); + + $mock->expects( $this->atLeastOnce() ) + ->method( 'excerpt_length_callback' ) + ->with( $this->equalTo( $expeceted_excerpt_length ) ) + ->willReturn( $expeceted_excerpt_length ); + + // Using PHP_INT_MAX for testing purposes only; this should be avoided in production code. + add_filter( 'excerpt_length', array( $mock, 'excerpt_length_callback' ), PHP_INT_MAX ); + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( WP_Http::OK, $response->get_status(), 'Expected response status to be ' . WP_Http::OK ); + remove_filter( 'excerpt_length', array( $mock, 'excerpt_length_callback' ), PHP_INT_MAX ); + unset( $_REQUEST['context'] ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_register_block_core_post_excerpt_length_filter() { + return array( + 'no_edit_context' => array( + 55, // Default filter value. + '', + ), + 'edit_context' => array( + 100, + 'edit', + ), + ); + } +}