Gutenberg Block manipulation: Undo parse_blocks() with serialize_blocks() results in unicode issues

As already described in the comments, many WordPress Gutenberg plugins do not use the strict JSON format as WordPress prescribes… (and poorly documented) So it comes to two problems:

  • Sometimes the data is in a JSON format, which parse_blocks() cannot
    read. If no change is made here, for example, the value attr will be
    NULL (example in the first post)
  • serialize_blocks() converts all special characters to Unicode, which causes the plugin block to stop working properly and results in error messages in the Gutenberg UI: “This block contains unexpected or invalid content.”.

Especially for the plugins Advanced-Gutenberg and Kadence-Blocks the bugs have already been reported. I have developed the following workaround, which is quick and dirty, but it works. If anyone has comments, additions or improvements, please let me know!

function change_post_data_before_save( $data ) {
    $post_data = $data['post_content'];
    $post_data = str_replace('\\"', '"', $post_data); //parse_blocks() need " to parse $blocks[]['attr'] correctly

    $blocks = parse_blocks($post_data);
    $post_content = implode( '', array_map( 'serialize_block2', $blocks ) ); //serialize_block() replacement because serialize_block_attributes() does not support JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE

    $post_content = str_replace('"', '\\"', $post_content); //undo " correction from above
    $data['post_content'] = $post_content;

    return $data;
}
add_filter( 'wp_insert_post_data', 'change_post_data_before_save', 10, 2 );


//only sub method calls has been changed
function serialize_block2( $block ) {
    $block_content="";

    $index = 0;
    foreach ( $block['innerContent'] as $chunk ) {
        $block_content .= is_string( $chunk ) ? $chunk : serialize_block2( $block['innerBlocks'][ $index++ ] ); //change
    }

    if ( ! is_array( $block['attrs'] ) ) {
        $block['attrs'] = array();
    }

    return get_comment_delimited_block_content2(
        $block['blockName'],
        $block['attrs'],
        $block_content
    );
}


//only sub method calls has been changed
function get_comment_delimited_block_content2( $block_name, $block_attributes, $block_content ) {
    if ( is_null( $block_name ) ) {
        return $block_content;
    }

    $serialized_block_name = strip_core_block_namespace( $block_name );
    $serialized_attributes = empty( $block_attributes ) ? '' : serialize_block_attributes2( $block_attributes ) . ' '; //change

    if ( empty( $block_content ) ) {
        return sprintf( '<!-- wp:%s %s/-->', $serialized_block_name, $serialized_attributes );
    }

    return sprintf(
        '<!-- wp:%s %s-->%s<!-- /wp:%s -->',
        $serialized_block_name,
        $serialized_attributes,
        $block_content,
        $serialized_block_name
    );
}


//change gutenberg json_encoding to keep plugin block formats and prevent issue "This block contains unexpected or invalid content."
function serialize_block_attributes2( $block_attributes ) {
    $encoded_attributes = json_encode( $block_attributes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); //change
    $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
    $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
    $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
    $encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
    // Regex: /\\"/
    $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );

    return $encoded_attributes;
}

Leave a Comment