Make WordPress Core

Changeset 57790


Ignore:
Timestamp:
03/07/2024 02:10:31 PM (3 months ago)
Author:
Bernhard Reiter
Message:

Block Hooks: Use new Templates Controller filter instead of action.

This changeset adds a new rest_pre_insert_{$this->post_type} filter in the WP_REST_Templates_Controller, where it is applied to the return value of the prepare_item_for_database method. (This is consistent with the WP_REST_Post_Controller, where that filter has existed before.)

The new filter is then used to inject hooked blocks into the template (or template part) content received via the endpoint, prior to persisting it to the database.

This supersedes the previous mechanism, which was using the rest_after_insert_{$this->post_type} action, from which it performed an additional wp_update_post call to update the template (part) content with the hooked blocks injected. The new technique eschews that additional call and the resulting extra revision it created, as well as a problem with regard to duplicated escaping and sanitization, which had caused some special characters to be garbled.

Props tomjcafferkey, gziolo, swissspidy, karolmanijak.
Fixes #60671.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-template-utils.php

    r57771 r57790  
    14331433    return $template_hierarchy;
    14341434}
     1435
    14351436/**
    14361437 * Inject ignoredHookedBlocks metadata attributes into a template or template part.
    14371438 *
    1438  * Given a `wp_template` or `wp_template_part` post object, locate all blocks that have
     1439 * Given an object that represents a `wp_template` or `wp_template_part` post object
     1440 * prepared for inserting or updating the database, locate all blocks that have
    14391441 * hooked blocks, and inject a `metadata.ignoredHookedBlocks` attribute into the anchor
    14401442 * blocks to reflect the latter.
    14411443 *
    1442  * @param WP_Post $post A post object with post type set to `wp_template` or `wp_template_part`.
    1443  * @return WP_Post The updated post object.
    1444  */
    1445 function inject_ignored_hooked_blocks_metadata_attributes( $post ) {
     1444 * @since 6.5.0
     1445 * @access private
     1446 *
     1447 * @param stdClass        $post    An object representing a template or template part
     1448 *                                 prepared for inserting or updating the database.
     1449 * @param WP_REST_Request $request Request object.
     1450 * @return stdClass The updated object representing a template or template part.
     1451 */
     1452function inject_ignored_hooked_blocks_metadata_attributes( $post, $request ) {
     1453    $filter_name = current_filter();
     1454    if ( ! str_starts_with( $filter_name, 'rest_pre_insert_' ) ) {
     1455        return $post;
     1456    }
     1457    $post_type = str_replace( 'rest_pre_insert_', '', $filter_name );
     1458
    14461459    $hooked_blocks = get_hooked_blocks();
    14471460    if ( empty( $hooked_blocks ) && ! has_filter( 'hooked_block_types' ) ) {
    1448         return;
     1461        return $post;
    14491462    }
    14501463
     
    14531466    // To that end, we need to suppress hooked blocks from getting inserted into the template.
    14541467    add_filter( 'hooked_block_types', '__return_empty_array', 99999, 0 );
    1455     $template = _build_block_template_result_from_post( $post );
     1468    $template = $request['id'] ? get_block_template( $request['id'], $post_type ) : null;
    14561469    remove_filter( 'hooked_block_types', '__return_empty_array', 99999 );
    14571470
     
    14591472    $after_block_visitor  = make_after_block_visitor( $hooked_blocks, $template, 'set_ignored_hooked_blocks_metadata' );
    14601473
    1461     $blocks  = parse_blocks( $template->content );
     1474    $blocks  = parse_blocks( $post->post_content );
    14621475    $content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
    14631476
    1464     wp_update_post(
    1465         array(
    1466             'ID'           => $post->ID,
    1467             'post_content' => $content,
    1468         )
    1469     );
    1470 }
     1477    $post->post_content = $content;
     1478    return $post;
     1479}
  • trunk/src/wp-includes/default-filters.php

    r57627 r57790  
    753753add_action( 'init', '_wp_register_default_font_collections' );
    754754
    755 // It might be nice to use a filter instead of an action, but the `WP_REST_Templates_Controller` doesn't
    756 // provide one (unlike e.g. `WP_REST_Posts_Controller`, which has `rest_pre_insert_{$this->post_type}`).
    757 add_action( 'rest_after_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 3 );
    758 add_action( 'rest_after_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 3 );
     755// Add ignoredHookedBlocks metadata attribute to the template and template part post types.
     756add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 );
     757add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 );
    759758
    760759unset( $filter, $action );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php

    r57374 r57790  
    620620        }
    621621
    622         return $changes;
     622        /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
     623        return apply_filters( "rest_pre_insert_{$this->post_type}", $changes, $request );
    623624    }
    624625
  • trunk/tests/phpunit/tests/block-template-utils.php

    r56983 r57790  
    8888
    8989    /**
     90     * Tear down after each test.
     91     *
     92     * @since 6.5.0
     93     */
     94    public function tear_down() {
     95        global $wp_current_filter;
     96
     97        if (
     98            'rest_pre_insert_wp_template' === current_filter() ||
     99            'rest_pre_insert_wp_template_part' === current_filter()
     100        ) {
     101            array_pop( $wp_current_filter );
     102        }
     103
     104        if ( WP_Block_Type_Registry::get_instance()->is_registered( 'tests/hooked-block' ) ) {
     105            unregister_block_type( 'tests/hooked-block' );
     106        }
     107
     108        parent::tear_down();
     109    }
     110
     111    /**
    90112     * @ticket 59338
    91113     *
     
    391413        $this->assertTrue( $has_html_files, 'contains at least one html file' );
    392414    }
     415
     416    /**
     417     * @ticket 60671
     418     *
     419     * @covers inject_ignored_hooked_blocks_metadata_attributes
     420     */
     421    public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template() {
     422        global $wp_current_filter;
     423        // Mock currently set filter.
     424        $wp_current_filter[] = 'rest_pre_insert_wp_template';
     425
     426        register_block_type(
     427            'tests/hooked-block',
     428            array(
     429                'block_hooks' => array(
     430                    'tests/anchor-block' => 'after',
     431                ),
     432            )
     433        );
     434
     435        $id      = self::TEST_THEME . '//' . 'my_template';
     436        $request = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $id );
     437
     438        $changes               = new stdClass();
     439        $changes->post_content = '<!-- wp:tests/anchor-block -->Hello<!-- /wp:tests/anchor-block -->';
     440
     441        $post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request );
     442        $this->assertSame(
     443            '<!-- wp:tests/anchor-block {"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->Hello<!-- /wp:tests/anchor-block -->',
     444            $post->post_content,
     445            'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.'
     446        );
     447    }
     448
     449    /**
     450     * @ticket 60671
     451     *
     452     * @covers inject_ignored_hooked_blocks_metadata_attributes
     453     */
     454    public function test_inject_ignored_hooked_blocks_metadata_attributes_into_template_part() {
     455        global $wp_current_filter;
     456        // Mock currently set filter.
     457        $wp_current_filter[] = 'rest_pre_insert_wp_template_part';
     458
     459        register_block_type(
     460            'tests/hooked-block',
     461            array(
     462                'block_hooks' => array(
     463                    'tests/anchor-block' => 'after',
     464                ),
     465            )
     466        );
     467
     468        $id      = self::TEST_THEME . '//' . 'my_template_part';
     469        $request = new WP_REST_Request( 'POST', '/wp/v2/template-parts/' . $id );
     470
     471        $changes               = new stdClass();
     472        $changes->post_content = '<!-- wp:tests/anchor-block -->Hello<!-- /wp:tests/anchor-block -->';
     473
     474        $post = inject_ignored_hooked_blocks_metadata_attributes( $changes, $request );
     475        $this->assertSame(
     476            '<!-- wp:tests/anchor-block {"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->Hello<!-- /wp:tests/anchor-block -->',
     477            $post->post_content,
     478            'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.'
     479        );
     480    }
    393481}
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplatesController.php

    r57366 r57790  
    4848    public static function wpTearDownAfterClass() {
    4949        wp_delete_post( self::$post->ID );
     50    }
     51
     52    /**
     53     * Tear down after each test.
     54     *
     55     * @since 6.5.0
     56     */
     57    public function tear_down() {
     58        if ( has_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
     59            remove_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10 );
     60        }
     61        if ( WP_Block_Type_Registry::get_instance()->is_registered( 'tests/block' ) ) {
     62            unregister_block_type( 'tests/hooked-block' );
     63        }
     64
     65        parent::tear_down();
    5066    }
    5167
     
    912928        $this->assertEmpty( $prepared->post_content, 'The content was not correct in the prepared template part.' );
    913929    }
     930
     931    /**
     932     * @ticket 60671
     933     *
     934     * @covers WP_REST_Templates_Controller::prepare_item_for_database
     935     * @covers inject_ignored_hooked_blocks_metadata_attributes
     936     */
     937    public function test_prepare_item_for_database_injects_hooked_block() {
     938        register_block_type(
     939            'tests/hooked-block',
     940            array(
     941                'block_hooks' => array(
     942                    'tests/anchor-block' => 'after',
     943                ),
     944            )
     945        );
     946
     947        add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes', 10, 2 );
     948
     949        $endpoint = new WP_REST_Templates_Controller( 'wp_template_part' );
     950
     951        $prepare_item_for_database = new ReflectionMethod( $endpoint, 'prepare_item_for_database' );
     952        $prepare_item_for_database->setAccessible( true );
     953
     954        $body_params = array(
     955            'title'   => 'Untitled Template Part',
     956            'slug'    => 'untitled-template-part',
     957            'content' => '<!-- wp:tests/anchor-block -->Hello<!-- /wp:tests/anchor-block -->',
     958        );
     959
     960        $request = new WP_REST_Request( 'POST', '/wp/v2/template-parts' );
     961        $request->set_body_params( $body_params );
     962
     963        $prepared = $prepare_item_for_database->invoke( $endpoint, $request );
     964        $this->assertSame(
     965            '<!-- wp:tests/anchor-block {"metadata":{"ignoredHookedBlocks":["tests/hooked-block"]}} -->Hello<!-- /wp:tests/anchor-block -->',
     966            $prepared->post_content,
     967            'The hooked block was not injected into the anchor block\'s ignoredHookedBlocks metadata.'
     968        );
     969    }
    914970}
Note: See TracChangeset for help on using the changeset viewer.