Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Hooks API: Create helper function for creating WP_Block_Template object for $context #6278

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
3034818
Block Hooks: Remove references to files in test
ockham Mar 12, 2024
7feddde
Block Hooks: Add test to verify that correct context is passed to fil…
ockham Mar 12, 2024
5c99e4f
Coding Standards
ockham Mar 12, 2024
2d9d585
Fix failing block-template-utils test
tjcafferkey Mar 13, 2024
1bfb892
Map incoming data to build object
tjcafferkey Mar 13, 2024
cd92831
Revert mock post_content values back to 'Content'
tjcafferkey Mar 13, 2024
fc4b1cf
Coding standards
tjcafferkey Mar 13, 2024
b00199a
Fix test
ockham Mar 13, 2024
3cda15c
Add some more coverage
ockham Mar 13, 2024
4800ae5
Remove commented-out line
ockham Mar 13, 2024
b906ae1
Move global to top of function
ockham Mar 13, 2024
9742450
Rearrange comment
ockham Mar 13, 2024
ee06af0
Include author
ockham Mar 13, 2024
ba39f47
Rewrite assertion
ockham Mar 13, 2024
9e8f963
Add TODO comment
ockham Mar 13, 2024
33ebc94
Make sure template exists
ockham Mar 13, 2024
95dc554
Cover origin
ockham Mar 13, 2024
fc78209
Test post type
ockham Mar 13, 2024
218e33d
Cover post name
ockham Mar 13, 2024
0b9bc72
Cover theme and ID
ockham Mar 13, 2024
b0591af
Remove whitespace
tjcafferkey Mar 14, 2024
67e1c47
Rephrase comment
ockham Mar 14, 2024
945670a
Whitespace, my raison d'etre
ockham Mar 14, 2024
dee0a86
Madness
ockham Mar 14, 2024
045ebee
Rewrite a bit
ockham Mar 14, 2024
d70d1fe
Add ticket number to test
ockham Mar 14, 2024
d25f35a
Get test to pass
ockham Mar 14, 2024
6a76a07
Rename static post var to template_post
ockham Mar 14, 2024
a757995
Attempt fixing controller test
ockham Mar 14, 2024
79f715f
Rename argument to changes
ockham Mar 15, 2024
3318e86
Remove temp var
ockham Mar 15, 2024
817be39
Polyfill missing information in changes from existing template post
ockham Mar 15, 2024
494de75
Revert now-obsolete change to the controller
ockham Mar 15, 2024
8720bc9
Remove now-obsolete test fixtures
ockham Mar 15, 2024
1cae0ca
Add helper function to create a WP_Block_Template object from
tjcafferkey Mar 15, 2024
1cdb1cd
Question above post_name
tjcafferkey Mar 15, 2024
48c7fec
Remove hooked blocks algorithm from new helper function
tjcafferkey Mar 15, 2024
ec5c8fb
Remove default values as theyre set in the class declaration
tjcafferkey Mar 15, 2024
7ee78fa
Add post to template mapping
tjcafferkey Mar 15, 2024
377da4c
Updated to missing default fields and is_custom
tjcafferkey Mar 15, 2024
6462922
Update safety checks
tjcafferkey Mar 18, 2024
7812ce7
Update condition
tjcafferkey Mar 18, 2024
1cda30a
Compute has_theme_file inside helper
ockham Mar 18, 2024
36c3597
Alignment
ockham Mar 18, 2024
7fc1800
source is hard-wired to 'custom'
ockham Mar 18, 2024
631ff10
Whitespace
ockham Mar 18, 2024
dd74e43
Still whitespace
ockham Mar 18, 2024
0d35323
Build template ID from values
tjcafferkey Mar 18, 2024
95d679b
Guard against class properties
tjcafferkey Mar 18, 2024
6490f66
Whitespace
tjcafferkey Mar 18, 2024
bf05a79
Whitespace
ockham Mar 18, 2024
c974de8
Refactor derived template object value assignment
tjcafferkey Mar 18, 2024
9274b8d
Remove from derived values
tjcafferkey Mar 18, 2024
b59b2d8
Tabs to spaces
tjcafferkey Mar 18, 2024
7e60e5a
Remove space
tjcafferkey Mar 18, 2024
fb03812
Comment
tjcafferkey Mar 18, 2024
efe5dbe
Add ID to additional fields
tjcafferkey Mar 18, 2024
535b1f3
Tabs to space
tjcafferkey Mar 18, 2024
db647c1
Make more explicit
ockham Mar 18, 2024
c0807cc
Even closer to original
ockham Mar 18, 2024
a22a6c4
Remove now-obsolete PHPDoc
ockham Mar 18, 2024
2faaab7
Break additional_fields into terms and meta
ockham Mar 18, 2024
1f4a05e
Remove unnecessary `get_post` call.
ockham Mar 18, 2024
6612330
Revert "Remove unnecessary `get_post` call."
ockham Mar 18, 2024
b38be2c
Whitespace
ockham Mar 18, 2024
f3d3e51
Taxonomy is called wp_theme
ockham Mar 18, 2024
35b3b5e
Base terms on tax_input
ockham Mar 18, 2024
13bf527
Rename terms to type_terms
ockham Mar 18, 2024
f70aa76
Rearrange logic a bit
ockham Mar 18, 2024
fac17bb
Whitespace
ockham Mar 18, 2024
6c969e9
Some more streamlining
ockham Mar 18, 2024
a0be4c4
Missed one additional_fields instance
ockham Mar 19, 2024
86e8f3c
Move meta below terms
ockham Mar 19, 2024
fd736a5
Add missing properties to
tjcafferkey Mar 19, 2024
3d6b2fe
Return if error
tjcafferkey Mar 19, 2024
d0a00d2
Return error to prepare_item_for_database method if template is a wp_…
tjcafferkey Mar 19, 2024
c2ebc06
Provide terms fallback
ockham Mar 19, 2024
4f9759d
Provide meta fallback
ockham Mar 19, 2024
a12bca6
Correctly set area
ockham Mar 19, 2024
605f1c8
Update function return value doc
tjcafferkey Mar 19, 2024
19f553b
Inidividual assertions
ockham Mar 19, 2024
6f5148d
Set ID on in test
tjcafferkey Mar 19, 2024
0925386
Coding standards
tjcafferkey Mar 19, 2024
619e3d3
Remove now-obsolete filter mocks
ockham Mar 19, 2024
4329b14
Remove now-obsolete post name stubbing
ockham Mar 19, 2024
c28a0fd
Change filter args
ockham Mar 19, 2024
f063167
Rewrite a bit to make more legible
ockham Mar 19, 2024
827dc59
Add assertions to cover fallback
ockham Mar 19, 2024
6e3829d
Add test coverage for template parts
ockham Mar 19, 2024
fe00325
Remove now-obsolete request arg
ockham Mar 19, 2024
0e4dae0
Whitespace
ockham Mar 19, 2024
eef7df8
Remove wp_ infix
ockham Mar 19, 2024
4459f05
Fix PHPDoc
ockham Mar 19, 2024
383a903
Add unit test to cover the file-based template case
ockham Mar 26, 2024
9c07f47
Move revision handling out of _build_block_template_object_from_post_…
ockham Mar 26, 2024
52ea662
Remove some extraneous assertions
ockham Mar 26, 2024
8270b57
Add coverage for template part
ockham Mar 26, 2024
bc2c5ef
Move tests to separate file
ockham Mar 28, 2024
86fbd45
Remove now-obsolete covers PHPDoc
ockham Mar 28, 2024
afaed48
Rename some tests
ockham Mar 28, 2024
15e9a59
Add check for origin field
ockham Mar 28, 2024
ad89464
Add some more explanatory PHPDoc
ockham Mar 28, 2024
1affcf6
Add test coverage for newly created template and template part
ockham Mar 28, 2024
5b00591
Change order of tests
ockham Mar 28, 2024
ef3f0b0
Remove unnecessary trailing slash
ockham Mar 28, 2024
df3483d
Bump since to 6.5.1
ockham Mar 28, 2024
02e7d7c
Update src/wp-includes/block-template-utils.php
tjcafferkey Apr 2, 2024
11b38f6
PHP Tests update array assignments
tjcafferkey Apr 2, 2024
e8fe56d
Add back second filter argument as deprecated
ockham Apr 3, 2024
afd10e7
Only pass one argument during test
ockham Apr 3, 2024
f0d1818
Remove default priority argument
ockham Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 126 additions & 51 deletions src/wp-includes/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -725,74 +725,115 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy,
}

