Commit be13dcdb4f for woocommerce
commit be13dcdb4f7c75d124ea76b062c3bf6d0eecc89a
Author: Rostislav Wolný <1082140+costasovo@users.noreply.github.com>
Date: Fri Jan 16 16:37:46 2026 +0100
Disable email editor renderer margin support (#62794)
* Remove margin from generate styles
* Remove margin inlined in text blocks styles
* Add docs for filter allowing disabling styles props
* Add tests checking margins are not rendered
* Fix error log being printed when running tests
The error is expected because we test an error state,
but we can hide it from test output to prevent confusion.
* Add change log
diff --git a/packages/php/email-editor/README.md b/packages/php/email-editor/README.md
index 4f487c4895..738ddfe0b9 100644
--- a/packages/php/email-editor/README.md
+++ b/packages/php/email-editor/README.md
@@ -99,12 +99,13 @@ We may add, update and delete any of them.
| `woocommerce_email_editor_post_sent_status_args` | `Array` `sent` post status args | `Array` register_post_status args | Allows update of the argument for the sent post status |
| `woocommerce_email_blocks_renderer_parsed_blocks` | `Array` Parsed blocks data | `Array` Parsed blocks data | You can modify the parsed blocks before they are processed by email renderer. |
| `woocommerce_email_editor_rendering_email_context` | `Array` $email_context | `Array` $email_context | Applied during email rendering to provide context data (e.g., `recipient_email`, `user_id`, `order_id`) to block renderers. |
-| `woocommerce_email_editor_send_preview_email_rendered_data` | `string` $data Rendered email, `WP_Post` $post | `string` Rendered email | Allows modifying the rendered email when displaying or sending it in preview |
+| `woocommerce_email_editor_send_preview_email_rendered_data` | `string` $data Rendered email, `WP_Post` $post | `string` Rendered email | Allows modifying the rendered email when displaying or sending it in preview |
| `woocommerce_email_editor_send_preview_email_personalizer_context` | `string` $content_styles, `WP_Post` $post` $personalizer_context | `Array` Personalizer context data | Allows modifying the personalizer context data for the send preview email function |
| `woocommerce_email_editor_synced_site_styles` | `Array` $synced_data, `Array` $site_data | `Array` Modified synced data | Used to filter the synced site style data before applying to email theme. |
| `woocommerce_email_editor_site_style_sync_enabled` | `bool` $enabled | `bool` | Use to control whether site style sync functionality is enabled or disabled. Returning `false` will disable site theme sync. |
| `woocommerce_email_editor_allowed_iframe_style_handles` | `Array` $allowed_iframe_style_handles | `Array` $allowed_iframe_style_handles | Filter the list of allowed stylesheet handles in the editor iframe. |
| `woocommerce_email_editor_script_localization_data` | `Array` $localization_data | `Array` $localization_data | Use to modify inlined JavaScript variables used by Email Editor client. |
+| `woocommerce_email_editor_styles_unsupported_props` | `Array` $unsupported_props | `Array` $unsupported_props | Filter the list of unsupported style properties (as nested key paths) that will be removed before block styles are converted to CSS. |
## Logging
diff --git a/packages/php/email-editor/changelog/fix-disable-email-editor-margin-support b/packages/php/email-editor/changelog/fix-disable-email-editor-margin-support
new file mode 100644
index 0000000000..dc8405d421
--- /dev/null
+++ b/packages/php/email-editor/changelog/fix-disable-email-editor-margin-support
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Prevent rendering of CSS margins in email HTML output
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
index 9e5a7b59f6..c2c98c03c4 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-text.php
@@ -116,15 +116,18 @@ class Text extends Abstract_Block_Renderer {
$element_style_value = $html->get_attribute( 'style' );
$element_style = isset( $element_style_value ) ? strval( $element_style_value ) : '';
// Padding may contain value like 10px or variable like var(--spacing-10).
- $element_style = preg_replace( '/padding[^:]*:.?[0-9a-z-()]+;?/', '', $element_style );
+ $element_style = (string) preg_replace( '/padding[^:]*:.?[0-9a-z-()]+;?/', '', $element_style );
+
+ // Margin is not supported in email renderer, so we need to remove it.
+ $element_style = (string) preg_replace( '/margin[^:]*:.?[0-9a-z-()]+;?/', '', $element_style );
// Remove border styles. We apply border styles on the wrapping table cell.
- $element_style = preg_replace( '/border[^:]*:.?[0-9a-z-()#]+;?/', '', strval( $element_style ) );
+ $element_style = (string) preg_replace( '/border[^:]*:.?[0-9a-z-()#]+;?/', '', $element_style );
// We define the font-size on the wrapper element, but we need to keep font-size definition here
// to prevent CSS Inliner from adding a default value and overriding the value set by user, which is on the wrapper element.
// The value provided by WP uses clamp() function which is not supported in many email clients.
- $element_style = preg_replace( '/font-size:[^;]+;?/', 'font-size: inherit;', strval( $element_style ) );
+ $element_style = (string) preg_replace( '/font-size:[^;]+;?/', 'font-size: inherit;', $element_style );
/** @var string $element_style */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort -- used for phpstan
$html->set_attribute( 'style', esc_attr( $element_style ) );
$block_content = $html->get_updated_html();
diff --git a/packages/php/email-editor/src/Integrations/Utils/class-styles-helper.php b/packages/php/email-editor/src/Integrations/Utils/class-styles-helper.php
index d43e25decc..40e7c5c600 100644
--- a/packages/php/email-editor/src/Integrations/Utils/class-styles-helper.php
+++ b/packages/php/email-editor/src/Integrations/Utils/class-styles-helper.php
@@ -120,6 +120,32 @@ class Styles_Helper {
* }
*/
public static function get_styles_from_block( array $block_styles, $skip_convert_vars = false ) {
+ $unsupported_props = array(
+ 'margin' => array( 'spacing', 'margin' ),
+ );
+ $unsupported_props = apply_filters( 'woocommerce_email_editor_styles_unsupported_props', $unsupported_props );
+ foreach ( $unsupported_props as $path ) {
+ if ( ! is_array( $path ) || count( $path ) === 0 ) {
+ continue;
+ }
+
+ $pointer = & $block_styles;
+ $last_key = array_pop( $path );
+
+ foreach ( $path as $segment ) {
+ if ( ! is_string( $segment ) && ! is_int( $segment ) ) {
+ continue 2;
+ }
+ if ( ! array_key_exists( $segment, $pointer ) || ! is_array( $pointer[ $segment ] ) ) {
+ continue 2;
+ }
+ $pointer = & $pointer[ $segment ];
+ }
+
+ if ( ( is_string( $last_key ) || is_int( $last_key ) ) && array_key_exists( $last_key, $pointer ) ) {
+ unset( $pointer[ $last_key ] );
+ }
+ }
return wp_parse_args(
wp_style_engine_get_styles( $block_styles, array( 'convert_vars_to_classnames' => $skip_convert_vars ) ),
self::$empty_block_styles
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Heading_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Heading_Test.php
index 9d0263bd0e..58eaa2b729 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Heading_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Heading_Test.php
@@ -109,6 +109,31 @@ class Heading_Test extends \Email_Editor_Integration_Test_Case {
$this->assertStringContainsString( 'font-size:24px', $rendered );
}
+ /**
+ * Test it removes inline margin styles (not supported in email renderer).
+ */
+ public function testItRemovesInlineMarginStyles(): void {
+ $content = '<h1 style="margin-top:10px;margin-bottom:12px;">This is Heading 1</h1>';
+ $parsed_heading = $this->parsed_heading;
+ $parsed_heading['innerHTML'] = $content;
+ $parsed_heading['innerContent'] = array( $content );
+
+ $rendered = $this->heading_renderer->render( $content, $parsed_heading, $this->rendering_context );
+ $html = new \WP_HTML_Tag_Processor( $rendered );
+ $html->next_tag( array( 'tag_name' => 'h1' ) );
+
+ $heading_style = $html->get_attribute( 'style' );
+ $this->assertIsString( $heading_style );
+ $this->assertStringNotContainsString( 'margin', $heading_style );
+
+ // Margin styles should also not leak to the wrapper table cell.
+ $html = new \WP_HTML_Tag_Processor( $rendered );
+ $html->next_tag( array( 'tag_name' => 'td' ) );
+ $table_cell_style = $html->get_attribute( 'style' );
+ $this->assertIsString( $table_cell_style );
+ $this->assertStringNotContainsString( 'margin', $table_cell_style );
+ }
+
/**
* Test it uses inherited color from email_attrs when no color is specified
*/
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Paragraph_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Paragraph_Test.php
index 982b73089d..8962fb082a 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Paragraph_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Paragraph_Test.php
@@ -132,6 +132,31 @@ class Paragraph_Test extends \Email_Editor_Integration_Test_Case {
$this->assertStringNotContainsString( 'border', $paragraph_style );
}
+ /**
+ * Test it removes inline margin styles (not supported in email renderer).
+ */
+ public function testItRemovesInlineMarginStyles(): void {
+ $content = '<p style="margin-top:10px;margin-bottom:12px;">Lorem Ipsum</p>';
+ $parsed_paragraph = $this->parsed_paragraph;
+ $parsed_paragraph['innerHTML'] = $content;
+ $parsed_paragraph['innerContent'] = array( $content );
+
+ $rendered = $this->paragraph_renderer->render( $content, $parsed_paragraph, $this->rendering_context );
+ $html = new \WP_HTML_Tag_Processor( $rendered );
+ $html->next_tag( array( 'tag_name' => 'p' ) );
+
+ $paragraph_style = $html->get_attribute( 'style' );
+ $this->assertIsString( $paragraph_style );
+ $this->assertStringNotContainsString( 'margin', $paragraph_style );
+
+ // Margin styles should also not leak to the wrapper table cell.
+ $html = new \WP_HTML_Tag_Processor( $rendered );
+ $html->next_tag( array( 'tag_name' => 'td' ) );
+ $table_cell_style = $html->get_attribute( 'style' );
+ $this->assertIsString( $table_cell_style );
+ $this->assertStringNotContainsString( 'margin', $table_cell_style );
+ }
+
/**
* Test it converts block typography
*/
diff --git a/packages/php/email-editor/tests/unit/Engine/Renderer/Html2Text_Test.php b/packages/php/email-editor/tests/unit/Engine/Renderer/Html2Text_Test.php
index 3529e59bb9..3aa97cd556 100644
--- a/packages/php/email-editor/tests/unit/Engine/Renderer/Html2Text_Test.php
+++ b/packages/php/email-editor/tests/unit/Engine/Renderer/Html2Text_Test.php
@@ -151,10 +151,19 @@ class Html2Text_Test extends \Email_Editor_Unit_Test {
* Test invalid option throws exception
*/
public function test_invalid_option_throws_exception(): void {
- $this->expectException( \InvalidArgumentException::class );
- $this->expectExceptionMessage( 'Invalid option provided for html2text conversion.' );
-
- Html2Text::convert( '<p>Test</p>', array( 'invalid_option' => true ) );
+ $previous_error_log = ini_get( 'error_log' );
+ ini_set( 'error_log', '/dev/null' ); // phpcs:ignore WordPress.PHP.IniSet.Risky -- this is to prevent the error from outputting to the screen.
+
+ try {
+ $this->expectException( \InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Invalid option provided for html2text conversion.' );
+
+ Html2Text::convert( '<p>Test</p>', array( 'invalid_option' => true ) );
+ } finally {
+ if ( false !== $previous_error_log ) {
+ ini_set( 'error_log', (string) $previous_error_log ); // phpcs:ignore WordPress.PHP.IniSet.Risky -- restore the previous value.
+ }
+ }
}
/**
diff --git a/packages/php/email-editor/tests/unit/Integrations/Utils/Styles_Helper_Test.php b/packages/php/email-editor/tests/unit/Integrations/Utils/Styles_Helper_Test.php
index 54e1f26a08..eb0695c801 100644
--- a/packages/php/email-editor/tests/unit/Integrations/Utils/Styles_Helper_Test.php
+++ b/packages/php/email-editor/tests/unit/Integrations/Utils/Styles_Helper_Test.php
@@ -260,6 +260,50 @@ class Styles_Helper_Test extends \Email_Editor_Unit_Test {
$this->assertSame( $expected, $result );
}
+ /**
+ * Test it can unset unsupported props using variable depth paths.
+ */
+ public function testItUnsetsUnsupportedPropsWithVariableDepthPaths(): void {
+ global $wp_filters, $__email_editor_last_wp_style_engine_get_styles_call;
+ $wp_filters = array();
+ $__email_editor_last_wp_style_engine_get_styles_call = null;
+
+ add_filter(
+ 'woocommerce_email_editor_styles_unsupported_props',
+ function ( $unsupported_props ) {
+ $unsupported_props['padding-top'] = array( 'spacing', 'padding', 'top' );
+ return $unsupported_props;
+ }
+ );
+
+ $block_styles = array(
+ 'spacing' => array(
+ 'padding' => array(
+ 'top' => '12px',
+ 'bottom' => '8px',
+ ),
+ 'margin' => array(
+ 'top' => '10px',
+ ),
+ ),
+ );
+
+ Styles_Helper::get_styles_from_block( $block_styles );
+
+ $this->assertIsArray( $__email_editor_last_wp_style_engine_get_styles_call );
+ $this->assertArrayHasKey( 'block_styles', $__email_editor_last_wp_style_engine_get_styles_call );
+ $passed_block_styles = $__email_editor_last_wp_style_engine_get_styles_call['block_styles'];
+
+ // Default behavior: margin is removed.
+ $this->assertArrayHasKey( 'spacing', $passed_block_styles );
+ $this->assertArrayNotHasKey( 'margin', $passed_block_styles['spacing'] );
+
+ // New behavior: deeper paths can be unset too.
+ $this->assertArrayHasKey( 'padding', $passed_block_styles['spacing'] );
+ $this->assertArrayNotHasKey( 'top', $passed_block_styles['spacing']['padding'] );
+ $this->assertSame( '8px', $passed_block_styles['spacing']['padding']['bottom'] );
+ }
+
/**
* Test it extends block styles with CSS declarations.
*/
diff --git a/packages/php/email-editor/tests/unit/stubs.php b/packages/php/email-editor/tests/unit/stubs.php
index 260066e3bb..af8677c2fc 100644
--- a/packages/php/email-editor/tests/unit/stubs.php
+++ b/packages/php/email-editor/tests/unit/stubs.php
@@ -40,9 +40,17 @@ if ( ! function_exists( 'wp_style_engine_get_styles' ) ) {
* Mock wp_style_engine_get_styles function.
*
* @param array $block_styles Array of block styles.
+ * @param array $options Optional. Style engine options.
* @return array
*/
- function wp_style_engine_get_styles( $block_styles ) {
+ function wp_style_engine_get_styles( $block_styles, $options = array() ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ // Capture last call for assertions in unit tests.
+ global $__email_editor_last_wp_style_engine_get_styles_call;
+ $__email_editor_last_wp_style_engine_get_styles_call = array(
+ 'block_styles' => $block_styles,
+ 'options' => $options,
+ );
+
// Return empty structure for empty input.
if ( empty( $block_styles ) ) {
return array(