2

I'm trying to manipulate the editor content(blocks) before it's shown in the editor. So ideally I would perform a search and replace and the editor will have a modified version of blocks, but will not be saved until the user clicks "Save".

Is there a filter for post content?

I think this should be done in REST, but I have no luck finding it.

Any help is highly appreciated.

3
  • You should also upvote the answer if it solves your problem. ;)
    – shramee
    Feb 27, 2022 at 7:13
  • 1
    @shramee It's not allowed :)) as it says:"You can't vote for your own post" Mar 2, 2022 at 10:24
  • Aah of course 🤦‍♂️ Voted both up for ya ;)
    – shramee
    Mar 3, 2022 at 7:58

2 Answers 2

3

I found that there is no way to change the data which is coming to editor when it is loaded. But it's possible to replace the data after that.

JS: script.js

wp.domReady(function () {
    const newData = window.myNewBlocksData;

    if (newData) {
        wp.data.dispatch('core/block-editor').resetBlocks(wp.blocks.parse(newData));
        console.log('replaced');
    }
}) 

PHP:

<?php

class MyBlocksManipulation
{
    public $prefix = 'my';

    public function __construct()
    {
        add_action('admin_enqueue_scripts', [$this, 'newDataType'], 999);
        add_action('save_post', [$this, 'markPostManipulationDone'], 999, 3);
    }

    public function newDataType()
    {
        $screen = get_current_screen();

        if (!(
            $screen !== null &&
            !empty($screen->is_block_editor) &&
            !empty($screen->base) &&
            $screen->base === 'post'
        )) {
            return;
        }

        $currentPost = get_post();
        $alreadyManipulated = get_post_meta($currentPost->ID, "{$this->prefix}_data_is_modified", true);

        if (!empty($alreadyManipulated)) {
            return;
        }

        $newData = wp_slash($this->modifyData());

        wp_add_inline_script('wp-editor', "var myNewBlocksData=`{$newData}`");

        wp_enqueue_script(
            'my-data-manipulation',
            'path/to/my/script.js',
            ['wp-editor'],
            false,
            true
        );
    }

    public function modifyData()
    {
        $currentPost = get_post();
        $content = $currentPost->post_content;

        return preg_replace_callback("<!-- wp:(.*) ({(.*)}) \/-->", function ($matches) {
            if (!empty($matches[2])) {
                $array = self::decode_block_attributes($matches[2]);


                // ...
                // Do something with this $array ...
                // ...


                $block = new WP_Block_Parser_Block($matches[1], $array, [], '', []);

                $serialized = serialize_block((array)$block);

                $serialized = trim($serialized, '<>');

                return $serialized;
            }

            return "<$matches[0]>";
        }, $content);
    }

    public static function is_json($item)
    {
        if (is_string($item)) {
            $json = json_decode($item, true);

            return is_array($json) && json_last_error() == JSON_ERROR_NONE;
        }

        return false;
    }

    public static function decode_block_attributes($item)
    {
        if (self::is_json($item) || is_array($item)) {
            $json = is_array($item) ? $item : json_decode($item, true);

            return $json;
        }

        return $item;
    }

    public function markPostManipulationDone($postId, $post, $update)
    {
        if (
            !$update ||
            $post->post_type === 'revision' ||
            !current_user_can('edit_post', $postId)
        ) {
            return $postId;
        }

        return update_post_meta($postId, "{$this->prefix}_data_is_modified", current_time('U'));
    }
}

new MyBlocksManipulation(); // Somwhere on top, before the theme or plugin content is rendered.
2
  • Slightly confused; how is the newDataType method ever called? Should the action added to admin_enqueue_scripts actually be [$this, 'newDataType']? Thanks for clarifying. Oct 31, 2023 at 10:10
  • @GlenWhitney, yes, you're right. I typed the wrong method name in the callback. Corrected. Nov 14, 2023 at 20:48
1

I've just worked on a solution for this for a friend. The Gutenburg editor calls on its internal REST API that unfortunately doesn't provide much room for manipulation.

Luckily, there's a filter called rest_post_dispatch. Tapping into this, you are able to modify the content before it's rendered out in the API.

With the generic API filter we can check if we have the necessary data to apply a filter. If we do, we pass the data to a custom filter that we've made called rest_post_content:

add_filter('rest_post_dispatch', function (WP_Rest_Response $object) {
    if (
        !property_exists($object, 'data')
        || !array_key_exists('content', $object->data)
        || !array_key_exists('type', $object->data)
        || !array_key_exists('id', $object->data)
    ) {
        return $object;
    }

    $object->data['content'] = apply_filters(
        'rest_post_content',
        $object->data['content'],
        $object->data['type'],
        $object->data['id']
    );

    return $object;
}, 10, 1);

Our custom rest_post_content then receives the post content array, post type and post ID for further manipulation:

add_filter('rest_post_content', function (array $content, string $type, int $post_id) {

    $content['raw'] = 'Updated content!';
    
    return $content;
}, 10, 3);

Hope this is useful!

1
  • 'rest_post_content' -- must have a prefix to avoid future conflicts with WP core. Using 'rest_post_dispatch' is nice, but how do we limit it to currently loaded post from the editor? If I call the API externally the content will still be processed. Actually, it always will be processed. The problem is that we don't have a filter that is used only by the editor and ignored in other API requests. Still a good start, thanks for sharing your solution. Nov 22, 2023 at 19:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.