/**
* Builds a unified template object based a post Object.
* Builds a block template object from a post object.
*
* @since 5.9.0
* @since 6.3.0 Added `modified` property to template objects.
* @since 6.4.0 Added support for a revision post to be passed to this function.
* This is a helper function that creates a block template object from a given post object.
* It is self-sufficient in that it only uses information passed as arguments; it does not
* query the database for additional information.
*
* @since 6.5.1
* @access private
*
* @param WP_Post $post Template post.
* @param WP_Post $post Template post.
* @param array $terms Additional terms to inform the template object.
* @param array $meta Additional meta fields to inform the template object.
* @return WP_Block_Template|WP_Error Template or error object.
swissspidy marked this conversation as resolved.
Show resolved Hide resolved
*/
function _build_block_template_result_from_post( $post ) {
$default_template_types = get_default_block_template_types();

$post_id = wp_is_post_revision( $post );
if ( ! $post_id ) {
$post_id = $post;
}
$parent_post = get_post( $post_id );

$terms = get_the_terms( $parent_post, 'wp_theme' );

if ( is_wp_error( $terms ) ) {
return $terms;
}

if ( ! $terms ) {
function _build_block_template_object_from_post_object( $post, $terms = array(), $meta = array() ) {
if ( empty( $terms['wp_theme'] ) ) {
return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) );
}
$theme = $terms['wp_theme'];

$default_template_types = get_default_block_template_types();

$theme = $terms[0]->name;
$template_file = _get_block_template_file( $post->post_type, $post->post_name );
$has_theme_file = get_stylesheet() === $theme && null !== $template_file;

$origin = get_post_meta( $parent_post->ID, 'origin', true );
$is_wp_suggestion = get_post_meta( $parent_post->ID, 'is_wp_suggestion', true );

$template = new WP_Block_Template();
$template->wp_id = $post->ID;
$template->id = $theme . '//' . $parent_post->post_name;
$template->id = $theme . '//' . $post->post_name;
$template->theme = $theme;
$template->content = $post->post_content;
$template->slug = $post->post_name;
$template->source = 'custom';
$template->origin = ! empty( $origin ) ? $origin : null;
$template->origin = ! empty( $meta['origin'] ) ? $meta['origin'] : null;
$template->type = $post->post_type;
$template->description = $post->post_excerpt;
$template->title = $post->post_title;
$template->status = $post->post_status;
$template->has_theme_file = $has_theme_file;
$template->is_custom = empty( $is_wp_suggestion );
$template->is_custom = empty( $meta['is_wp_suggestion'] );
$template->author = $post->post_author;
tjcafferkey marked this conversation as resolved.
Show resolved Hide resolved
$template->modified = $post->post_modified;
tjcafferkey marked this conversation as resolved.
Show resolved Hide resolved

if ( 'wp_template' === $parent_post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
$template->post_types = $template_file['postTypes'];
}

if ( 'wp_template' === $parent_post->post_type && isset( $default_template_types[ $template->slug ] ) ) {
if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) {
$template->is_custom = false;
}

if ( 'wp_template_part' === $post->post_type && isset( $terms['wp_template_part_area'] ) ) {
$template->area = $terms['wp_template_part_area'];
}

return $template;
}

