WordPress.org

Make WordPress Core

Opened 12 months ago

Closed 4 months ago

#43887 closed enhancement (fixed)

Expose Gutenberg Data Format version in the REST API

Reported by: danielbachhuber Owned by: desrosj
Milestone: 5.0 Priority: normal
Severity: normal Version:
Component: REST API Keywords: has-patch has-unit-tests fixed-5.0
Focuses: rest-api Cc:

Description

From https://github.com/WordPress/gutenberg/issues/6435

More advanced REST API consumers will want to switch how they handle post_content depending on whether the content contains blocks or not, and (in the future) will likely want to switch based on which version of the block format it contains.

Rather than requiring consumers to duplicate how Gutenberg determines whether to parse blocks or not, we can expose the same information in post objects returned by the REST API.

Currently, it would return:

  • 0: The post_content does not contain blocks, it can be treated as classic content.
  • 1: The post_content contains blocks, it should be treated appropriately.

If the block format has back compat breaking changes in the future, we can increment this value as needed.

For an immediate practical use, this would be useful for the WordPress mobile apps to decide how to handle the post_content: https://github.com/wordpress-mobile/WordPress-iOS/pull/9194

Attachments (4)

43887.diff (5.8 KB) - added by birgire 12 months ago.
43887.2.diff (6.9 KB) - added by birgire 6 months ago.
43887.3.diff (11.5 KB) - added by danielbachhuber 6 months ago.
43887.3.2.diff (11.5 KB) - added by danielbachhuber 6 months ago.

Download all attachments as: .zip

Change History (18)

#1 @birgire
12 months ago

For reference here are Gutenberg's functions to determine if the post or the content has blocks:

/**
 * Determine whether a post has blocks. This test optimizes for performance
 * rather than strict accuracy, detecting the pattern of a block but not
 * validating its structure. For strict accuracy, you should use the block
 * parser on post content.
 *
 * @see gutenberg_parse_blocks()
 *
 * @since 0.5.0
 *
 * @param object $post Post.
 * @return bool  Whether the post has blocks.
 */
function gutenberg_post_has_blocks( $post ) {
        $post = get_post( $post );
        return $post && gutenberg_content_has_blocks( $post->post_content );
}

/**
 * Determine whether a content string contains blocks. This test optimizes for
 * performance rather than strict accuracy, detecting the pattern of a block
 * but not validating its structure. For strict accuracy, you should use the
 * block parser on post content.
 *
 * @since 1.6.0
 * @see gutenberg_parse_blocks()
 *
 * @param string $content Content to test.
 * @return bool Whether the content contains blocks.
 */
function gutenberg_content_has_blocks( $content ) {
        return false !== strpos( $content, '<!-- wp:' );
}

#2 @birgire
12 months ago

A possible direct approach, where we explicitly set the available values 0 or 1 instead of type casting, could be:

if ( ! empty( $schema['properties']['block'] ) ) {
	$data['block'] = ( false !== strpos( $post->post_content, '<!-- wp:' ) ) ? 1 : 0;
}

where:

$schema['properties']['block'] = array(
	'description' => __( 'The version of the block format that the object's content string is using.' ),
	'type'        => 'integer',
	'context'     => array( 'view', 'edit' ),
	'readonly'    => true,
);

assuming the same context as the rendered content.

The integer type sounds like a better choice than boolean, if it can increment in the future.

Actually there's a version function in Gutenberg:

/**
 * Returns the current version of the block format that the content string is using.
 *
 * If the string doesn't contain blocks, it returns 0.
 *
 * @since 2.8.0
 * @see gutenberg_content_has_blocks()
 *
 * @param string $content Content to test.
 * @return int The block format version.
 */
function gutenberg_content_block_version( $content ) {
	return gutenberg_content_has_blocks( $content ) ? 1 : 0;
}

When Gutenberg merges into core, I guess the function gutenberg_content_block_version() will not change it's name. So one could use:

if ( function_exists( 'gutenberg_content_block_version' ) ) {
    $data['block'] = gutenberg_content_block_version( $post );
}

with above as a fallback (depending on when implemented).

One could also consider a wrapper method:

if ( ! empty( $schema['properties']['block'] ) ) {
	$data['block'] = $this->get_content_block_version( $post );
}

if there need to be fallback checks.

ps: perhaps the block_version is more descriptive as a property name than just block.

Last edited 12 months ago by birgire (previous) (diff)

#3 @birgire
12 months ago

This seems to belong to the content property:

if ( ! empty( $schema['properties']['content'] ) ) {
	$data['content'] = array(
		'raw'           => $post->post_content,
		/** This filter is documented in wp-includes/post-template.php */
		'rendered'      => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ),
		'protected'     => (bool) $post->post_password,
		/** @todo: Replace with `gutenberg_content_block_version()` when Gutenberg merges into core. */
		'block_version' => ( false !== strpos( $post->post_content, '<!-- wp:' ) ) ? 1 : 0,
	);
}

@birgire
12 months ago

#4 @birgire
12 months ago

  • Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed

43887.diff is a first pass that:

  • Adds the readonly block_version property of integer type, under the post content property, with view and edit context.
  • Adds a block content check with strpos().
  • Adds a @todo to consider the gutenberg_content_block_version() if implemented after the Gutenberg merge.
  • Adds tests.
Last edited 12 months ago by birgire (previous) (diff)

This ticket was mentioned in Slack in #core-restapi by danielbachhuber. View the logs.


6 months ago

#6 @danielbachhuber
6 months ago

  • Keywords needs-patch needs-unit-tests added; has-patch has-unit-tests removed

This needs a new patch with unit tests that uses the new Block functions.

This ticket was mentioned in Slack in #core-restapi by danielbachhuber. View the logs.


6 months ago

#8 @desrosj
6 months ago

  • Owner set to desrosj
  • Status changed from new to assigned

@birgire
6 months ago

#9 @birgire
6 months ago

  • Keywords has-unit-tests has-patch added; needs-patch needs-unit-tests removed

43887.2.diff

  • Moves the gutenberg_content_block_version() function into wp-includes/blocks.php and renames it block_version(). Maybe it's a too general name?
  • I couldn't find Gutenberg unit tests for the gutenberg_content_block_version() function, so I added a some into tests/phpunit/tests/blocks.php.
  • Updates the 43887.diff with block_version().

#10 @danielbachhuber
6 months ago

In 43887.3.2.diff:

  • Limited context to solely 'edit', as block_version is meant for the editor and not public consumption.
  • Fixes the errant comma in schema description.
  • Regenerates wp-api-generated.js.

#11 @danielbachhuber
6 months ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 43770:

REST API: Include block_version on Post content object.

The block_version denotes which version of Blocks the post_content contains. Introduces new block_version() function for versioning Blocks.

Props danielbachhuber, birgire.
Fixes #43887.

#12 @danielbachhuber
6 months ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

Reopening for merge to trunk.

#13 @danielbachhuber
6 months ago

  • Keywords fixed-5.0 added

#14 @pento
4 months ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 44127:

REST API: Include block_version on Post content object.

The block_version denotes which version of Blocks the post_content contains. Introduces new block_version() function for versioning Blocks.

Merges [43770] from the 5.0 branch to trunk.

Props danielbachhuber, birgire.
Fixes #43887.

Note: See TracTickets for help on using tickets.