diff --git a/.changeset/fix-gutenberg-dollar-sign.md b/.changeset/fix-gutenberg-dollar-sign.md new file mode 100644 index 000000000..d5e069e73 --- /dev/null +++ b/.changeset/fix-gutenberg-dollar-sign.md @@ -0,0 +1,6 @@ +--- +"@headstartwp/headstartwp": patch +--- + +Fix Gutenberg block attribute rendering when JSON attribute values contain dollar signs (for example, `$50 million`), preventing incorrect replacement escaping. + diff --git a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php index 6b3525967..a2454a2e0 100644 --- a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php +++ b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php @@ -320,7 +320,8 @@ public function process_block_with_dom_document_api( $html, $block_name, $block_ public function set_block_attributes_tag_api( $placeholder, $html, $block_attrs_serialized ) { $search = sprintf( '/data-wp-block="%s"/', preg_quote( $placeholder, '/' ) ); $replace = sprintf( 'data-wp-block="%s"', htmlspecialchars( $block_attrs_serialized ) ); - + // Escape backslashes and dollar signs for the replacement string + $replace = str_replace( [ '\\', '$' ], [ '\\\\', '\\$' ], $replace ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return preg_replace( $search, diff --git a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php index ab39924ce..8df621af5 100644 --- a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php +++ b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php @@ -152,6 +152,34 @@ public function test_render_classic_block() { ); } + /** + * Tests HTML_Tag_Processor attribute injection does not drop `$...` sequences + * in serialized JSON string values (e.g. "$50 million"). + * + * This specifically covers the preg_replace() replacement-string escaping logic + * used by set_block_attributes_tag_api(). + * + * @return void + */ + public function test_set_block_attributes_tag_api_preserves_dollar_signs() { + $placeholder = '___HEADSTARTWP_BLOCK_ATTRS___'; + $html = '

'; + $attrs_json = wp_json_encode( + [ + 'content' => '$50 million', + 'level' => 2, + ] + ); + + $enhanced = $this->parser->set_block_attributes_tag_api( $placeholder, $html, $attrs_json ); + + // set_block_attributes_tag_api() only replaces the `data-wp-block` placeholder value. + $this->assertStringNotContainsString( $placeholder, $enhanced ); + $this->assertStringContainsString( 'data-wp-block="', $enhanced ); + // The core assertion: `$50` should not be interpreted by preg_replace() replacement parsing. + $this->assertStringContainsString( '$50 million', $enhanced ); + } + /** * Test to ensure the parser handles both HTML and Multi-byte encodings properly * @@ -435,6 +463,14 @@ public function block_roundtrip_data_provider() { ], '

', ], + 'block value containing dollar sign in JSON string' => [ + 'core/heading', + [ + 'x' => '$50 million', + 'level' => 2, + ], + '

', + ], 'html_entities' => [ 'core/heading', [