/**
* Builds a unified template object based a post Object.
*
* @since 5.9.0
* @since 6.3.0 Added `modified` property to template objects.
* @since 6.4.0 Added support for a revision post to be passed to this function.
* @access private
*
* @param WP_Post $post Template post.
* @return WP_Block_Template|WP_Error Template or error object.
*/
function _build_block_template_result_from_post( $post ) {
$post_id = wp_is_post_revision( $post );
if ( ! $post_id ) {
$post_id = $post;
}
$parent_post = get_post( $post_id );
$post->post_name = $parent_post->post_name;
$post->post_type = $parent_post->post_type;

$terms = get_the_terms( $parent_post, 'wp_theme' );

if ( is_wp_error( $terms ) ) {
return $terms;
}

if ( ! $terms ) {
return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) );
}

$terms = array(
'wp_theme' => $terms[0]->name,
);

if ( 'wp_template_part' === $parent_post->post_type ) {
$type_terms = get_the_terms( $parent_post, 'wp_template_part_area' );
if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
$template->area = $type_terms[0]->name;
$terms['wp_template_part_area'] = $type_terms[0]->name;
}
}

$meta = array(
'origin' => get_post_meta( $parent_post->ID, 'origin', true ),
'is_wp_suggestion' => get_post_meta( $parent_post->ID, 'is_wp_suggestion', true ),
);

