WordPress.org

Make WordPress Core

Opened 2 months ago

Last modified 8 weeks ago

#43887 new enhancement

Expose Gutenberg Data Format version in the REST API

Reported by: danielbachhuber Owned by:
Milestone: 5.0 Priority: normal
Severity: normal Version:
Component: REST API Keywords: has-patch has-unit-tests
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 (1)

43887.diff (5.8 KB) - added by birgire 8 weeks ago.

Download all attachments as: .zip

Change History (5)

#1 @birgire
2 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
2 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 8 weeks ago by birgire (previous) (diff)

#3 @birgire
8 weeks 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
8 weeks ago

#4 @birgire
8 weeks 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 8 weeks ago by birgire (previous) (diff)
Note: See TracTickets for help on using tickets.