$template = _build_block_template_object_from_post_object( $post, $terms, $meta );

if ( is_wp_error( $template ) ) {
return $template;
}

// Check for a block template without a description and title or with a title equal to the slug.
if ( 'wp_template' === $parent_post->post_type && empty( $template->description ) && ( empty( $template->title ) || $template->title === $template->slug ) ) {
$matches = array();
Expand Down Expand Up @@ -1444,36 +1485,70 @@ function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '
* @since 6.5.0
* @access private
*
* @param stdClass $post An object representing a template or template part
* prepared for inserting or updating the database.
* @param WP_REST_Request $request Request object.
* @return stdClass The updated object representing a template or template part.
* @param stdClass $changes An object representing a template or template part
* prepared for inserting or updating the database.
* @param WP_REST_Request $deprecated Deprecated. Not used.
* @return stdClass|WP_Error The updated object representing a template or template part.
*/
function inject_ignored_hooked_blocks_metadata_attributes( $post, $request ) {
$filter_name = current_filter();
if ( ! str_starts_with( $filter_name, 'rest_pre_insert_' ) ) {
return $post;
function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated = null ) {
if ( null !== $deprecated ) {
_deprecated_argument( __FUNCTION__, '6.5.1' );
}
$post_type = str_replace( 'rest_pre_insert_', '', $filter_name );

$hooked_blocks = get_hooked_blocks();
if ( empty( $hooked_blocks ) && ! has_filter( 'hooked_block_types' ) ) {
return $post;
return $changes;
}

// At this point, the post has already been created.
// We need to build the corresponding `WP_Block_Template` object as context argument for the visitor.
// To that end, we need to suppress hooked blocks from getting inserted into the template.
add_filter( 'hooked_block_types', '__return_empty_array', 99999, 0 );
$template = $request['id'] ? get_block_template( $request['id'], $post_type ) : null;
remove_filter( 'hooked_block_types', '__return_empty_array', 99999 );
$meta = isset( $changes->meta_input ) ? $changes->meta_input : array();
$terms = isset( $changes->tax_input ) ? $changes->tax_input : array();
tjcafferkey marked this conversation as resolved.
Show resolved Hide resolved

if ( empty( $changes->ID ) ) {
// There's no post object for this template in the database for this template yet.
$post = $changes;
} else {
// Find the existing post object.
$post = get_post( $changes->ID );

// If the post is a revision, use the parent post's post_name and post_type.
$post_id = wp_is_post_revision( $post );
if ( $post_id ) {
$parent_post = get_post( $post_id );
$post->post_name = $parent_post->post_name;
$post->post_type = $parent_post->post_type;
}

// Apply the changes to the existing post object.
$post = (object) array_merge( (array) $post, (array) $changes );

$type_terms = get_the_terms( $changes->ID, 'wp_theme' );
$terms['wp_theme'] = ! is_wp_error( $type_terms ) && ! empty( $type_terms ) ? $type_terms[0]->name : null;
}

// Required for the WP_Block_Template. Update the post object with the current time.
$post->post_modified = current_time( 'mysql' );

// If the post_author is empty, set it to the current user.
if ( empty( $post->post_author ) ) {
$post->post_author = get_current_user_id();
}

if ( 'wp_template_part' === $post->post_type && ! isset( $terms['wp_template_part_area'] ) ) {
$area_terms = get_the_terms( $changes->ID, 'wp_template_part_area' );
$terms['wp_template_part_area'] = ! is_wp_error( $area_terms ) && ! empty( $area_terms ) ? $area_terms[0]->name : null;
}

$template = _build_block_template_object_from_post_object( new WP_Post( $post ), $terms, $meta );

if ( is_wp_error( $template ) ) {
return $template;
}

$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'set_ignored_hooked_blocks_metadata' );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'set_ignored_hooked_blocks_metadata' );

$blocks = parse_blocks( $post->post_content );
$content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
$blocks = parse_blocks( $changes->post_content );
$changes->post_content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );

$post->post_content = $content;
return $post;
return $changes;
}
4 changes: 2 additions & 2 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@
add_action( 'init', '_wp_register_default_font_collections' );

// Add ignoredHookedBlocks metadata attribute to the template and template part post types.
add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 );
add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 );
add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes' );
add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' );

unset( $filter, $action );
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ public function delete_item( $request ) {
* @since 5.8.0
*
* @param WP_REST_Request $request Request object.
* @return stdClass Changes to pass to wp_update_post.
* @return stdClass|WP_Error Changes to pass to wp_update_post.
tjcafferkey marked this conversation as resolved.
Show resolved Hide resolved
*/
protected function prepare_item_for_database( $request ) {
$template = $request['id'] ? get_block_template( $request['id'], $this->post_type ) : null;
Expand Down
68 changes: 0 additions & 68 deletions tests/phpunit/tests/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,72 +403,4 @@ public function test_wp_generate_block_templates_export_file() {
}
$this->assertTrue( $has_html_files, 'contains at least one html file' );
}

/**
* @ticket 60671
*
* @covers inject_ignored_hooked_blocks_metadata_attributes
*/
public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template() {
global $wp_current_filter;
// Mock currently set filter. The $wp_current_filter global is reset during teardown by
// WP_UnitTestCase_Base::_restore_hooks() in tests/phpunit/includes/abstract-testcase.php.
$wp_current_filter[] = 'rest_pre_insert_wp_template';

register_block_type(
'tests/hooked-block',
array(
'block_hooks' => array(
'tests/anchor-block' => 'after',
),
)
);

$id = self::TEST_THEME . '//' . 'my_template';
$request = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $id );

$changes = new stdClass();
$changes->post_content = '<!-- wp:tests/anchor-block -->Hello<!-- /wp:tests/anchor-block -->';

$post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request );
$this->assertSame(
'<!-- wp:tests/anchor-block {"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->Hello<!-- /wp:tests/anchor-block -->',
$post->post_content,
'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.'
);
}

/**
* @ticket 60671
*
* @covers inject_ignored_hooked_blocks_metadata_attributes
*/
public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template_part() {
global $wp_current_filter;
// Mock currently set filter. The $wp_current_filter global is reset during teardown by
// WP_UnitTestCase_Base::_restore_hooks() in tests/phpunit/includes/abstract-testcase.php.
$wp_current_filter[] = 'rest_pre_insert_wp_template_part';

register_block_type(
'tests/hooked-block',
array(
'block_hooks' => array(
'tests/anchor-block' => 'after',
),
)
);

$id = self::TEST_THEME . '//' . 'my_template_part';
$request = new WP_REST_Request( 'POST', '/wp/v2/template-parts/' . $id );

$changes = new stdClass();
$changes->post_content = '<!-- wp:tests/anchor-block -->Hello<!-- /wp:tests/anchor-block -->';

$post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request );
$this->assertSame(
'<!-- wp:tests/anchor-block {"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->Hello<!-- /wp:tests/anchor-block -->',
$post->post_content,
'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.'
);
}
}
Loading
Loading