diff --git a/packages/php/email-editor/src/Engine/Patterns/Library/class-abstract-pattern.php b/packages/php/email-editor/src/Engine/Patterns/Library/class-abstract-pattern.php index e005a5f04b..55c2a09ca4 100644 --- a/packages/php/email-editor/src/Engine/Patterns/Library/class-abstract-pattern.php +++ b/packages/php/email-editor/src/Engine/Patterns/Library/class-abstract-pattern.php @@ -5,39 +5,39 @@ namespace MailPoet\EmailEditor\Engine\Patterns\Library; use MailPoet\EmailEditor\Utils\Cdn_Asset_Url; abstract class Abstract_Pattern { - protected $cdnAssetUrl; - protected $blockTypes = []; - protected $templateTypes = []; - protected $inserter = true; - protected $source = 'plugin'; - protected $categories = ['mailpoet']; - protected $viewportWidth = 620; + protected $cdnAssetUrl; + protected $blockTypes = array(); + protected $templateTypes = array(); + protected $inserter = true; + protected $source = 'plugin'; + protected $categories = array( 'mailpoet' ); + protected $viewportWidth = 620; - public function __construct( - Cdn_Asset_Url $cdnAssetUrl - ) { - $this->cdnAssetUrl = $cdnAssetUrl; - } + public function __construct( + Cdn_Asset_Url $cdnAssetUrl + ) { + $this->cdnAssetUrl = $cdnAssetUrl; + } - public function getProperties() { - return [ - 'title' => $this->getTitle(), - 'content' => $this->getContent(), - 'description' => $this->getDescription(), - 'categories' => $this->categories, - 'inserter' => $this->inserter, - 'blockTypes' => $this->blockTypes, - 'templateTypes' => $this->templateTypes, - 'source' => $this->source, - 'viewportWidth' => $this->viewportWidth, - ]; - } + public function getProperties() { + return array( + 'title' => $this->getTitle(), + 'content' => $this->getContent(), + 'description' => $this->getDescription(), + 'categories' => $this->categories, + 'inserter' => $this->inserter, + 'blockTypes' => $this->blockTypes, + 'templateTypes' => $this->templateTypes, + 'source' => $this->source, + 'viewportWidth' => $this->viewportWidth, + ); + } - abstract protected function getContent(): string; + abstract protected function getContent(): string; - abstract protected function getTitle(): string; + abstract protected function getTitle(): string; - protected function getDescription(): string { - return ''; - } + protected function getDescription(): string { + return ''; + } } diff --git a/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content-full.php b/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content-full.php index 30eada0f28..c2fe519e96 100644 --- a/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content-full.php +++ b/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content-full.php @@ -3,45 +3,45 @@ namespace MailPoet\EmailEditor\Engine\Patterns\Library; class Default_Content_Full extends Abstract_Pattern { - protected $blockTypes = [ - 'core/post-content', - ]; + protected $blockTypes = array( + 'core/post-content', + ); - protected $templateTypes = [ - 'email-general-template', - ]; + protected $templateTypes = array( + 'email-general-template', + ); - protected function getContent(): string { - return ' + protected function getContent(): string { + return '
-
Your Logo
+
Your Logo
-

' . __('One column layout', 'mailpoet') . '

+

' . __( 'One column layout', 'mailpoet' ) . '

-
Banner Image
+
Banner Image
-

' . esc_html__('A one-column layout is great for simplified and concise content, like announcements or newsletters with brief updates. Drag blocks to add content and customize your styles from the styles panel on the top right.', 'mailpoet') . '

+

' . esc_html__( 'A one-column layout is great for simplified and concise content, like announcements or newsletters with brief updates. Drag blocks to add content and customize your styles from the styles panel on the top right.', 'mailpoet' ) . '

-

' . esc_html__('You received this email because you are subscribed to the [site:title]', 'mailpoet') . '

+

' . esc_html__( 'You received this email because you are subscribed to the [site:title]', 'mailpoet' ) . '

-

' . esc_html__('Unsubscribe', 'mailpoet') . ' | ' . esc_html__('Manage subscription', 'mailpoet') . '

+

' . esc_html__( 'Unsubscribe', 'mailpoet' ) . ' | ' . esc_html__( 'Manage subscription', 'mailpoet' ) . '

'; - } + } - protected function getTitle(): string { - return __('Default Email Content with Header and Footer', 'mailpoet'); - } + protected function getTitle(): string { + return __( 'Default Email Content with Header and Footer', 'mailpoet' ); + } } diff --git a/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content.php b/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content.php index 78dd0d2433..1f0019ed3e 100644 --- a/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content.php +++ b/packages/php/email-editor/src/Engine/Patterns/Library/class-default-content.php @@ -3,36 +3,36 @@ namespace MailPoet\EmailEditor\Engine\Patterns\Library; class Default_Content extends Abstract_Pattern { - protected $blockTypes = [ - 'core/post-content', - ]; + protected $blockTypes = array( + 'core/post-content', + ); - protected $templateTypes = [ - 'email-template', - ]; + protected $templateTypes = array( + 'email-template', + ); - protected function getContent(): string - return ' + protected function getContent(): string { + return '
-

' . __('One column layout', 'mailpoet') . '

+

' . __( 'One column layout', 'mailpoet' ) . '

-
Banner Image
+
Banner Image
-

' . esc_html__('A one-column layout is great for simplified and concise content, like announcements or newsletters with brief updates. Drag blocks to add content and customize your styles from the styles panel on the top right.', 'mailpoet') . '

+

' . esc_html__( 'A one-column layout is great for simplified and concise content, like announcements or newsletters with brief updates. Drag blocks to add content and customize your styles from the styles panel on the top right.', 'mailpoet' ) . '

'; - } + } - protected function getTitle(): string { - return __('Default Email Content', 'mailpoet'); - } + protected function getTitle(): string { + return __( 'Default Email Content', 'mailpoet' ); + } } diff --git a/packages/php/email-editor/src/Engine/Patterns/class-patterns.php b/packages/php/email-editor/src/Engine/Patterns/class-patterns.php index f4d578511d..d3e6d9b4a1 100644 --- a/packages/php/email-editor/src/Engine/Patterns/class-patterns.php +++ b/packages/php/email-editor/src/Engine/Patterns/class-patterns.php @@ -5,36 +5,36 @@ namespace MailPoet\EmailEditor\Engine\Patterns; use MailPoet\EmailEditor\Utils\Cdn_Asset_Url; class Patterns { - private $namespace = 'mailpoet'; - protected $cdnAssetUrl; + private $namespace = 'mailpoet'; + protected $cdnAssetUrl; - public function __construct( - Cdn_Asset_Url $cdnAssetUrl - ) { - $this->cdnAssetUrl = $cdnAssetUrl; - } + public function __construct( + Cdn_Asset_Url $cdnAssetUrl + ) { + $this->cdnAssetUrl = $cdnAssetUrl; + } - public function initialize(): void { - $this->registerBlockPatternCategory(); - $this->registerPatterns(); - } + public function initialize(): void { + $this->registerBlockPatternCategory(); + $this->registerPatterns(); + } - private function registerBlockPatternCategory() { - register_block_pattern_category( - 'mailpoet', - [ - 'label' => _x('MailPoet', 'Block pattern category', 'mailpoet'), - 'description' => __('A collection of email template layouts.', 'mailpoet'), - ] - ); - } + private function registerBlockPatternCategory() { + register_block_pattern_category( + 'mailpoet', + array( + 'label' => _x( 'MailPoet', 'Block pattern category', 'mailpoet' ), + 'description' => __( 'A collection of email template layouts.', 'mailpoet' ), + ) + ); + } - private function registerPatterns() { - $this->registerPattern('default', new Library\Default_Content($this->cdnAssetUrl)); - $this->registerPattern('default-full', new Library\Default_Content_Full($this->cdnAssetUrl)); - } + private function registerPatterns() { + $this->registerPattern( 'default', new Library\Default_Content( $this->cdnAssetUrl ) ); + $this->registerPattern( 'default-full', new Library\Default_Content_Full( $this->cdnAssetUrl ) ); + } - private function registerPattern($name, $pattern) { - register_block_pattern($this->namespace . '/' . $name, $pattern->getProperties()); - } + private function registerPattern( $name, $pattern ) { + register_block_pattern( $this->namespace . '/' . $name, $pattern->getProperties() ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Layout/class-flex-layout-renderer.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Layout/class-flex-layout-renderer.php index dcd37aec31..1997da523f 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Layout/class-flex-layout-renderer.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Layout/class-flex-layout-renderer.php @@ -8,89 +8,89 @@ use MailPoet\EmailEditor\Engine\Settings_Controller; * This class provides functionality to render inner blocks of a block that supports reduced flex layout. */ class Flex_Layout_Renderer { - public function renderInnerBlocksInLayout(array $parsedBlock, Settings_Controller $settingsController): string { - $themeStyles = $settingsController->getEmailStyles(); - $flexGap = $themeStyles['spacing']['blockGap'] ?? '0px'; - $flexGapNumber = $settingsController->parseNumberFromStringWithPixels($flexGap); + public function renderInnerBlocksInLayout( array $parsedBlock, Settings_Controller $settingsController ): string { + $themeStyles = $settingsController->getEmailStyles(); + $flexGap = $themeStyles['spacing']['blockGap'] ?? '0px'; + $flexGapNumber = $settingsController->parseNumberFromStringWithPixels( $flexGap ); - $marginTop = $parsedBlock['email_attrs']['margin-top'] ?? '0px'; - $justify = $parsedBlock['attrs']['layout']['justifyContent'] ?? 'left'; - $styles = wp_style_engine_get_styles($parsedBlock['attrs']['style'] ?? [])['css'] ?? ''; - $styles .= 'margin-top: ' . $marginTop . ';'; - $styles .= 'text-align: ' . $justify; + $marginTop = $parsedBlock['email_attrs']['margin-top'] ?? '0px'; + $justify = $parsedBlock['attrs']['layout']['justifyContent'] ?? 'left'; + $styles = wp_style_engine_get_styles( $parsedBlock['attrs']['style'] ?? array() )['css'] ?? ''; + $styles .= 'margin-top: ' . $marginTop . ';'; + $styles .= 'text-align: ' . $justify; - // MS Outlook doesn't support style attribute in divs so we conditionally wrap the buttons in a table and repeat styles - $outputHtml = sprintf( - ' + // MS Outlook doesn't support style attribute in divs so we conditionally wrap the buttons in a table and repeat styles + $outputHtml = sprintf( + '
', - esc_attr($styles), - esc_attr($justify) - ); + esc_attr( $styles ), + esc_attr( $justify ) + ); - $innerBlocks = $this->computeWidthsForFlexLayout($parsedBlock, $settingsController, $flexGapNumber); + $innerBlocks = $this->computeWidthsForFlexLayout( $parsedBlock, $settingsController, $flexGapNumber ); - foreach ($innerBlocks as $key => $block) { - $styles = []; - if ($block['email_attrs']['layout_width'] ?? null) { - $styles['width'] = $block['email_attrs']['layout_width']; - } - if ($key > 0) { - $styles['padding-left'] = $flexGap; - } - $outputHtml .= ''; - } - $outputHtml .= '
' . render_block($block) . '
+ foreach ( $innerBlocks as $key => $block ) { + $styles = array(); + if ( $block['email_attrs']['layout_width'] ?? null ) { + $styles['width'] = $block['email_attrs']['layout_width']; + } + if ( $key > 0 ) { + $styles['padding-left'] = $flexGap; + } + $outputHtml .= '' . render_block( $block ) . ''; + } + $outputHtml .= ' '; - return $outputHtml; - } + return $outputHtml; + } - private function computeWidthsForFlexLayout(array $parsedBlock, Settings_Controller $settingsController, float $flexGap): array { - // When there is no parent width we can't compute widths so auto width will be used - if (!isset($parsedBlock['email_attrs']['width'])) { - return $parsedBlock['innerBlocks'] ?? []; - } - $blocksCount = count($parsedBlock['innerBlocks']); - $totalUsedWidth = 0; // Total width assuming items without set width would consume proportional width - $parentWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width']); - $innerBlocks = $parsedBlock['innerBlocks'] ?? []; + private function computeWidthsForFlexLayout( array $parsedBlock, Settings_Controller $settingsController, float $flexGap ): array { + // When there is no parent width we can't compute widths so auto width will be used + if ( ! isset( $parsedBlock['email_attrs']['width'] ) ) { + return $parsedBlock['innerBlocks'] ?? array(); + } + $blocksCount = count( $parsedBlock['innerBlocks'] ); + $totalUsedWidth = 0; // Total width assuming items without set width would consume proportional width + $parentWidth = $settingsController->parseNumberFromStringWithPixels( $parsedBlock['email_attrs']['width'] ); + $innerBlocks = $parsedBlock['innerBlocks'] ?? array(); - foreach ($innerBlocks as $key => $block) { - $blockWidthPercent = ($block['attrs']['width'] ?? 0) ? intval($block['attrs']['width']) : 0; - $blockWidth = floor($parentWidth * ($blockWidthPercent / 100)); - // If width is not set, we assume it's 25% of the parent width - $totalUsedWidth += $blockWidth ?: floor($parentWidth * (25 / 100)); + foreach ( $innerBlocks as $key => $block ) { + $blockWidthPercent = ( $block['attrs']['width'] ?? 0 ) ? intval( $block['attrs']['width'] ) : 0; + $blockWidth = floor( $parentWidth * ( $blockWidthPercent / 100 ) ); + // If width is not set, we assume it's 25% of the parent width + $totalUsedWidth += $blockWidth ?: floor( $parentWidth * ( 25 / 100 ) ); - if (!$blockWidth) { - $innerBlocks[$key]['email_attrs']['layout_width'] = null; // Will be rendered as auto - continue; - } - $innerBlocks[$key]['email_attrs']['layout_width'] = $this->getWidthWithoutGap($blockWidth, $flexGap, $blockWidthPercent) . 'px'; - } + if ( ! $blockWidth ) { + $innerBlocks[ $key ]['email_attrs']['layout_width'] = null; // Will be rendered as auto + continue; + } + $innerBlocks[ $key ]['email_attrs']['layout_width'] = $this->getWidthWithoutGap( $blockWidth, $flexGap, $blockWidthPercent ) . 'px'; + } - // When there is only one block, or percentage is set reasonably we don't need to adjust and just render as set by user - if ($blocksCount <= 1 || ($totalUsedWidth <= $parentWidth)) { - return $innerBlocks; - } + // When there is only one block, or percentage is set reasonably we don't need to adjust and just render as set by user + if ( $blocksCount <= 1 || ( $totalUsedWidth <= $parentWidth ) ) { + return $innerBlocks; + } - foreach ($innerBlocks as $key => $block) { - $proportionalSpaceOverflow = $parentWidth / $totalUsedWidth; - $blockWidth = $block['email_attrs']['layout_width'] ? $settingsController->parseNumberFromStringWithPixels($block['email_attrs']['layout_width']) : 0; - $blockProportionalWidth = $blockWidth * $proportionalSpaceOverflow; - $blockProportionalPercentage = ($blockProportionalWidth / $parentWidth) * 100; - $innerBlocks[$key]['email_attrs']['layout_width'] = $blockWidth ? $this->getWidthWithoutGap($blockProportionalWidth, $flexGap, $blockProportionalPercentage) . 'px' : null; - } - return $innerBlocks; - } + foreach ( $innerBlocks as $key => $block ) { + $proportionalSpaceOverflow = $parentWidth / $totalUsedWidth; + $blockWidth = $block['email_attrs']['layout_width'] ? $settingsController->parseNumberFromStringWithPixels( $block['email_attrs']['layout_width'] ) : 0; + $blockProportionalWidth = $blockWidth * $proportionalSpaceOverflow; + $blockProportionalPercentage = ( $blockProportionalWidth / $parentWidth ) * 100; + $innerBlocks[ $key ]['email_attrs']['layout_width'] = $blockWidth ? $this->getWidthWithoutGap( $blockProportionalWidth, $flexGap, $blockProportionalPercentage ) . 'px' : null; + } + return $innerBlocks; + } - /** - * How much of width we will strip to keep some space for the gap - * This is computed based on CSS rule used in the editor: - * For block with width set to X percent - * width: calc(X% - (var(--wp--style--block-gap) * (100 - X)/100))); - */ - private function getWidthWithoutGap(float $blockWidth, float $flexGap, float $blockWidthPercent): int { - $widthGapReduction = $flexGap * ((100 - $blockWidthPercent) / 100); - return intval(floor($blockWidth - $widthGapReduction)); - } + /** + * How much of width we will strip to keep some space for the gap + * This is computed based on CSS rule used in the editor: + * For block with width set to X percent + * width: calc(X% - (var(--wp--style--block-gap) * (100 - X)/100))); + */ + private function getWidthWithoutGap( float $blockWidth, float $flexGap, float $blockWidthPercent ): int { + $widthGapReduction = $flexGap * ( ( 100 - $blockWidthPercent ) / 100 ); + return intval( floor( $blockWidth - $widthGapReduction ) ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-highlighting-postprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-highlighting-postprocessor.php index 8254477ef0..09e4bb5024 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-highlighting-postprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-highlighting-postprocessor.php @@ -6,11 +6,11 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Postprocessors; * This postprocessor replaces tags with tags because mark tags are not supported across all email clients */ class Highlighting_Postprocessor implements Postprocessor { - public function postprocess(string $html): string { - return str_replace( - [''], - [''], - $html - ); - } + public function postprocess( string $html ): string { + return str_replace( + array( '' ), + array( '' ), + $html + ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-variables-postprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-variables-postprocessor.php index f4d869658b..32fd1358e3 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-variables-postprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/class-variables-postprocessor.php @@ -10,37 +10,37 @@ use MailPoet\EmailEditor\Engine\Theme_Controller; * This postprocessor uses variables from theme.json and replaces the CSS variables with their values in final email HTML. */ class Variables_Postprocessor implements Postprocessor { - private Theme_Controller $themeController; + private Theme_Controller $themeController; - public function __construct( - Theme_Controller $themeController - ) { - $this->themeController = $themeController; - } + public function __construct( + Theme_Controller $themeController + ) { + $this->themeController = $themeController; + } - public function postprocess(string $html): string { - $variables = $this->themeController->getVariablesValuesMap(); - $replacements = []; + public function postprocess( string $html ): string { + $variables = $this->themeController->getVariablesValuesMap(); + $replacements = array(); - foreach ($variables as $varName => $varValue) { - $varPattern = '/' . preg_quote('var(' . $varName . ')', '/') . '/i'; - $replacements[$varPattern] = $varValue; - } + foreach ( $variables as $varName => $varValue ) { + $varPattern = '/' . preg_quote( 'var(' . $varName . ')', '/' ) . '/i'; + $replacements[ $varPattern ] = $varValue; + } - // Pattern to match style attributes and their values. - $callback = function ($matches) use ($replacements) { - // For each match, replace CSS variables with their values - $style = $matches[1]; - $style = preg_replace(array_keys($replacements), array_values($replacements), $style); - return 'style="' . esc_attr($style) . '"'; - }; + // Pattern to match style attributes and their values. + $callback = function ( $matches ) use ( $replacements ) { + // For each match, replace CSS variables with their values + $style = $matches[1]; + $style = preg_replace( array_keys( $replacements ), array_values( $replacements ), $style ); + return 'style="' . esc_attr( $style ) . '"'; + }; - // We want to replace the CSS variables only in the style attributes to avoid replacing the actual content. - $stylePattern = '/style="(.*?)"/i'; - $stylePatternAlt = "/style='(.*?)'/i"; - $html = (string)preg_replace_callback($stylePattern, $callback, $html); - $html = (string)preg_replace_callback($stylePatternAlt, $callback, $html); + // We want to replace the CSS variables only in the style attributes to avoid replacing the actual content. + $stylePattern = '/style="(.*?)"/i'; + $stylePatternAlt = "/style='(.*?)'/i"; + $html = (string) preg_replace_callback( $stylePattern, $callback, $html ); + $html = (string) preg_replace_callback( $stylePatternAlt, $callback, $html ); - return $html; - } + return $html; + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/interface-postprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/interface-postprocessor.php index 27c83cd3eb..eb9c809e1b 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/interface-postprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Postprocessors/interface-postprocessor.php @@ -3,5 +3,5 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Postprocessors; interface Postprocessor { - public function postprocess(string $html): string; + public function postprocess( string $html ): string; } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php index d794abb73f..f8d22ff15b 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php @@ -7,98 +7,98 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors; * The final width in pixels is stored in the email_attrs array because we would like to avoid changing the original attributes. */ class Blocks_Width_Preprocessor implements Preprocessor { - public function preprocess(array $parsedBlocks, array $layout, array $styles): array { - foreach ($parsedBlocks as $key => $block) { - // Layout width is recalculated for each block because full-width blocks don't exclude padding - $layoutWidth = $this->parseNumberFromStringWithPixels($layout['contentSize']); - $alignment = $block['attrs']['align'] ?? null; - // Subtract padding from the block width if it's not full-width - if ($alignment !== 'full') { - $layoutWidth -= $this->parseNumberFromStringWithPixels($styles['spacing']['padding']['left'] ?? '0px'); - $layoutWidth -= $this->parseNumberFromStringWithPixels($styles['spacing']['padding']['right'] ?? '0px'); - } + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array { + foreach ( $parsedBlocks as $key => $block ) { + // Layout width is recalculated for each block because full-width blocks don't exclude padding + $layoutWidth = $this->parseNumberFromStringWithPixels( $layout['contentSize'] ); + $alignment = $block['attrs']['align'] ?? null; + // Subtract padding from the block width if it's not full-width + if ( $alignment !== 'full' ) { + $layoutWidth -= $this->parseNumberFromStringWithPixels( $styles['spacing']['padding']['left'] ?? '0px' ); + $layoutWidth -= $this->parseNumberFromStringWithPixels( $styles['spacing']['padding']['right'] ?? '0px' ); + } - $widthInput = $block['attrs']['width'] ?? '100%'; - // Currently we support only % and px units in case only the number is provided we assume it's % - // because editor saves percent values as a number. - $widthInput = is_numeric($widthInput) ? "$widthInput%" : $widthInput; - $width = $this->convertWidthToPixels($widthInput, $layoutWidth); + $widthInput = $block['attrs']['width'] ?? '100%'; + // Currently we support only % and px units in case only the number is provided we assume it's % + // because editor saves percent values as a number. + $widthInput = is_numeric( $widthInput ) ? "$widthInput%" : $widthInput; + $width = $this->convertWidthToPixels( $widthInput, $layoutWidth ); - if ($block['blockName'] === 'core/columns') { - // Calculate width of the columns based on the layout width and padding - $columnsWidth = $layoutWidth; - $columnsWidth -= $this->parseNumberFromStringWithPixels($block['attrs']['style']['spacing']['padding']['left'] ?? '0px'); - $columnsWidth -= $this->parseNumberFromStringWithPixels($block['attrs']['style']['spacing']['padding']['right'] ?? '0px'); - $borderWidth = $block['attrs']['style']['border']['width'] ?? '0px'; - $columnsWidth -= $this->parseNumberFromStringWithPixels($block['attrs']['style']['border']['left']['width'] ?? $borderWidth); - $columnsWidth -= $this->parseNumberFromStringWithPixels($block['attrs']['style']['border']['right']['width'] ?? $borderWidth); - $block['innerBlocks'] = $this->addMissingColumnWidths($block['innerBlocks'], $columnsWidth); - } + if ( $block['blockName'] === 'core/columns' ) { + // Calculate width of the columns based on the layout width and padding + $columnsWidth = $layoutWidth; + $columnsWidth -= $this->parseNumberFromStringWithPixels( $block['attrs']['style']['spacing']['padding']['left'] ?? '0px' ); + $columnsWidth -= $this->parseNumberFromStringWithPixels( $block['attrs']['style']['spacing']['padding']['right'] ?? '0px' ); + $borderWidth = $block['attrs']['style']['border']['width'] ?? '0px'; + $columnsWidth -= $this->parseNumberFromStringWithPixels( $block['attrs']['style']['border']['left']['width'] ?? $borderWidth ); + $columnsWidth -= $this->parseNumberFromStringWithPixels( $block['attrs']['style']['border']['right']['width'] ?? $borderWidth ); + $block['innerBlocks'] = $this->addMissingColumnWidths( $block['innerBlocks'], $columnsWidth ); + } - // Copy layout styles and update width and padding - $modifiedLayout = $layout; - $modifiedLayout['contentSize'] = "{$width}px"; - $modifiedStyles = $styles; - $modifiedStyles['spacing']['padding']['left'] = $block['attrs']['style']['spacing']['padding']['left'] ?? '0px'; - $modifiedStyles['spacing']['padding']['right'] = $block['attrs']['style']['spacing']['padding']['right'] ?? '0px'; + // Copy layout styles and update width and padding + $modifiedLayout = $layout; + $modifiedLayout['contentSize'] = "{$width}px"; + $modifiedStyles = $styles; + $modifiedStyles['spacing']['padding']['left'] = $block['attrs']['style']['spacing']['padding']['left'] ?? '0px'; + $modifiedStyles['spacing']['padding']['right'] = $block['attrs']['style']['spacing']['padding']['right'] ?? '0px'; - $block['email_attrs']['width'] = "{$width}px"; - $block['innerBlocks'] = $this->preprocess($block['innerBlocks'], $modifiedLayout, $modifiedStyles); - $parsedBlocks[$key] = $block; - } - return $parsedBlocks; - } + $block['email_attrs']['width'] = "{$width}px"; + $block['innerBlocks'] = $this->preprocess( $block['innerBlocks'], $modifiedLayout, $modifiedStyles ); + $parsedBlocks[ $key ] = $block; + } + return $parsedBlocks; + } - // TODO: We could add support for other units like em, rem, etc. - private function convertWidthToPixels(string $currentWidth, float $layoutWidth): float { - $width = $layoutWidth; - if (strpos($currentWidth, '%') !== false) { - $width = (float)str_replace('%', '', $currentWidth); - $width = round($width / 100 * $layoutWidth); - } elseif (strpos($currentWidth, 'px') !== false) { - $width = $this->parseNumberFromStringWithPixels($currentWidth); - } + // TODO: We could add support for other units like em, rem, etc. + private function convertWidthToPixels( string $currentWidth, float $layoutWidth ): float { + $width = $layoutWidth; + if ( strpos( $currentWidth, '%' ) !== false ) { + $width = (float) str_replace( '%', '', $currentWidth ); + $width = round( $width / 100 * $layoutWidth ); + } elseif ( strpos( $currentWidth, 'px' ) !== false ) { + $width = $this->parseNumberFromStringWithPixels( $currentWidth ); + } - return $width; - } + return $width; + } - private function parseNumberFromStringWithPixels(string $string): float { - return (float)str_replace('px', '', $string); - } + private function parseNumberFromStringWithPixels( string $string ): float { + return (float) str_replace( 'px', '', $string ); + } - private function addMissingColumnWidths(array $columns, float $columnsWidth): array { - $columnsCountWithDefinedWidth = 0; - $definedColumnWidth = 0; - $columnsCount = count($columns); - foreach ($columns as $column) { - if (isset($column['attrs']['width']) && !empty($column['attrs']['width'])) { - $columnsCountWithDefinedWidth++; - $definedColumnWidth += $this->convertWidthToPixels($column['attrs']['width'], $columnsWidth); - } else { - // When width is not set we need to add padding to the defined column width for better ratio accuracy - $definedColumnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['spacing']['padding']['left'] ?? '0px'); - $definedColumnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['spacing']['padding']['right'] ?? '0px'); - $borderWidth = $column['attrs']['style']['border']['width'] ?? '0px'; - $definedColumnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['border']['left']['width'] ?? $borderWidth); - $definedColumnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['border']['right']['width'] ?? $borderWidth); - } - } + private function addMissingColumnWidths( array $columns, float $columnsWidth ): array { + $columnsCountWithDefinedWidth = 0; + $definedColumnWidth = 0; + $columnsCount = count( $columns ); + foreach ( $columns as $column ) { + if ( isset( $column['attrs']['width'] ) && ! empty( $column['attrs']['width'] ) ) { + ++$columnsCountWithDefinedWidth; + $definedColumnWidth += $this->convertWidthToPixels( $column['attrs']['width'], $columnsWidth ); + } else { + // When width is not set we need to add padding to the defined column width for better ratio accuracy + $definedColumnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px' ); + $definedColumnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px' ); + $borderWidth = $column['attrs']['style']['border']['width'] ?? '0px'; + $definedColumnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['border']['left']['width'] ?? $borderWidth ); + $definedColumnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['border']['right']['width'] ?? $borderWidth ); + } + } - if ($columnsCount - $columnsCountWithDefinedWidth > 0) { - $defaultColumnsWidth = round(($columnsWidth - $definedColumnWidth) / ($columnsCount - $columnsCountWithDefinedWidth), 2); - foreach ($columns as $key => $column) { - if (!isset($column['attrs']['width']) || empty($column['attrs']['width'])) { - // Add padding to the specific column width because it's not included in the default width - $columnWidth = $defaultColumnsWidth; - $columnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['spacing']['padding']['left'] ?? '0px'); - $columnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['spacing']['padding']['right'] ?? '0px'); - $borderWidth = $column['attrs']['style']['border']['width'] ?? '0px'; - $columnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['border']['left']['width'] ?? $borderWidth); - $columnWidth += $this->parseNumberFromStringWithPixels($column['attrs']['style']['border']['right']['width'] ?? $borderWidth); - $columns[$key]['attrs']['width'] = "{$columnWidth}px"; - } - } - } - return $columns; - } + if ( $columnsCount - $columnsCountWithDefinedWidth > 0 ) { + $defaultColumnsWidth = round( ( $columnsWidth - $definedColumnWidth ) / ( $columnsCount - $columnsCountWithDefinedWidth ), 2 ); + foreach ( $columns as $key => $column ) { + if ( ! isset( $column['attrs']['width'] ) || empty( $column['attrs']['width'] ) ) { + // Add padding to the specific column width because it's not included in the default width + $columnWidth = $defaultColumnsWidth; + $columnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px' ); + $columnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px' ); + $borderWidth = $column['attrs']['style']['border']['width'] ?? '0px'; + $columnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['border']['left']['width'] ?? $borderWidth ); + $columnWidth += $this->parseNumberFromStringWithPixels( $column['attrs']['style']['border']['right']['width'] ?? $borderWidth ); + $columns[ $key ]['attrs']['width'] = "{$columnWidth}px"; + } + } + } + return $columns; + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php index a0225044ec..62bbf6b7e1 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php @@ -3,15 +3,15 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors; class Cleanup_Preprocessor implements Preprocessor { - public function preprocess(array $parsedBlocks, array $layout, array $styles): array { - foreach ($parsedBlocks as $key => $block) { - // https://core.trac.wordpress.org/ticket/45312 - // \WP_Block_Parser::parse_blocks() sometimes add a block with name null that can cause unexpected spaces in rendered content - // This behavior was reported as an issue, but it was closed as won't fix - if ($block['blockName'] === null) { - unset($parsedBlocks[$key]); - } - } - return array_values($parsedBlocks); - } + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array { + foreach ( $parsedBlocks as $key => $block ) { + // https://core.trac.wordpress.org/ticket/45312 + // \WP_Block_Parser::parse_blocks() sometimes add a block with name null that can cause unexpected spaces in rendered content + // This behavior was reported as an issue, but it was closed as won't fix + if ( $block['blockName'] === null ) { + unset( $parsedBlocks[ $key ] ); + } + } + return array_values( $parsedBlocks ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php index 1dea882572..569dad471e 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php @@ -7,30 +7,30 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors; * In the early development phase, we are setting only margin-top for blocks that are not first or last in the columns block. */ class Spacing_Preprocessor implements Preprocessor { - public function preprocess(array $parsedBlocks, array $layout, array $styles): array { - $parsedBlocks = $this->addBlockGaps($parsedBlocks, $styles['spacing']['blockGap'] ?? '', null); - return $parsedBlocks; - } + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array { + $parsedBlocks = $this->addBlockGaps( $parsedBlocks, $styles['spacing']['blockGap'] ?? '', null ); + return $parsedBlocks; + } - private function addBlockGaps(array $parsedBlocks, string $gap = '', $parentBlock = null): array { - foreach ($parsedBlocks as $key => $block) { - $parentBlockName = $parentBlock['blockName'] ?? ''; - // Ensure that email_attrs are set - $block['email_attrs'] = $block['email_attrs'] ?? []; - /** - * Do not add a gap to: - * - the top level blocks - they are post-content, and header and footer wrappers and we don't want a gap between those - * - first child - * - parent block is a buttons block (where buttons are side by side). - **/ - if ($parentBlock && $key !== 0 && $gap && $parentBlockName !== 'core/buttons') { - $block['email_attrs']['margin-top'] = $gap; - } + private function addBlockGaps( array $parsedBlocks, string $gap = '', $parentBlock = null ): array { + foreach ( $parsedBlocks as $key => $block ) { + $parentBlockName = $parentBlock['blockName'] ?? ''; + // Ensure that email_attrs are set + $block['email_attrs'] = $block['email_attrs'] ?? array(); + /** + * Do not add a gap to: + * - the top level blocks - they are post-content, and header and footer wrappers and we don't want a gap between those + * - first child + * - parent block is a buttons block (where buttons are side by side). + */ + if ( $parentBlock && $key !== 0 && $gap && $parentBlockName !== 'core/buttons' ) { + $block['email_attrs']['margin-top'] = $gap; + } - $block['innerBlocks'] = $this->addBlockGaps($block['innerBlocks'] ?? [], $gap, $block); - $parsedBlocks[$key] = $block; - } + $block['innerBlocks'] = $this->addBlockGaps( $block['innerBlocks'] ?? array(), $gap, $block ); + $parsedBlocks[ $key ] = $block; + } - return $parsedBlocks; - } + return $parsedBlocks; + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php index f46cf723ba..6066bd89a7 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php @@ -5,82 +5,83 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors; use MailPoet\EmailEditor\Engine\Settings_Controller; class Typography_Preprocessor implements Preprocessor { - /** - * List of styles that should be copied from parent to children. - * @var string[] - */ - private const TYPOGRAPHY_STYLES = [ - 'color', - 'font-size', - 'text-decoration', - ]; + /** + * List of styles that should be copied from parent to children. + * + * @var string[] + */ + private const TYPOGRAPHY_STYLES = array( + 'color', + 'font-size', + 'text-decoration', + ); - /** @var Settings_Controller */ - private $settingsController; + /** @var Settings_Controller */ + private $settingsController; - public function __construct( - Settings_Controller $settingsController - ) { - $this->settingsController = $settingsController; - } + public function __construct( + Settings_Controller $settingsController + ) { + $this->settingsController = $settingsController; + } - public function preprocess(array $parsedBlocks, array $layout, array $styles): array { - foreach ($parsedBlocks as $key => $block) { - $block = $this->preprocessParent($block); - // Set defaults from theme - this needs to be done on top level blocks only - $block = $this->setDefaultsFromTheme($block); + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array { + foreach ( $parsedBlocks as $key => $block ) { + $block = $this->preprocessParent( $block ); + // Set defaults from theme - this needs to be done on top level blocks only + $block = $this->setDefaultsFromTheme( $block ); - $block['innerBlocks'] = $this->copyTypographyFromParent($block['innerBlocks'], $block); - $parsedBlocks[$key] = $block; - } - return $parsedBlocks; - } + $block['innerBlocks'] = $this->copyTypographyFromParent( $block['innerBlocks'], $block ); + $parsedBlocks[ $key ] = $block; + } + return $parsedBlocks; + } - private function copyTypographyFromParent(array $children, array $parent): array { - foreach ($children as $key => $child) { - $child = $this->preprocessParent($child); - $child['email_attrs'] = array_merge($this->filterStyles($parent['email_attrs']), $child['email_attrs']); - $child['innerBlocks'] = $this->copyTypographyFromParent($child['innerBlocks'] ?? [], $child); - $children[$key] = $child; - } + private function copyTypographyFromParent( array $children, array $parent ): array { + foreach ( $children as $key => $child ) { + $child = $this->preprocessParent( $child ); + $child['email_attrs'] = array_merge( $this->filterStyles( $parent['email_attrs'] ), $child['email_attrs'] ); + $child['innerBlocks'] = $this->copyTypographyFromParent( $child['innerBlocks'] ?? array(), $child ); + $children[ $key ] = $child; + } - return $children; - } + return $children; + } - private function preprocessParent(array $block): array { - // Build styles that should be copied to children - $emailAttrs = []; - if (isset($block['attrs']['style']['color']['text'])) { - $emailAttrs['color'] = $block['attrs']['style']['color']['text']; - } - // In case the fontSize is set via a slug (small, medium, large, etc.) we translate it to a number - // The font size slug is set in $block['attrs']['fontSize'] and value in $block['attrs']['style']['typography']['fontSize'] - if (isset($block['attrs']['fontSize'])) { - $block['attrs']['style']['typography']['fontSize'] = $this->settingsController->translateSlugToFontSize($block['attrs']['fontSize']); - } - // Pass font size to email_attrs - if (isset($block['attrs']['style']['typography']['fontSize'])) { - $emailAttrs['font-size'] = $block['attrs']['style']['typography']['fontSize']; - } - if (isset($block['attrs']['style']['typography']['textDecoration'])) { - $emailAttrs['text-decoration'] = $block['attrs']['style']['typography']['textDecoration']; - } - $block['email_attrs'] = array_merge($emailAttrs, $block['email_attrs'] ?? []); - return $block; - } + private function preprocessParent( array $block ): array { + // Build styles that should be copied to children + $emailAttrs = array(); + if ( isset( $block['attrs']['style']['color']['text'] ) ) { + $emailAttrs['color'] = $block['attrs']['style']['color']['text']; + } + // In case the fontSize is set via a slug (small, medium, large, etc.) we translate it to a number + // The font size slug is set in $block['attrs']['fontSize'] and value in $block['attrs']['style']['typography']['fontSize'] + if ( isset( $block['attrs']['fontSize'] ) ) { + $block['attrs']['style']['typography']['fontSize'] = $this->settingsController->translateSlugToFontSize( $block['attrs']['fontSize'] ); + } + // Pass font size to email_attrs + if ( isset( $block['attrs']['style']['typography']['fontSize'] ) ) { + $emailAttrs['font-size'] = $block['attrs']['style']['typography']['fontSize']; + } + if ( isset( $block['attrs']['style']['typography']['textDecoration'] ) ) { + $emailAttrs['text-decoration'] = $block['attrs']['style']['typography']['textDecoration']; + } + $block['email_attrs'] = array_merge( $emailAttrs, $block['email_attrs'] ?? array() ); + return $block; + } - private function filterStyles(array $styles): array { - return array_intersect_key($styles, array_flip(self::TYPOGRAPHY_STYLES)); - } + private function filterStyles( array $styles ): array { + return array_intersect_key( $styles, array_flip( self::TYPOGRAPHY_STYLES ) ); + } - private function setDefaultsFromTheme(array $block): array { - $themeData = $this->settingsController->getTheme()->get_data(); - if (!($block['email_attrs']['color'] ?? '')) { - $block['email_attrs']['color'] = $themeData['styles']['color']['text'] ?? null; - } - if (!($block['email_attrs']['font-size'] ?? '')) { - $block['email_attrs']['font-size'] = $themeData['styles']['typography']['fontSize']; - } - return $block; - } + private function setDefaultsFromTheme( array $block ): array { + $themeData = $this->settingsController->getTheme()->get_data(); + if ( ! ( $block['email_attrs']['color'] ?? '' ) ) { + $block['email_attrs']['color'] = $themeData['styles']['color']['text'] ?? null; + } + if ( ! ( $block['email_attrs']['font-size'] ?? '' ) ) { + $block['email_attrs']['font-size'] = $themeData['styles']['typography']['fontSize']; + } + return $block; + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php index eddb9107e4..938d4e1b2e 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php @@ -3,9 +3,9 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors; interface Preprocessor { - /** - * @param array{contentSize: string} $layout - * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles - */ - public function preprocess(array $parsedBlocks, array $layout, array $styles): array; + /** + * @param array{contentSize: string} $layout + * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles + */ + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array; } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-block-renderer.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-block-renderer.php index 91a30143ed..50376f4aa2 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-block-renderer.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-block-renderer.php @@ -5,5 +5,5 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer; use MailPoet\EmailEditor\Engine\Settings_Controller; interface Block_Renderer { - public function render(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string; + public function render( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string; } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-parser.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-parser.php index 048f8cb047..8afe09370e 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-parser.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-parser.php @@ -5,15 +5,15 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer; use WP_Block_Parser; class Blocks_Parser extends WP_Block_Parser { - /** - * List of parsed blocks - * - * @var \WP_Block_Parser_Block[] - */ - public $output; + /** + * List of parsed blocks + * + * @var \WP_Block_Parser_Block[] + */ + public $output; - public function parse($document) { - parent::parse($document); - return apply_filters('mailpoet_blocks_renderer_parsed_blocks', $this->output); - } + public function parse( $document ) { + parent::parse( $document ); + return apply_filters( 'mailpoet_blocks_renderer_parsed_blocks', $this->output ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-registry.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-registry.php index 1c65463b82..1cf9dbf2d4 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-registry.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-blocks-registry.php @@ -4,38 +4,38 @@ namespace MailPoet\EmailEditor\Engine\Renderer\ContentRenderer; class Blocks_Registry { - /** @var Block_Renderer[] */ - private $blockRenderersMap = []; - /** @var BlockRenderer */ - private $fallbackRenderer = null; + /** @var Block_Renderer[] */ + private $blockRenderersMap = array(); + /** @var BlockRenderer */ + private $fallbackRenderer = null; - public function addBlockRenderer(string $blockName, Block_Renderer $renderer): void { - $this->blockRenderersMap[$blockName] = $renderer; - } + public function addBlockRenderer( string $blockName, Block_Renderer $renderer ): void { + $this->blockRenderersMap[ $blockName ] = $renderer; + } - public function addFallbackRenderer(BlockRenderer $renderer): void { - $this->fallbackRenderer = $renderer; - } + public function addFallbackRenderer( BlockRenderer $renderer ): void { + $this->fallbackRenderer = $renderer; + } - public function hasBlockRenderer(string $blockName): bool { - return isset($this->blockRenderersMap[$blockName]); - } + public function hasBlockRenderer( string $blockName ): bool { + return isset( $this->blockRenderersMap[ $blockName ] ); + } - public function getBlockRenderer(string $blockName): ?Block_Renderer { - return $this->blockRenderersMap[$blockName] ?? null; - } + public function getBlockRenderer( string $blockName ): ?Block_Renderer { + return $this->blockRenderersMap[ $blockName ] ?? null; + } - public function getFallbackRenderer(): ?BlockRenderer { - return $this->fallbackRenderer; - } + public function getFallbackRenderer(): ?BlockRenderer { + return $this->fallbackRenderer; + } - public function removeAllBlockRenderers(): void { - foreach (array_keys($this->blockRenderersMap) as $blockName) { - $this->removeBlockRenderer($blockName); - } - } + public function removeAllBlockRenderers(): void { + foreach ( array_keys( $this->blockRenderersMap ) as $blockName ) { + $this->removeBlockRenderer( $blockName ); + } + } - private function removeBlockRenderer(string $blockName): void { - unset($this->blockRenderersMap[$blockName]); - } + private function removeBlockRenderer( string $blockName ): void { + unset( $this->blockRenderersMap[ $blockName ] ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php index ecbd8b3f4d..ff5b41c5e1 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php @@ -9,92 +9,92 @@ use WP_Block_Template; use WP_Post; class Content_Renderer { - private Blocks_Registry $blocksRegistry; - private Process_Manager $processManager; - private Settings_Controller $settingsController; - private Theme_Controller $themeController; - private $post = null; - private $template = null; + private Blocks_Registry $blocksRegistry; + private Process_Manager $processManager; + private Settings_Controller $settingsController; + private Theme_Controller $themeController; + private $post = null; + private $template = null; - const CONTENT_STYLES_FILE = 'content.css'; + const CONTENT_STYLES_FILE = 'content.css'; - public function __construct( - Process_Manager $preprocessManager, - Blocks_Registry $blocksRegistry, - Settings_Controller $settingsController, - Theme_Controller $themeController - ) { - $this->processManager = $preprocessManager; - $this->blocksRegistry = $blocksRegistry; - $this->settingsController = $settingsController; - $this->themeController = $themeController; - } + public function __construct( + Process_Manager $preprocessManager, + Blocks_Registry $blocksRegistry, + Settings_Controller $settingsController, + Theme_Controller $themeController + ) { + $this->processManager = $preprocessManager; + $this->blocksRegistry = $blocksRegistry; + $this->settingsController = $settingsController; + $this->themeController = $themeController; + } - private function initialize() { - add_filter('render_block', [$this, 'renderBlock'], 10, 2); - add_filter('block_parser_class', [$this, 'blockParser']); - add_filter('mailpoet_blocks_renderer_parsed_blocks', [$this, 'preprocessParsedBlocks']); + private function initialize() { + add_filter( 'render_block', array( $this, 'renderBlock' ), 10, 2 ); + add_filter( 'block_parser_class', array( $this, 'blockParser' ) ); + add_filter( 'mailpoet_blocks_renderer_parsed_blocks', array( $this, 'preprocessParsedBlocks' ) ); - do_action('mailpoet_blocks_renderer_initialized', $this->blocksRegistry); - } + do_action( 'mailpoet_blocks_renderer_initialized', $this->blocksRegistry ); + } - public function render(WP_Post $post, WP_Block_Template $template): string { - $this->post = $post; - $this->template = $template; - $this->setTemplateGlobals($post, $template); - $this->initialize(); - $renderedHtml = get_the_block_template_html(); - $this->reset(); + public function render( WP_Post $post, WP_Block_Template $template ): string { + $this->post = $post; + $this->template = $template; + $this->setTemplateGlobals( $post, $template ); + $this->initialize(); + $renderedHtml = get_the_block_template_html(); + $this->reset(); - return $this->processManager->postprocess($this->inlineStyles($renderedHtml, $post, $template)); - } + return $this->processManager->postprocess( $this->inlineStyles( $renderedHtml, $post, $template ) ); + } - public function blockParser() { - return 'MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Blocks_Parser'; - } + public function blockParser() { + return 'MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Blocks_Parser'; + } - public function preprocessParsedBlocks(array $parsedBlocks): array { - return $this->processManager->preprocess($parsedBlocks, $this->themeController->getLayoutSettings(), $this->themeController->getStyles($this->post, $this->template)); - } + public function preprocessParsedBlocks( array $parsedBlocks ): array { + return $this->processManager->preprocess( $parsedBlocks, $this->themeController->getLayoutSettings(), $this->themeController->getStyles( $this->post, $this->template ) ); + } - public function renderBlock($blockContent, $parsedBlock) { - $renderer = $this->blocksRegistry->getBlockRenderer($parsedBlock['blockName']); - if (!$renderer) { - $renderer = $this->blocksRegistry->getFallbackRenderer(); - } - return $renderer ? $renderer->render($blockContent, $parsedBlock, $this->settingsController) : $blockContent; - } + public function renderBlock( $blockContent, $parsedBlock ) { + $renderer = $this->blocksRegistry->getBlockRenderer( $parsedBlock['blockName'] ); + if ( ! $renderer ) { + $renderer = $this->blocksRegistry->getFallbackRenderer(); + } + return $renderer ? $renderer->render( $blockContent, $parsedBlock, $this->settingsController ) : $blockContent; + } - private function setTemplateGlobals(WP_Post $post, WP_Block_Template $template) { - global $_wp_current_template_content, $_wp_current_template_id; - $_wp_current_template_id = $template->id; - $_wp_current_template_content = $template->content; - $GLOBALS['post'] = $post; - } + private function setTemplateGlobals( WP_Post $post, WP_Block_Template $template ) { + global $_wp_current_template_content, $_wp_current_template_id; + $_wp_current_template_id = $template->id; + $_wp_current_template_content = $template->content; + $GLOBALS['post'] = $post; + } - /** - * As we use default WordPress filters, we need to remove them after email rendering - * so that we don't interfere with possible post rendering that might happen later. - */ - private function reset() { - $this->blocksRegistry->removeAllBlockRenderers(); - remove_filter('render_block', [$this, 'renderBlock']); - remove_filter('block_parser_class', [$this, 'blockParser']); - remove_filter('mailpoet_blocks_renderer_parsed_blocks', [$this, 'preprocessParsedBlocks']); - } + /** + * As we use default WordPress filters, we need to remove them after email rendering + * so that we don't interfere with possible post rendering that might happen later. + */ + private function reset() { + $this->blocksRegistry->removeAllBlockRenderers(); + remove_filter( 'render_block', array( $this, 'renderBlock' ) ); + remove_filter( 'block_parser_class', array( $this, 'blockParser' ) ); + remove_filter( 'mailpoet_blocks_renderer_parsed_blocks', array( $this, 'preprocessParsedBlocks' ) ); + } - /** - * @param string $html - * @return string - */ - private function inlineStyles($html, WP_Post $post, $template = null) { - $styles = (string)file_get_contents(dirname(__FILE__) . '/' . self::CONTENT_STYLES_FILE); - $styles .= (string)file_get_contents(dirname(__FILE__) . '/../../content-shared.css'); + /** + * @param string $html + * @return string + */ + private function inlineStyles( $html, WP_Post $post, $template = null ) { + $styles = (string) file_get_contents( __DIR__ . '/' . self::CONTENT_STYLES_FILE ); + $styles .= (string) file_get_contents( __DIR__ . '/../../content-shared.css' ); - // Apply default contentWidth to constrained blocks. - $layout = $this->themeController->getLayoutSettings(); - $styles .= sprintf( - ' + // Apply default contentWidth to constrained blocks. + $layout = $this->themeController->getLayoutSettings(); + $styles .= sprintf( + ' .is-layout-constrained > *:not(.alignleft):not(.alignright):not(.alignfull) { max-width: %1$s; margin-left: auto !important; @@ -106,40 +106,40 @@ class Content_Renderer { margin-right: auto !important; } ', - $layout['contentSize'], - $layout['wideSize'] - ); + $layout['contentSize'], + $layout['wideSize'] + ); - // Get styles from theme. - $styles .= $this->themeController->getStylesheetForRendering($post, $template); - $blockSupportStyles = $this->themeController->getStylesheetFromContext('block-supports', []); - // Get styles from block-supports stylesheet. This includes rules such as layout (contentWidth) that some blocks use. - // @see https://github.com/WordPress/WordPress/blob/3c5da9c74344aaf5bf8097f2e2c6a1a781600e03/wp-includes/script-loader.php#L3134 - // @internal :where is not supported by emogrifier, so we need to replace it with *. - $blockSupportStyles = str_replace( - ':where(:not(.alignleft):not(.alignright):not(.alignfull))', - '*:not(.alignleft):not(.alignright):not(.alignfull)', - $blockSupportStyles - ); - // Layout CSS assumes the top level block will have a single DIV wrapper with children. Since our blocks use tables, - // we need to adjust this to look for children in the TD element. This may requires more advanced replacement but - // this works in the current version of Gutenberg. - // Example rule we're targetting: .wp-container-core-group-is-layout-1.wp-container-core-group-is-layout-1 > * - $blockSupportStyles = preg_replace( - '/group-is-layout-(\d+) >/', - 'group-is-layout-$1 > tbody tr td >', - $blockSupportStyles - ); + // Get styles from theme. + $styles .= $this->themeController->getStylesheetForRendering( $post, $template ); + $blockSupportStyles = $this->themeController->getStylesheetFromContext( 'block-supports', array() ); + // Get styles from block-supports stylesheet. This includes rules such as layout (contentWidth) that some blocks use. + // @see https://github.com/WordPress/WordPress/blob/3c5da9c74344aaf5bf8097f2e2c6a1a781600e03/wp-includes/script-loader.php#L3134 + // @internal :where is not supported by emogrifier, so we need to replace it with *. + $blockSupportStyles = str_replace( + ':where(:not(.alignleft):not(.alignright):not(.alignfull))', + '*:not(.alignleft):not(.alignright):not(.alignfull)', + $blockSupportStyles + ); + // Layout CSS assumes the top level block will have a single DIV wrapper with children. Since our blocks use tables, + // we need to adjust this to look for children in the TD element. This may requires more advanced replacement but + // this works in the current version of Gutenberg. + // Example rule we're targetting: .wp-container-core-group-is-layout-1.wp-container-core-group-is-layout-1 > * + $blockSupportStyles = preg_replace( + '/group-is-layout-(\d+) >/', + 'group-is-layout-$1 > tbody tr td >', + $blockSupportStyles + ); - $styles .= $blockSupportStyles; + $styles .= $blockSupportStyles; - // Debugging for content styles. Remember these get inlined. - //echo '
';
-    //var_dump($styles);
-    //echo '
'; + // Debugging for content styles. Remember these get inlined. + // echo '
';
+		// var_dump($styles);
+		// echo '
'; - $styles = ''; + $styles = ''; - return CssInliner::fromHtml($styles . $html)->inlineCss()->render(); - } + return CssInliner::fromHtml( $styles . $html )->inlineCss()->render(); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php index 6c0bcc23d1..883dcd4621 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php @@ -12,53 +12,53 @@ use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors\Spacing_P use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Preprocessors\Typography_Preprocessor; class Process_Manager { - /** @var Preprocessor[] */ - private $preprocessors = []; + /** @var Preprocessor[] */ + private $preprocessors = array(); - /** @var Postprocessor[] */ - private $postprocessors = []; + /** @var Postprocessor[] */ + private $postprocessors = array(); - public function __construct( - Cleanup_Preprocessor $cleanupPreprocessor, - Blocks_Width_Preprocessor $blocksWidthPreprocessor, - Typography_Preprocessor $typographyPreprocessor, - Spacing_Preprocessor $spacingPreprocessor, - Highlighting_Postprocessor $highlightingPostprocessor, - Variables_Postprocessor $variablesPostprocessor - ) { - $this->registerPreprocessor($cleanupPreprocessor); - $this->registerPreprocessor($blocksWidthPreprocessor); - $this->registerPreprocessor($typographyPreprocessor); - $this->registerPreprocessor($spacingPreprocessor); - $this->registerPostprocessor($highlightingPostprocessor); - $this->registerPostprocessor($variablesPostprocessor); - } + public function __construct( + Cleanup_Preprocessor $cleanupPreprocessor, + Blocks_Width_Preprocessor $blocksWidthPreprocessor, + Typography_Preprocessor $typographyPreprocessor, + Spacing_Preprocessor $spacingPreprocessor, + Highlighting_Postprocessor $highlightingPostprocessor, + Variables_Postprocessor $variablesPostprocessor + ) { + $this->registerPreprocessor( $cleanupPreprocessor ); + $this->registerPreprocessor( $blocksWidthPreprocessor ); + $this->registerPreprocessor( $typographyPreprocessor ); + $this->registerPreprocessor( $spacingPreprocessor ); + $this->registerPostprocessor( $highlightingPostprocessor ); + $this->registerPostprocessor( $variablesPostprocessor ); + } - /** - * @param array $parsedBlocks - * @param array{contentSize: string} $layout - * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles - * @return array - */ - public function preprocess(array $parsedBlocks, array $layout, array $styles): array { - foreach ($this->preprocessors as $preprocessor) { - $parsedBlocks = $preprocessor->preprocess($parsedBlocks, $layout, $styles); - } - return $parsedBlocks; - } + /** + * @param array $parsedBlocks + * @param array{contentSize: string} $layout + * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles + * @return array + */ + public function preprocess( array $parsedBlocks, array $layout, array $styles ): array { + foreach ( $this->preprocessors as $preprocessor ) { + $parsedBlocks = $preprocessor->preprocess( $parsedBlocks, $layout, $styles ); + } + return $parsedBlocks; + } - public function postprocess(string $html): string { - foreach ($this->postprocessors as $postprocessor) { - $html = $postprocessor->postprocess($html); - } - return $html; - } + public function postprocess( string $html ): string { + foreach ( $this->postprocessors as $postprocessor ) { + $html = $postprocessor->postprocess( $html ); + } + return $html; + } - public function registerPreprocessor(Preprocessor $preprocessor): void { - $this->preprocessors[] = $preprocessor; - } + public function registerPreprocessor( Preprocessor $preprocessor ): void { + $this->preprocessors[] = $preprocessor; + } - public function registerPostprocessor(Postprocessor $postprocessor): void { - $this->postprocessors[] = $postprocessor; - } + public function registerPostprocessor( Postprocessor $postprocessor ): void { + $this->postprocessors[] = $postprocessor; + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/content.css b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/content.css index 87cf55f04c..075bdb0b11 100644 --- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/content.css +++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/content.css @@ -1,28 +1,28 @@ /** - CSS reset for email clients for elements used in email content - StyleLint is disabled because some rules contain properties that linter marks as unknown (e.g. mso- prefix), but they are valid for email rendering +CSS reset for email clients for elements used in email content +StyleLint is disabled because some rules contain properties that linter marks as unknown (e.g. mso- prefix), but they are valid for email rendering */ /* stylelint-disable property-no-unknown */ table, td { - border-collapse: collapse; - mso-table-lspace: 0; - mso-table-rspace: 0; + border-collapse: collapse; + mso-table-lspace: 0; + mso-table-rspace: 0; } img { - border: 0; - height: auto; - -ms-interpolation-mode: bicubic; - line-height: 100%; - max-width: 100%; - outline: none; - text-decoration: none; + border: 0; + height: auto; + -ms-interpolation-mode: bicubic; + line-height: 100%; + max-width: 100%; + outline: none; + text-decoration: none; } p { - display: block; - margin: 0; + display: block; + margin: 0; } h1, @@ -31,25 +31,25 @@ h3, h4, h5, h6 { - margin-bottom: 0; - margin-top: 0; + margin-bottom: 0; + margin-top: 0; } /* Ensure border style is set when a block has a border */ .has-border-color { - border-style: solid; + border-style: solid; } /* We want ensure the same design for all email clients */ ul, ol { - /* When margin attribute is set to zero, Outlook doesn't render the list properly. As a possible workaround, we can reset only margin for top and bottom */ - margin-bottom: 0; - margin-top: 0; - padding: 0 0 0 40px; + /* When margin attribute is set to zero, Outlook doesn't render the list properly. As a possible workaround, we can reset only margin for top and bottom */ + margin-bottom: 0; + margin-top: 0; + padding: 0 0 0 40px; } /* Outlook was adding weird spaces around lists in some versions. Resetting vertical margin for list items solved it */ li { - margin-bottom: 0; - margin-top: 0; + margin-bottom: 0; + margin-top: 0; } diff --git a/packages/php/email-editor/src/Engine/Renderer/class-renderer.php b/packages/php/email-editor/src/Engine/Renderer/class-renderer.php index 10749f9e6c..42ff263183 100644 --- a/packages/php/email-editor/src/Engine/Renderer/class-renderer.php +++ b/packages/php/email-editor/src/Engine/Renderer/class-renderer.php @@ -11,94 +11,94 @@ use WP_Style_Engine; use WP_Theme_JSON; class Renderer { - private Theme_Controller $themeController; - private Content_Renderer $contentRenderer; - private Templates $templates; - /** @var WP_Theme_JSON|null */ - private static $theme = null; + private Theme_Controller $themeController; + private Content_Renderer $contentRenderer; + private Templates $templates; + /** @var WP_Theme_JSON|null */ + private static $theme = null; - const TEMPLATE_FILE = 'template-canvas.php'; - const TEMPLATE_STYLES_FILE = 'template-canvas.css'; + const TEMPLATE_FILE = 'template-canvas.php'; + const TEMPLATE_STYLES_FILE = 'template-canvas.css'; - public function __construct( - Content_Renderer $contentRenderer, - Templates $templates, - Theme_Controller $themeController - ) { - $this->contentRenderer = $contentRenderer; - $this->templates = $templates; - $this->themeController = $themeController; - } + public function __construct( + Content_Renderer $contentRenderer, + Templates $templates, + Theme_Controller $themeController + ) { + $this->contentRenderer = $contentRenderer; + $this->templates = $templates; + $this->themeController = $themeController; + } - /** - * During rendering, this stores the theme data for the template being rendered. - */ - public static function getTheme() { - return self::$theme; - } + /** + * During rendering, this stores the theme data for the template being rendered. + */ + public static function getTheme() { + return self::$theme; + } - public function render(\WP_Post $post, string $subject, string $preHeader, string $language, $metaRobots = ''): array { - $templateId = 'mailpoet/mailpoet//' . (get_page_template_slug($post) ?: 'email-general'); - $template = $this->templates->getBlockTemplate($templateId); - $theme = $this->templates->getBlockTemplateTheme($templateId, $template->wp_id); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + public function render( \WP_Post $post, string $subject, string $preHeader, string $language, $metaRobots = '' ): array { + $templateId = 'mailpoet/mailpoet//' . ( get_page_template_slug( $post ) ?: 'email-general' ); + $template = $this->templates->getBlockTemplate( $templateId ); + $theme = $this->templates->getBlockTemplateTheme( $templateId, $template->wp_id ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - // Set the theme for the template. This is merged with base theme.json and core json before rendering. - self::$theme = new WP_Theme_JSON($theme, 'default'); + // Set the theme for the template. This is merged with base theme.json and core json before rendering. + self::$theme = new WP_Theme_JSON( $theme, 'default' ); - $emailStyles = $this->themeController->getStyles($post, $template); - $templateHtml = $this->contentRenderer->render($post, $template); - $layout = $this->themeController->getLayoutSettings(); + $emailStyles = $this->themeController->getStyles( $post, $template ); + $templateHtml = $this->contentRenderer->render( $post, $template ); + $layout = $this->themeController->getLayoutSettings(); - ob_start(); - include self::TEMPLATE_FILE; - $renderedTemplate = (string)ob_get_clean(); + ob_start(); + include self::TEMPLATE_FILE; + $renderedTemplate = (string) ob_get_clean(); - $templateStyles = - WP_Style_Engine::compile_css( - [ - 'background-color' => $emailStyles['color']['background'] ?? 'inherit', - 'color' => $emailStyles['color']['text'] ?? 'inherit', - 'padding-top' => $emailStyles['spacing']['padding']['top'] ?? '0px', - 'padding-bottom' => $emailStyles['spacing']['padding']['bottom'] ?? '0px', - 'padding-left' => $emailStyles['spacing']['padding']['left'] ?? '0px', - 'padding-right' => $emailStyles['spacing']['padding']['right'] ?? '0px', - 'font-family' => $emailStyles['typography']['fontFamily'] ?? 'inherit', - 'line-height' => $emailStyles['typography']['lineHeight'] ?? '1.5', - 'font-size' => $emailStyles['typography']['fontSize'] ?? 'inherit', - ], - 'body, .email_layout_wrapper' - ); - $templateStyles .= '.email_layout_wrapper { box-sizing: border-box;}'; - $templateStyles .= file_get_contents(dirname(__FILE__) . '/' . self::TEMPLATE_STYLES_FILE); - $templateStyles = ''; - $renderedTemplate = $this->inlineCSSStyles($templateStyles . $renderedTemplate); + $templateStyles = + WP_Style_Engine::compile_css( + array( + 'background-color' => $emailStyles['color']['background'] ?? 'inherit', + 'color' => $emailStyles['color']['text'] ?? 'inherit', + 'padding-top' => $emailStyles['spacing']['padding']['top'] ?? '0px', + 'padding-bottom' => $emailStyles['spacing']['padding']['bottom'] ?? '0px', + 'padding-left' => $emailStyles['spacing']['padding']['left'] ?? '0px', + 'padding-right' => $emailStyles['spacing']['padding']['right'] ?? '0px', + 'font-family' => $emailStyles['typography']['fontFamily'] ?? 'inherit', + 'line-height' => $emailStyles['typography']['lineHeight'] ?? '1.5', + 'font-size' => $emailStyles['typography']['fontSize'] ?? 'inherit', + ), + 'body, .email_layout_wrapper' + ); + $templateStyles .= '.email_layout_wrapper { box-sizing: border-box;}'; + $templateStyles .= file_get_contents( __DIR__ . '/' . self::TEMPLATE_STYLES_FILE ); + $templateStyles = ''; + $renderedTemplate = $this->inlineCSSStyles( $templateStyles . $renderedTemplate ); - // This is a workaround to support link :hover in some clients. Ideally we would remove the ability to set :hover - // however this is not possible using the color panel from Gutenberg. - if (isset($emailStyles['elements']['link'][':hover']['color']['text'])) { - $renderedTemplate = str_replace('', '', $renderedTemplate); - } + // This is a workaround to support link :hover in some clients. Ideally we would remove the ability to set :hover + // however this is not possible using the color panel from Gutenberg. + if ( isset( $emailStyles['elements']['link'][':hover']['color']['text'] ) ) { + $renderedTemplate = str_replace( '', '', $renderedTemplate ); + } - return [ - 'html' => $renderedTemplate, - 'text' => $this->renderTextVersion($renderedTemplate), - ]; - } + return array( + 'html' => $renderedTemplate, + 'text' => $this->renderTextVersion( $renderedTemplate ), + ); + } - /** - * @param string $template - * @return string - */ - private function inlineCSSStyles($template) { - return CssInliner::fromHtml($template)->inlineCss()->render(); - } + /** + * @param string $template + * @return string + */ + private function inlineCSSStyles( $template ) { + return CssInliner::fromHtml( $template )->inlineCss()->render(); + } - /** - * @param string $template - * @return string - */ - private function renderTextVersion($template) { - $template = (mb_detect_encoding($template, 'UTF-8', true)) ? $template : mb_convert_encoding($template, 'UTF-8', mb_list_encodings()); - return @Html2Text::convert($template); - } + /** + * @param string $template + * @return string + */ + private function renderTextVersion( $template ) { + $template = ( mb_detect_encoding( $template, 'UTF-8', true ) ) ? $template : mb_convert_encoding( $template, 'UTF-8', mb_list_encodings() ); + return @Html2Text::convert( $template ); + } } diff --git a/packages/php/email-editor/src/Engine/Renderer/template-canvas.css b/packages/php/email-editor/src/Engine/Renderer/template-canvas.css index 4932f35606..6a77cf3adf 100644 --- a/packages/php/email-editor/src/Engine/Renderer/template-canvas.css +++ b/packages/php/email-editor/src/Engine/Renderer/template-canvas.css @@ -3,89 +3,89 @@ /* StyleLint is disabled because some rules contain properties that linter marks as unknown (e.g. mso- prefix), but they are valid for email rendering */ /* stylelint-disable property-no-unknown */ body { - margin: 0; - padding: 0; - -webkit-text-size-adjust: 100%; /* From MJMJ - Automatic test adjustment on mobile max to 100% */ - -ms-text-size-adjust: 100%; /* From MJMJ - Automatic test adjustment on mobile max to 100% */ - word-spacing: normal; + margin: 0; + padding: 0; + -webkit-text-size-adjust: 100%; /* From MJMJ - Automatic test adjustment on mobile max to 100% */ + -ms-text-size-adjust: 100%; /* From MJMJ - Automatic test adjustment on mobile max to 100% */ + word-spacing: normal; } a { - text-decoration: none; + text-decoration: none; } .email_layout_wrapper { - margin: 0 auto; - width: 100%; + margin: 0 auto; + width: 100%; } .email_content_wrapper { - direction: ltr; - font-size: inherit; - text-align: left; + direction: ltr; + font-size: inherit; + text-align: left; } .email_footer { - direction: ltr; - text-align: center; + direction: ltr; + text-align: center; } /* https://www.emailonacid.com/blog/article/email-development/tips-for-coding-email-preheaders */ .email_preheader, .email_preheader * { - color: #fff; - display: none; - font-size: 1px; - line-height: 1px; - max-height: 0; - max-width: 0; - mso-hide: all; - opacity: 0; - overflow: hidden; - -webkit-text-size-adjust: none; - visibility: hidden; + color: #fff; + display: none; + font-size: 1px; + line-height: 1px; + max-height: 0; + max-width: 0; + mso-hide: all; + opacity: 0; + overflow: hidden; + -webkit-text-size-adjust: none; + visibility: hidden; } @media screen and (max-width: 660px) { - .email-block-column-content { - max-width: 100% !important; - } - .block { - display: block; - width: 100% !important; - } + .email-block-column-content { + max-width: 100% !important; + } + .block { + display: block; + width: 100% !important; + } - /* Ensure proper width of columns on mobile when we set 100% and a border is set */ - .email-block-column { - box-sizing: border-box; - } + /* Ensure proper width of columns on mobile when we set 100% and a border is set */ + .email-block-column { + box-sizing: border-box; + } - /* We set width to some tables e.g. for wrappers of horizontally aligned images and we force width 100% on mobile */ - .email-table-with-width { - width: 100% !important; - } + /* We set width to some tables e.g. for wrappers of horizontally aligned images and we force width 100% on mobile */ + .email-table-with-width { + width: 100% !important; + } - /* Flex Layout */ - .layout-flex-wrapper, - .layout-flex-wrapper tbody, - .layout-flex-wrapper tr { - display: block !important; - width: 100% !important; - } + /* Flex Layout */ + .layout-flex-wrapper, + .layout-flex-wrapper tbody, + .layout-flex-wrapper tr { + display: block !important; + width: 100% !important; + } - .layout-flex-item { - display: block !important; - padding-bottom: 8px !important; /* Half of the flex gap between blocks */ - padding-left: 0 !important; - width: 100% !important; - } + .layout-flex-item { + display: block !important; + padding-bottom: 8px !important; /* Half of the flex gap between blocks */ + padding-left: 0 !important; + width: 100% !important; + } - .layout-flex-item table, - .layout-flex-item td { - box-sizing: border-box !important; - display: block !important; - width: 100% !important; - } - /* Flex Layout End */ + .layout-flex-item table, + .layout-flex-item td { + box-sizing: border-box !important; + display: block !important; + width: 100% !important; + } + /* Flex Layout End */ } /* stylelint-enable property-no-unknown */ diff --git a/packages/php/email-editor/src/Engine/Renderer/template-canvas.php b/packages/php/email-editor/src/Engine/Renderer/template-canvas.php index 4754f4e0bc..53a773329e 100644 --- a/packages/php/email-editor/src/Engine/Renderer/template-canvas.php +++ b/packages/php/email-editor/src/Engine/Renderer/template-canvas.php @@ -6,6 +6,7 @@ * Template file to render the current 'wp_template', specifcally for emails. * * Variables passed to this template: + * * @var $subject string * @var $preHeader string * @var $templateHtml string @@ -15,33 +16,33 @@ ?> > - <?php echo esc_html($subject); ?> - - - - - - - + <?php echo esc_html( $subject ); ?> + + + + + + + - - - + + + diff --git a/packages/php/email-editor/src/Engine/Templates/class-template-preview.php b/packages/php/email-editor/src/Engine/Templates/class-template-preview.php index 37858df1ee..f1e123fafb 100644 --- a/packages/php/email-editor/src/Engine/Templates/class-template-preview.php +++ b/packages/php/email-editor/src/Engine/Templates/class-template-preview.php @@ -8,50 +8,50 @@ use MailPoet\EmailEditor\Validator\Builder; use WP_Theme_JSON; class Template_Preview { - private Theme_Controller $themeController; - private Settings_Controller $settingsController; - private Templates $templates; + private Theme_Controller $themeController; + private Settings_Controller $settingsController; + private Templates $templates; - public function __construct( - Theme_Controller $themeController, - Settings_Controller $settingsController, - Templates $templates - ) { - $this->themeController = $themeController; - $this->settingsController = $settingsController; - $this->templates = $templates; - } + public function __construct( + Theme_Controller $themeController, + Settings_Controller $settingsController, + Templates $templates + ) { + $this->themeController = $themeController; + $this->settingsController = $settingsController; + $this->templates = $templates; + } - public function initialize(): void { - register_rest_field( - 'wp_template', - 'email_theme_css', - [ - 'get_callback' => [$this, 'getEmailThemePreviewCss'], - 'update_callback' => null, - 'schema' => Builder::string()->toArray(), - ] - ); - } + public function initialize(): void { + register_rest_field( + 'wp_template', + 'email_theme_css', + array( + 'get_callback' => array( $this, 'getEmailThemePreviewCss' ), + 'update_callback' => null, + 'schema' => Builder::string()->toArray(), + ) + ); + } - /** - * Generates CSS for preview of email theme - * They are applied in the preview BLockPreview in template selection - */ - public function getEmailThemePreviewCss($template): string { - $editorTheme = clone $this->themeController->getTheme(); - $templateTheme = $this->templates->getBlockTemplateTheme($template['id'], $template['wp_id']); - if (is_array($templateTheme)) { - $editorTheme->merge(new WP_Theme_JSON($templateTheme, 'custom')); - } - $editorSettings = $this->settingsController->getSettings(); - $additionalCSS = ''; - foreach ($editorSettings['styles'] as $style) { - $additionalCSS .= $style['css']; - } - // Set proper content width for previews - $layoutSettings = $this->themeController->getLayoutSettings(); - $additionalCSS .= ".is-root-container { width: {$layoutSettings['contentSize']}; margin: 0 auto; }"; - return $editorTheme->get_stylesheet() . $additionalCSS; - } + /** + * Generates CSS for preview of email theme + * They are applied in the preview BLockPreview in template selection + */ + public function getEmailThemePreviewCss( $template ): string { + $editorTheme = clone $this->themeController->getTheme(); + $templateTheme = $this->templates->getBlockTemplateTheme( $template['id'], $template['wp_id'] ); + if ( is_array( $templateTheme ) ) { + $editorTheme->merge( new WP_Theme_JSON( $templateTheme, 'custom' ) ); + } + $editorSettings = $this->settingsController->getSettings(); + $additionalCSS = ''; + foreach ( $editorSettings['styles'] as $style ) { + $additionalCSS .= $style['css']; + } + // Set proper content width for previews + $layoutSettings = $this->themeController->getLayoutSettings(); + $additionalCSS .= ".is-root-container { width: {$layoutSettings['contentSize']}; margin: 0 auto; }"; + return $editorTheme->get_stylesheet() . $additionalCSS; + } } diff --git a/packages/php/email-editor/src/Engine/Templates/class-templates.php b/packages/php/email-editor/src/Engine/Templates/class-templates.php index baa1b56fb3..e10de1aa1b 100644 --- a/packages/php/email-editor/src/Engine/Templates/class-templates.php +++ b/packages/php/email-editor/src/Engine/Templates/class-templates.php @@ -7,289 +7,292 @@ use WP_Block_Template; // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps class Templates { - const MAILPOET_EMAIL_META_THEME_TYPE = 'mailpoet_email_theme'; - const MAILPOET_TEMPLATE_EMPTY_THEME = ['version' => 2]; // The version 2 is important to merge themes correctly + const MAILPOET_EMAIL_META_THEME_TYPE = 'mailpoet_email_theme'; + const MAILPOET_TEMPLATE_EMPTY_THEME = array( 'version' => 2 ); // The version 2 is important to merge themes correctly - private Utils $utils; - private string $pluginSlug = 'mailpoet/mailpoet'; - private string $postType = 'mailpoet_email'; - private string $templateDirectory; - private array $templates = []; - private array $themeJson = []; + private Utils $utils; + private string $pluginSlug = 'mailpoet/mailpoet'; + private string $postType = 'mailpoet_email'; + private string $templateDirectory; + private array $templates = array(); + private array $themeJson = array(); - public function __construct( - Utils $utils - ) { - $this->utils = $utils; - $this->templateDirectory = dirname(__FILE__) . DIRECTORY_SEPARATOR; - } + public function __construct( + Utils $utils + ) { + $this->utils = $utils; + $this->templateDirectory = __DIR__ . DIRECTORY_SEPARATOR; + } - public function initialize(): void { - add_filter('pre_get_block_file_template', [$this, 'getBlockFileTemplate'], 10, 3); - add_filter('get_block_templates', [$this, 'addBlockTemplates'], 10, 3); - add_filter('theme_templates', [$this, 'addThemeTemplates'], 10, 4); // Needed when saving post – template association - add_filter('get_block_template', [$this, 'addBlockTemplateDetails'], 10, 1); - add_filter('rest_pre_insert_wp_template', [$this, 'forcePostContent'], 9, 1); - $this->initializeTemplates(); - $this->initializeApi(); - } + public function initialize(): void { + add_filter( 'pre_get_block_file_template', array( $this, 'getBlockFileTemplate' ), 10, 3 ); + add_filter( 'get_block_templates', array( $this, 'addBlockTemplates' ), 10, 3 ); + add_filter( 'theme_templates', array( $this, 'addThemeTemplates' ), 10, 4 ); // Needed when saving post – template association + add_filter( 'get_block_template', array( $this, 'addBlockTemplateDetails' ), 10, 1 ); + add_filter( 'rest_pre_insert_wp_template', array( $this, 'forcePostContent' ), 9, 1 ); + $this->initializeTemplates(); + $this->initializeApi(); + } - /** - * Get a block template by ID. - */ - public function getBlockTemplate($templateId) { - $templates = $this->getBlockTemplates(); - return $templates[$templateId] ?? null; - } + /** + * Get a block template by ID. + */ + public function getBlockTemplate( $templateId ) { + $templates = $this->getBlockTemplates(); + return $templates[ $templateId ] ?? null; + } - /** - * Get a predefined or user defined theme for a block template. - * - * @param string $templateId - * @param int|null $templateWpId - * @return array - */ - public function getBlockTemplateTheme($templateId, $templateWpId = null) { - // First check if there is a user updated theme saved - $theme = $this->getCustomTemplateTheme($templateWpId); + /** + * Get a predefined or user defined theme for a block template. + * + * @param string $templateId + * @param int|null $templateWpId + * @return array + */ + public function getBlockTemplateTheme( $templateId, $templateWpId = null ) { + // First check if there is a user updated theme saved + $theme = $this->getCustomTemplateTheme( $templateWpId ); - if ($theme) { - return $theme; - } + if ( $theme ) { + return $theme; + } - // If there is no user edited theme, look for default template themes in files. - ['prefix' => $templatePrefix, 'slug' => $templateSlug] = $this->utils->getTemplateIdParts($templateId); + // If there is no user edited theme, look for default template themes in files. + ['prefix' => $templatePrefix, 'slug' => $templateSlug] = $this->utils->getTemplateIdParts( $templateId ); - if ($this->pluginSlug !== $templatePrefix) { - return self::MAILPOET_TEMPLATE_EMPTY_THEME; - } + if ( $this->pluginSlug !== $templatePrefix ) { + return self::MAILPOET_TEMPLATE_EMPTY_THEME; + } - if (!isset($this->themeJson[$templateSlug])) { - $jsonFile = $this->templateDirectory . $templateSlug . '.json'; + if ( ! isset( $this->themeJson[ $templateSlug ] ) ) { + $jsonFile = $this->templateDirectory . $templateSlug . '.json'; - if (file_exists($jsonFile)) { - $this->themeJson[$templateSlug] = json_decode((string)file_get_contents($jsonFile), true); - } - } + if ( file_exists( $jsonFile ) ) { + $this->themeJson[ $templateSlug ] = json_decode( (string) file_get_contents( $jsonFile ), true ); + } + } - return $this->themeJson[$templateSlug] ?? self::MAILPOET_TEMPLATE_EMPTY_THEME; - } + return $this->themeJson[ $templateSlug ] ?? self::MAILPOET_TEMPLATE_EMPTY_THEME; + } - public function getBlockFileTemplate($return, $templateId, $template_type) { - ['prefix' => $templatePrefix, 'slug' => $templateSlug] = $this->utils->getTemplateIdParts($templateId); + public function getBlockFileTemplate( $return, $templateId, $template_type ) { + ['prefix' => $templatePrefix, 'slug' => $templateSlug] = $this->utils->getTemplateIdParts( $templateId ); - if ($this->pluginSlug !== $templatePrefix) { - return $return; - } + if ( $this->pluginSlug !== $templatePrefix ) { + return $return; + } - $templatePath = $templateSlug . '.html'; + $templatePath = $templateSlug . '.html'; - if (!is_readable($this->templateDirectory . $templatePath)) { - return $return; - } + if ( ! is_readable( $this->templateDirectory . $templatePath ) ) { + return $return; + } - return $this->getBlockTemplateFromFile($templatePath); - } + return $this->getBlockTemplateFromFile( $templatePath ); + } - public function addBlockTemplates($query_result, $query, $template_type) { - if ('wp_template' !== $template_type) { - return $query_result; - } + public function addBlockTemplates( $query_result, $query, $template_type ) { + if ( 'wp_template' !== $template_type ) { + return $query_result; + } - $post_type = isset($query['post_type']) ? $query['post_type'] : ''; + $post_type = isset( $query['post_type'] ) ? $query['post_type'] : ''; - if ($post_type && $post_type !== $this->postType) { - return $query_result; - } + if ( $post_type && $post_type !== $this->postType ) { + return $query_result; + } - foreach ($this->getBlockTemplates() as $blockTemplate) { - $fits_slug_query = !isset($query['slug__in']) || in_array($blockTemplate->slug, $query['slug__in'], true); - $fits_area_query = !isset($query['area']) || ( property_exists($blockTemplate, 'area') && $blockTemplate->area === $query['area'] ); - $should_include = $fits_slug_query && $fits_area_query; + foreach ( $this->getBlockTemplates() as $blockTemplate ) { + $fits_slug_query = ! isset( $query['slug__in'] ) || in_array( $blockTemplate->slug, $query['slug__in'], true ); + $fits_area_query = ! isset( $query['area'] ) || ( property_exists( $blockTemplate, 'area' ) && $blockTemplate->area === $query['area'] ); + $should_include = $fits_slug_query && $fits_area_query; - if ($should_include) { - $query_result[] = $blockTemplate; - } - } + if ( $should_include ) { + $query_result[] = $blockTemplate; + } + } - return $query_result; - } + return $query_result; + } - public function addThemeTemplates($templates, $theme, $post, $post_type) { - if ($post_type && $post_type !== $this->postType) { - return $templates; - } - foreach ($this->getBlockTemplates() as $blockTemplate) { - $templates[$blockTemplate->slug] = $blockTemplate; - } - return $templates; - } + public function addThemeTemplates( $templates, $theme, $post, $post_type ) { + if ( $post_type && $post_type !== $this->postType ) { + return $templates; + } + foreach ( $this->getBlockTemplates() as $blockTemplate ) { + $templates[ $blockTemplate->slug ] = $blockTemplate; + } + return $templates; + } - /** - * This is a workaround to ensure the post object passed to `inject_ignored_hooked_blocks_metadata_attributes` contains - * content to prevent the template being empty when saved. The issue currently occurs when WooCommerce enables block hooks, - * and when older versions of `inject_ignored_hooked_blocks_metadata_attributes` are - * used (before https://github.com/WordPress/WordPress/commit/725f302121c84c648c38789b2e88dbd1eb41fa48). - * This can be removed in the future. - * - * To test the issue create a new email, revert template changes, save a color change, then save a color change again. - * When you refresh if the post is blank, the issue is present. - * - * @param \stdClass $changes - */ - public function forcePostContent($changes) { - if (empty($changes->post_content) && !empty($changes->ID)) { - // Find the existing post object. - $post = get_post($changes->ID); - if ($post && !empty($post->post_content)) { - $changes->post_content = $post->post_content; - } - } - return $changes; - } + /** + * This is a workaround to ensure the post object passed to `inject_ignored_hooked_blocks_metadata_attributes` contains + * content to prevent the template being empty when saved. The issue currently occurs when WooCommerce enables block hooks, + * and when older versions of `inject_ignored_hooked_blocks_metadata_attributes` are + * used (before https://github.com/WordPress/WordPress/commit/725f302121c84c648c38789b2e88dbd1eb41fa48). + * This can be removed in the future. + * + * To test the issue create a new email, revert template changes, save a color change, then save a color change again. + * When you refresh if the post is blank, the issue is present. + * + * @param \stdClass $changes + */ + public function forcePostContent( $changes ) { + if ( empty( $changes->post_content ) && ! empty( $changes->ID ) ) { + // Find the existing post object. + $post = get_post( $changes->ID ); + if ( $post && ! empty( $post->post_content ) ) { + $changes->post_content = $post->post_content; + } + } + return $changes; + } - /** - * Add details to templates in editor. - * - * @param WP_Block_Template $block_template Block template object. - * @return WP_Block_Template - */ - public function addBlockTemplateDetails($block_template) { - if (!$block_template || !isset($this->templates[$block_template->slug])) { - return $block_template; - } - if (empty($block_template->title)) { - $block_template->title = $this->templates[$block_template->slug]['title']; - } - if (empty($block_template->description)) { - $block_template->description = $this->templates[$block_template->slug]['description']; - } - return $block_template; - } + /** + * Add details to templates in editor. + * + * @param WP_Block_Template $block_template Block template object. + * @return WP_Block_Template + */ + public function addBlockTemplateDetails( $block_template ) { + if ( ! $block_template || ! isset( $this->templates[ $block_template->slug ] ) ) { + return $block_template; + } + if ( empty( $block_template->title ) ) { + $block_template->title = $this->templates[ $block_template->slug ]['title']; + } + if ( empty( $block_template->description ) ) { + $block_template->description = $this->templates[ $block_template->slug ]['description']; + } + return $block_template; + } - /** - * Initialize template details. This is done at runtime because of localisation. - */ - private function initializeTemplates(): void { - $this->templates['email-general'] = [ - 'title' => __('General Email', 'mailpoet'), - 'description' => __('A general template for emails.', 'mailpoet'), - ]; - $this->templates['simple-light'] = [ - 'title' => __('Simple Light', 'mailpoet'), - 'description' => __('A basic template with header and footer.', 'mailpoet'), - ]; - } + /** + * Initialize template details. This is done at runtime because of localisation. + */ + private function initializeTemplates(): void { + $this->templates['email-general'] = array( + 'title' => __( 'General Email', 'mailpoet' ), + 'description' => __( 'A general template for emails.', 'mailpoet' ), + ); + $this->templates['simple-light'] = array( + 'title' => __( 'Simple Light', 'mailpoet' ), + 'description' => __( 'A basic template with header and footer.', 'mailpoet' ), + ); + } - private function initializeApi(): void { - register_post_meta( - 'wp_template', - self::MAILPOET_EMAIL_META_THEME_TYPE, - [ - 'show_in_rest' => [ - 'schema' => (new Email_Styles_Schema())->getSchema(), - ], - 'single' => true, - 'type' => 'object', - 'default' => self::MAILPOET_TEMPLATE_EMPTY_THEME, - ] - ); - register_rest_field( - 'wp_template', - self::MAILPOET_EMAIL_META_THEME_TYPE, - [ - 'get_callback' => function($object) { - return $this->getBlockTemplateTheme($object['id'], $object['wp_id']); - }, - 'update_callback' => function($value, $template) { - return update_post_meta($template->wp_id, self::MAILPOET_EMAIL_META_THEME_TYPE, $value); - }, - 'schema' => (new Email_Styles_Schema())->getSchema(), - ] - ); - } + private function initializeApi(): void { + register_post_meta( + 'wp_template', + self::MAILPOET_EMAIL_META_THEME_TYPE, + array( + 'show_in_rest' => array( + 'schema' => ( new Email_Styles_Schema() )->getSchema(), + ), + 'single' => true, + 'type' => 'object', + 'default' => self::MAILPOET_TEMPLATE_EMPTY_THEME, + ) + ); + register_rest_field( + 'wp_template', + self::MAILPOET_EMAIL_META_THEME_TYPE, + array( + 'get_callback' => function ( $object ) { + return $this->getBlockTemplateTheme( $object['id'], $object['wp_id'] ); + }, + 'update_callback' => function ( $value, $template ) { + return update_post_meta( $template->wp_id, self::MAILPOET_EMAIL_META_THEME_TYPE, $value ); + }, + 'schema' => ( new Email_Styles_Schema() )->getSchema(), + ) + ); + } - /** - * Gets block templates indexed by ID. - */ - private function getBlockTemplates() { - $blockTemplates = array_map(function($templateSlug) { - return $this->getBlockTemplateFromFile($templateSlug . '.html'); - }, array_keys($this->templates)); - $customTemplates = $this->getCustomTemplates(); // From the DB. - $customTemplateIds = wp_list_pluck($customTemplates, 'id'); + /** + * Gets block templates indexed by ID. + */ + private function getBlockTemplates() { + $blockTemplates = array_map( + function ( $templateSlug ) { + return $this->getBlockTemplateFromFile( $templateSlug . '.html' ); + }, + array_keys( $this->templates ) + ); + $customTemplates = $this->getCustomTemplates(); // From the DB. + $customTemplateIds = wp_list_pluck( $customTemplates, 'id' ); - // Combine to remove duplicates if a custom template has the same ID as a file template. - return array_column( - array_merge( - $customTemplates, - array_filter( - $blockTemplates, - function($blockTemplate) use ($customTemplateIds) { - return !in_array($blockTemplate->id, $customTemplateIds, true); - } - ), - ), - null, - 'id' - ); - } + // Combine to remove duplicates if a custom template has the same ID as a file template. + return array_column( + array_merge( + $customTemplates, + array_filter( + $blockTemplates, + function ( $blockTemplate ) use ( $customTemplateIds ) { + return ! in_array( $blockTemplate->id, $customTemplateIds, true ); + } + ), + ), + null, + 'id' + ); + } - private function getBlockTemplateFromFile(string $template) { - $template_slug = $this->utils->getBlockTemplateSlugFromPath($template); - $templateObject = (object)[ - 'slug' => $template_slug, - 'id' => $this->pluginSlug . '//' . $template_slug, - 'title' => $this->templates[$template_slug]['title'] ?? '', - 'description' => $this->templates[$template_slug]['description'] ?? '', - 'path' => $this->templateDirectory . $template, - 'type' => 'wp_template', - 'theme' => $this->pluginSlug, - 'source' => 'plugin', - 'post_types' => [ - $this->postType, - ], - ]; - return $this->utils->buildBlockTemplateFromFile($templateObject); - } + private function getBlockTemplateFromFile( string $template ) { + $template_slug = $this->utils->getBlockTemplateSlugFromPath( $template ); + $templateObject = (object) array( + 'slug' => $template_slug, + 'id' => $this->pluginSlug . '//' . $template_slug, + 'title' => $this->templates[ $template_slug ]['title'] ?? '', + 'description' => $this->templates[ $template_slug ]['description'] ?? '', + 'path' => $this->templateDirectory . $template, + 'type' => 'wp_template', + 'theme' => $this->pluginSlug, + 'source' => 'plugin', + 'post_types' => array( + $this->postType, + ), + ); + return $this->utils->buildBlockTemplateFromFile( $templateObject ); + } - private function getCustomTemplates($slugs = [], $template_type = 'wp_template') { - $check_query_args = [ - 'post_type' => $template_type, - 'posts_per_page' => -1, - 'no_found_rows' => true, - 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query - [ - 'taxonomy' => 'wp_theme', - 'field' => 'name', - 'terms' => [ $this->pluginSlug, get_stylesheet() ], - ], - ], - ]; + private function getCustomTemplates( $slugs = array(), $template_type = 'wp_template' ) { + $check_query_args = array( + 'post_type' => $template_type, + 'posts_per_page' => -1, + 'no_found_rows' => true, + 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + array( + 'taxonomy' => 'wp_theme', + 'field' => 'name', + 'terms' => array( $this->pluginSlug, get_stylesheet() ), + ), + ), + ); - if (is_array($slugs) && count($slugs) > 0) { - $check_query_args['post_name__in'] = $slugs; - } + if ( is_array( $slugs ) && count( $slugs ) > 0 ) { + $check_query_args['post_name__in'] = $slugs; + } - $check_query = new \WP_Query($check_query_args); - $custom_templates = $check_query->posts; + $check_query = new \WP_Query( $check_query_args ); + $custom_templates = $check_query->posts; - return array_map( - function($custom_template) { - return $this->utils->buildBlockTemplateFromPost($custom_template); - }, - $custom_templates - ); - } + return array_map( + function ( $custom_template ) { + return $this->utils->buildBlockTemplateFromPost( $custom_template ); + }, + $custom_templates + ); + } - private function getCustomTemplateTheme($templateWpId) { - if (!$templateWpId) { - return null; - } - $theme = get_post_meta($templateWpId, self::MAILPOET_EMAIL_META_THEME_TYPE, true); - if (is_array($theme) && isset($theme['styles'])) { - return $theme; - } - return null; - } + private function getCustomTemplateTheme( $templateWpId ) { + if ( ! $templateWpId ) { + return null; + } + $theme = get_post_meta( $templateWpId, self::MAILPOET_EMAIL_META_THEME_TYPE, true ); + if ( is_array( $theme ) && isset( $theme['styles'] ) ) { + return $theme; + } + return null; + } } diff --git a/packages/php/email-editor/src/Engine/Templates/class-utils.php b/packages/php/email-editor/src/Engine/Templates/class-utils.php index 83e1afbb81..30f8d909a5 100644 --- a/packages/php/email-editor/src/Engine/Templates/class-utils.php +++ b/packages/php/email-editor/src/Engine/Templates/class-utils.php @@ -7,88 +7,88 @@ use WP_Error; // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps class Utils { - /** - * Gets the prefix and slug from the template ID. - * - * @param string $templateId Id of the template in prefix//slug format. - * @return array Associative array with keys 'prefix' and 'slug'. - */ - public function getTemplateIdParts($templateId) { - $template_name_parts = explode('//', $templateId); + /** + * Gets the prefix and slug from the template ID. + * + * @param string $templateId Id of the template in prefix//slug format. + * @return array Associative array with keys 'prefix' and 'slug'. + */ + public function getTemplateIdParts( $templateId ) { + $template_name_parts = explode( '//', $templateId ); - if (count($template_name_parts) < 2) { - return [ - 'prefix' => '', - 'slug' => '', - ]; - } + if ( count( $template_name_parts ) < 2 ) { + return array( + 'prefix' => '', + 'slug' => '', + ); + } - return [ - 'prefix' => $template_name_parts[0], - 'slug' => $template_name_parts[1], - ]; - } + return array( + 'prefix' => $template_name_parts[0], + 'slug' => $template_name_parts[1], + ); + } - public static function getBlockTemplateSlugFromPath($path) { - return basename($path, '.html'); - } + public static function getBlockTemplateSlugFromPath( $path ) { + return basename( $path, '.html' ); + } - public function buildBlockTemplateFromPost($post) { - $terms = get_the_terms($post, 'wp_theme'); + public function buildBlockTemplateFromPost( $post ) { + $terms = get_the_terms( $post, 'wp_theme' ); - if (is_wp_error($terms)) { - return $terms; - } + if ( is_wp_error( $terms ) ) { + return $terms; + } - if (!$terms) { - return new WP_Error('template_missing_theme', 'No theme is defined for this template.'); - } + if ( ! $terms ) { + return new WP_Error( 'template_missing_theme', 'No theme is defined for this template.' ); + } - $templatePrefix = $terms[0]->name; - $templateSlug = $post->post_name; - $templateId = $templatePrefix . '//' . $templateSlug; + $templatePrefix = $terms[0]->name; + $templateSlug = $post->post_name; + $templateId = $templatePrefix . '//' . $templateSlug; - $template = new WP_Block_Template(); - $template->wp_id = $post->ID; - $template->id = $templateId; - $template->theme = $templatePrefix; - $template->content = $post->post_content ? $post->post_content : '

empty

'; - $template->slug = $templateSlug; - $template->source = 'custom'; - $template->type = $post->post_type; - $template->description = $post->post_excerpt; - $template->title = $post->post_title; - $template->status = $post->post_status; - $template->has_theme_file = false; - $template->is_custom = true; - $template->post_types = []; + $template = new WP_Block_Template(); + $template->wp_id = $post->ID; + $template->id = $templateId; + $template->theme = $templatePrefix; + $template->content = $post->post_content ? $post->post_content : '

empty

'; + $template->slug = $templateSlug; + $template->source = 'custom'; + $template->type = $post->post_type; + $template->description = $post->post_excerpt; + $template->title = $post->post_title; + $template->status = $post->post_status; + $template->has_theme_file = false; + $template->is_custom = true; + $template->post_types = array(); - if ('wp_template_part' === $post->post_type) { - $type_terms = get_the_terms($post, 'wp_template_part_area'); + if ( 'wp_template_part' === $post->post_type ) { + $type_terms = get_the_terms( $post, 'wp_template_part_area' ); - if (!is_wp_error($type_terms) && false !== $type_terms) { - $template->area = $type_terms[0]->name; - } - } + if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { + $template->area = $type_terms[0]->name; + } + } - return $template; - } + return $template; + } - public function buildBlockTemplateFromFile($templateObject): WP_Block_Template { - $template = new WP_Block_Template(); - $template->id = $templateObject->id; - $template->theme = $templateObject->theme; - $template->content = (string)file_get_contents($templateObject->path); - $template->source = $templateObject->source; - $template->slug = $templateObject->slug; - $template->type = $templateObject->type; - $template->title = $templateObject->title; - $template->description = $templateObject->description; - $template->status = 'publish'; - $template->has_theme_file = false; - $template->post_types = $templateObject->post_types; - $template->is_custom = false; // Templates are only custom if they are loaded from the DB. - $template->area = 'uncategorized'; - return $template; - } + public function buildBlockTemplateFromFile( $templateObject ): WP_Block_Template { + $template = new WP_Block_Template(); + $template->id = $templateObject->id; + $template->theme = $templateObject->theme; + $template->content = (string) file_get_contents( $templateObject->path ); + $template->source = $templateObject->source; + $template->slug = $templateObject->slug; + $template->type = $templateObject->type; + $template->title = $templateObject->title; + $template->description = $templateObject->description; + $template->status = 'publish'; + $template->has_theme_file = false; + $template->post_types = $templateObject->post_types; + $template->is_custom = false; // Templates are only custom if they are loaded from the DB. + $template->area = 'uncategorized'; + return $template; + } } diff --git a/packages/php/email-editor/src/Engine/class-email-api-controller.php b/packages/php/email-editor/src/Engine/class-email-api-controller.php index de3b26d27d..95f84ec089 100644 --- a/packages/php/email-editor/src/Engine/class-email-api-controller.php +++ b/packages/php/email-editor/src/Engine/class-email-api-controller.php @@ -5,22 +5,22 @@ namespace MailPoet\EmailEditor\Engine; use MailPoet\EmailEditor\Validator\Builder; class Email_Api_Controller { - /** - * @return array - Email specific data such styles. - */ - public function getEmailData(): array { - // Here comes code getting Email specific data that will be passed on 'email_data' attribute - return []; - } + /** + * @return array - Email specific data such styles. + */ + public function getEmailData(): array { + // Here comes code getting Email specific data that will be passed on 'email_data' attribute + return array(); + } - /** - * Update Email specific data we store. - */ - public function saveEmailData(array $data, \WP_Post $emailPost): void { - // Here comes code saving of Email specific data that will be passed on 'email_data' attribute - } + /** + * Update Email specific data we store. + */ + public function saveEmailData( array $data, \WP_Post $emailPost ): void { + // Here comes code saving of Email specific data that will be passed on 'email_data' attribute + } - public function getEmailDataSchema(): array { - return Builder::object()->toArray(); - } + public function getEmailDataSchema(): array { + return Builder::object()->toArray(); + } } diff --git a/packages/php/email-editor/src/Engine/class-email-editor.php b/packages/php/email-editor/src/Engine/class-email-editor.php index 07255951f9..7044ef915f 100644 --- a/packages/php/email-editor/src/Engine/class-email-editor.php +++ b/packages/php/email-editor/src/Engine/class-email-editor.php @@ -13,116 +13,123 @@ use WP_Theme_JSON; * See register_post_type for details about EmailPostType args. */ class Email_Editor { - public const MAILPOET_EMAIL_META_THEME_TYPE = 'mailpoet_email_theme'; + public const MAILPOET_EMAIL_META_THEME_TYPE = 'mailpoet_email_theme'; - private Email_Api_Controller $emailApiController; - private Templates $templates; - private Template_Preview $templatePreview; - private Patterns $patterns; - private Settings_Controller $settingsController; + private Email_Api_Controller $emailApiController; + private Templates $templates; + private Template_Preview $templatePreview; + private Patterns $patterns; + private Settings_Controller $settingsController; - public function __construct( - Email_Api_Controller $emailApiController, - Templates $templates, - Template_Preview $templatePreview, - Patterns $patterns, - Settings_Controller $settingsController - ) { - $this->emailApiController = $emailApiController; - $this->templates = $templates; - $this->templatePreview = $templatePreview; - $this->patterns = $patterns; - $this->settingsController = $settingsController; - } + public function __construct( + Email_Api_Controller $emailApiController, + Templates $templates, + Template_Preview $templatePreview, + Patterns $patterns, + Settings_Controller $settingsController + ) { + $this->emailApiController = $emailApiController; + $this->templates = $templates; + $this->templatePreview = $templatePreview; + $this->patterns = $patterns; + $this->settingsController = $settingsController; + } - public function initialize(): void { - do_action('mailpoet_email_editor_initialized'); - add_filter('mailpoet_email_editor_rendering_theme_styles', [$this, 'extendEmailThemeStyles'], 10, 2); - $this->registerBlockTemplates(); - $this->registerBlockPatterns(); - $this->registerEmailPostTypes(); - $this->registerEmailPostSendStatus(); - $isEditorPage = apply_filters('mailpoet_is_email_editor_page', false); - if ($isEditorPage) { - $this->extendEmailPostApi(); - $this->settingsController->init(); - } - } + public function initialize(): void { + do_action( 'mailpoet_email_editor_initialized' ); + add_filter( 'mailpoet_email_editor_rendering_theme_styles', array( $this, 'extendEmailThemeStyles' ), 10, 2 ); + $this->registerBlockTemplates(); + $this->registerBlockPatterns(); + $this->registerEmailPostTypes(); + $this->registerEmailPostSendStatus(); + $isEditorPage = apply_filters( 'mailpoet_is_email_editor_page', false ); + if ( $isEditorPage ) { + $this->extendEmailPostApi(); + $this->settingsController->init(); + } + } - private function registerBlockTemplates(): void { - // Since we cannot currently disable blocks in the editor for specific templates, disable templates when viewing site editor. @see https://github.com/WordPress/gutenberg/issues/41062 - if (strstr(wp_unslash($_SERVER['REQUEST_URI'] ?? ''), 'site-editor.php') === false) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - $this->templates->initialize(); - $this->templatePreview->initialize(); - } - } + private function registerBlockTemplates(): void { + // Since we cannot currently disable blocks in the editor for specific templates, disable templates when viewing site editor. @see https://github.com/WordPress/gutenberg/issues/41062 + if ( strstr( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ), 'site-editor.php' ) === false ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $this->templates->initialize(); + $this->templatePreview->initialize(); + } + } - private function registerBlockPatterns(): void { - $this->patterns->initialize(); - } + private function registerBlockPatterns(): void { + $this->patterns->initialize(); + } - /** - * Register all custom post types that should be edited via the email editor - * The post types are added via mailpoet_email_editor_post_types filter. - */ - private function registerEmailPostTypes(): void { - foreach ($this->getPostTypes() as $postType) { - register_post_type( - $postType['name'], - array_merge($this->getDefaultEmailPostArgs(), $postType['args']) - ); - } - } + /** + * Register all custom post types that should be edited via the email editor + * The post types are added via mailpoet_email_editor_post_types filter. + */ + private function registerEmailPostTypes(): void { + foreach ( $this->getPostTypes() as $postType ) { + register_post_type( + $postType['name'], + array_merge( $this->getDefaultEmailPostArgs(), $postType['args'] ) + ); + } + } - /** - * @phpstan-return EmailPostType[] - */ - private function getPostTypes(): array { - $postTypes = []; - return apply_filters('mailpoet_email_editor_post_types', $postTypes); - } + /** + * @phpstan-return EmailPostType[] + */ + private function getPostTypes(): array { + $postTypes = array(); + return apply_filters( 'mailpoet_email_editor_post_types', $postTypes ); + } - private function getDefaultEmailPostArgs(): array { - return [ - 'public' => false, - 'hierarchical' => false, - 'show_ui' => true, - 'show_in_menu' => false, - 'show_in_nav_menus' => false, - 'supports' => ['editor', 'title', 'custom-fields'], // 'custom-fields' is required for loading meta fields via API - 'has_archive' => true, - 'show_in_rest' => true, // Important to enable Gutenberg editor - ]; - } + private function getDefaultEmailPostArgs(): array { + return array( + 'public' => false, + 'hierarchical' => false, + 'show_ui' => true, + 'show_in_menu' => false, + 'show_in_nav_menus' => false, + 'supports' => array( 'editor', 'title', 'custom-fields' ), // 'custom-fields' is required for loading meta fields via API + 'has_archive' => true, + 'show_in_rest' => true, // Important to enable Gutenberg editor + ); + } - private function registerEmailPostSendStatus(): void { - register_post_status('sent', [ - 'public' => false, - 'exclude_from_search' => true, - 'internal' => true, // for now, we hide it, if we use the status in the listings we may flip this and following values - 'show_in_admin_all_list' => false, - 'show_in_admin_status_list' => false, - ]); - } + private function registerEmailPostSendStatus(): void { + register_post_status( + 'sent', + array( + 'public' => false, + 'exclude_from_search' => true, + 'internal' => true, // for now, we hide it, if we use the status in the listings we may flip this and following values + 'show_in_admin_all_list' => false, + 'show_in_admin_status_list' => false, + ) + ); + } - public function extendEmailPostApi() { - $emailPostTypes = array_column($this->getPostTypes(), 'name'); - register_rest_field($emailPostTypes, 'email_data', [ - 'get_callback' => [$this->emailApiController, 'getEmailData'], - 'update_callback' => [$this->emailApiController, 'saveEmailData'], - 'schema' => $this->emailApiController->getEmailDataSchema(), - ]); - } + public function extendEmailPostApi() { + $emailPostTypes = array_column( $this->getPostTypes(), 'name' ); + register_rest_field( + $emailPostTypes, + 'email_data', + array( + 'get_callback' => array( $this->emailApiController, 'getEmailData' ), + 'update_callback' => array( $this->emailApiController, 'saveEmailData' ), + 'schema' => $this->emailApiController->getEmailDataSchema(), + ) + ); + } - public function getEmailThemeDataSchema(): array { - return (new Email_Styles_Schema())->getSchema(); - } + public function getEmailThemeDataSchema(): array { + return ( new Email_Styles_Schema() )->getSchema(); + } - public function extendEmailThemeStyles(WP_Theme_JSON $theme, WP_Post $post): WP_Theme_JSON { - $emailTheme = get_post_meta($post->ID, Email_Editor::MAILPOET_EMAIL_META_THEME_TYPE, true); - if ($emailTheme && is_array($emailTheme)) { - $theme->merge(new WP_Theme_JSON($emailTheme)); - } - return $theme; - } + public function extendEmailThemeStyles( WP_Theme_JSON $theme, WP_Post $post ): WP_Theme_JSON { + $emailTheme = get_post_meta( $post->ID, self::MAILPOET_EMAIL_META_THEME_TYPE, true ); + if ( $emailTheme && is_array( $emailTheme ) ) { + $theme->merge( new WP_Theme_JSON( $emailTheme ) ); + } + return $theme; + } } diff --git a/packages/php/email-editor/src/Engine/class-email-styles-schema.php b/packages/php/email-editor/src/Engine/class-email-styles-schema.php index 3382770e28..f649ae6720 100644 --- a/packages/php/email-editor/src/Engine/class-email-styles-schema.php +++ b/packages/php/email-editor/src/Engine/class-email-styles-schema.php @@ -5,64 +5,96 @@ namespace MailPoet\EmailEditor\Engine; use MailPoet\EmailEditor\Validator\Builder; class Email_Styles_Schema { - public function getSchema(): array { - $typographyProps = Builder::object([ - 'fontFamily' => Builder::string()->nullable(), - 'fontSize' => Builder::string()->nullable(), - 'fontStyle' => Builder::string()->nullable(), - 'fontWeight' => Builder::string()->nullable(), - 'letterSpacing' => Builder::string()->nullable(), - 'lineHeight' => Builder::string()->nullable(), - 'textTransform' => Builder::string()->nullable(), - 'textDecoration' => Builder::string()->nullable(), - ])->nullable(); - return Builder::object([ - 'version' => Builder::integer(), - 'styles' => Builder::object([ - 'spacing' => Builder::object([ - 'padding' => Builder::object([ - 'top' => Builder::string(), - 'right' => Builder::string(), - 'bottom' => Builder::string(), - 'left' => Builder::string(), - ])->nullable(), - 'blockGap' => Builder::string()->nullable(), - ])->nullable(), - 'color' => Builder::object([ - 'background' => Builder::string()->nullable(), - 'text' => Builder::string()->nullable(), - ])->nullable(), - 'typography' => $typographyProps, - 'elements' => Builder::object([ - 'heading' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'button' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'link' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h1' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h2' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h3' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h4' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h5' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - 'h6' => Builder::object([ - 'typography' => $typographyProps, - ])->nullable(), - ])->nullable(), - ])->nullable(), - ])->toArray(); - } + public function getSchema(): array { + $typographyProps = Builder::object( + array( + 'fontFamily' => Builder::string()->nullable(), + 'fontSize' => Builder::string()->nullable(), + 'fontStyle' => Builder::string()->nullable(), + 'fontWeight' => Builder::string()->nullable(), + 'letterSpacing' => Builder::string()->nullable(), + 'lineHeight' => Builder::string()->nullable(), + 'textTransform' => Builder::string()->nullable(), + 'textDecoration' => Builder::string()->nullable(), + ) + )->nullable(); + return Builder::object( + array( + 'version' => Builder::integer(), + 'styles' => Builder::object( + array( + 'spacing' => Builder::object( + array( + 'padding' => Builder::object( + array( + 'top' => Builder::string(), + 'right' => Builder::string(), + 'bottom' => Builder::string(), + 'left' => Builder::string(), + ) + )->nullable(), + 'blockGap' => Builder::string()->nullable(), + ) + )->nullable(), + 'color' => Builder::object( + array( + 'background' => Builder::string()->nullable(), + 'text' => Builder::string()->nullable(), + ) + )->nullable(), + 'typography' => $typographyProps, + 'elements' => Builder::object( + array( + 'heading' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'button' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'link' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h1' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h2' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h3' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h4' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h5' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + 'h6' => Builder::object( + array( + 'typography' => $typographyProps, + ) + )->nullable(), + ) + )->nullable(), + ) + )->nullable(), + ) + )->toArray(); + } } diff --git a/packages/php/email-editor/src/Engine/class-settings-controller.php b/packages/php/email-editor/src/Engine/class-settings-controller.php index 4c2be2ef87..4fa8e076fe 100644 --- a/packages/php/email-editor/src/Engine/class-settings-controller.php +++ b/packages/php/email-editor/src/Engine/class-settings-controller.php @@ -4,130 +4,130 @@ namespace MailPoet\EmailEditor\Engine; class Settings_Controller { - const ALLOWED_BLOCK_TYPES = [ - 'core/button', - 'core/buttons', - 'core/paragraph', - 'core/heading', - 'core/column', - 'core/columns', - 'core/image', - 'core/list', - 'core/list-item', - 'core/group', - 'core/spacer' - ]; + const ALLOWED_BLOCK_TYPES = array( + 'core/button', + 'core/buttons', + 'core/paragraph', + 'core/heading', + 'core/column', + 'core/columns', + 'core/image', + 'core/list', + 'core/list-item', + 'core/group', + 'core/spacer', + ); - const DEFAULT_SETTINGS = [ - 'enableCustomUnits' => ['px', '%'], - ]; + const DEFAULT_SETTINGS = array( + 'enableCustomUnits' => array( 'px', '%' ), + ); - private Theme_Controller $themeController; + private Theme_Controller $themeController; - private array $iframeAssets = []; + private array $iframeAssets = array(); - /** - * @param Theme_Controller $themeController - */ - public function __construct( - Theme_Controller $themeController - ) { - $this->themeController = $themeController; - } + /** + * @param Theme_Controller $themeController + */ + public function __construct( + Theme_Controller $themeController + ) { + $this->themeController = $themeController; + } - public function init() { - // We need to initialize these assets early because they are read from global variables $wp_styles and $wp_scripts - // and in later WordPress page load pages they contain stuff we don't want (e.g. html for admin login popup) - // in the post editor this is called directly in post.php - $this->iframeAssets = _wp_get_iframed_editor_assets(); - } + public function init() { + // We need to initialize these assets early because they are read from global variables $wp_styles and $wp_scripts + // and in later WordPress page load pages they contain stuff we don't want (e.g. html for admin login popup) + // in the post editor this is called directly in post.php + $this->iframeAssets = _wp_get_iframed_editor_assets(); + } - public function getSettings(): array { - $coreDefaultSettings = \get_default_block_editor_settings(); - $themeSettings = $this->themeController->getSettings(); + public function getSettings(): array { + $coreDefaultSettings = \get_default_block_editor_settings(); + $themeSettings = $this->themeController->getSettings(); - $settings = array_merge($coreDefaultSettings, self::DEFAULT_SETTINGS); - $settings['allowedBlockTypes'] = self::ALLOWED_BLOCK_TYPES; - // Assets for iframe editor (component styles, scripts, etc.) - $settings['__unstableResolvedAssets'] = $this->iframeAssets; - $editorContentStyles = file_get_contents(__DIR__ . '/content-editor.css'); - $sharesContentStyles = file_get_contents(__DIR__ . '/content-shared.css'); - $settings['styles'] = [ - ['css' => $editorContentStyles], - ['css' => $sharesContentStyles], - ]; + $settings = array_merge( $coreDefaultSettings, self::DEFAULT_SETTINGS ); + $settings['allowedBlockTypes'] = self::ALLOWED_BLOCK_TYPES; + // Assets for iframe editor (component styles, scripts, etc.) + $settings['__unstableResolvedAssets'] = $this->iframeAssets; + $editorContentStyles = file_get_contents( __DIR__ . '/content-editor.css' ); + $sharesContentStyles = file_get_contents( __DIR__ . '/content-shared.css' ); + $settings['styles'] = array( + array( 'css' => $editorContentStyles ), + array( 'css' => $sharesContentStyles ), + ); - $settings['__experimentalFeatures'] = $themeSettings; - // Controls which alignment options are available for blocks - $settings['supportsLayout'] = true; // Allow using default layouts - $settings['__unstableIsBlockBasedTheme'] = true; // For default setting this to true disables wide and full alignments - return $settings; - } + $settings['__experimentalFeatures'] = $themeSettings; + // Controls which alignment options are available for blocks + $settings['supportsLayout'] = true; // Allow using default layouts + $settings['__unstableIsBlockBasedTheme'] = true; // For default setting this to true disables wide and full alignments + return $settings; + } - /** - * @return array{contentSize: string, wideSize: string} - */ - public function getLayout(): array { - $layoutSettings = $this->themeController->getLayoutSettings(); - return [ - 'contentSize' => $layoutSettings['contentSize'], - 'wideSize' => $layoutSettings['wideSize'], - ]; - } + /** + * @return array{contentSize: string, wideSize: string} + */ + public function getLayout(): array { + $layoutSettings = $this->themeController->getLayoutSettings(); + return array( + 'contentSize' => $layoutSettings['contentSize'], + 'wideSize' => $layoutSettings['wideSize'], + ); + } - /** - * @return array{ - * spacing: array{ - * blockGap: string, - * padding: array{bottom: string, left: string, right: string, top: string} - * }, - * color: array{ - * background: string - * }, - * typography: array{ - * fontFamily: string - * } - * } - */ - public function getEmailStyles(): array { - $theme = $this->getTheme(); - return $theme->get_data()['styles']; - } + /** + * @return array{ + * spacing: array{ + * blockGap: string, + * padding: array{bottom: string, left: string, right: string, top: string} + * }, + * color: array{ + * background: string + * }, + * typography: array{ + * fontFamily: string + * } + * } + */ + public function getEmailStyles(): array { + $theme = $this->getTheme(); + return $theme->get_data()['styles']; + } - public function getLayoutWidthWithoutPadding(): string { - $styles = $this->getEmailStyles(); - $layout = $this->getLayout(); - $width = $this->parseNumberFromStringWithPixels($layout['contentSize']); - $width -= $this->parseNumberFromStringWithPixels($styles['spacing']['padding']['left']); - $width -= $this->parseNumberFromStringWithPixels($styles['spacing']['padding']['right']); - return "{$width}px"; - } + public function getLayoutWidthWithoutPadding(): string { + $styles = $this->getEmailStyles(); + $layout = $this->getLayout(); + $width = $this->parseNumberFromStringWithPixels( $layout['contentSize'] ); + $width -= $this->parseNumberFromStringWithPixels( $styles['spacing']['padding']['left'] ); + $width -= $this->parseNumberFromStringWithPixels( $styles['spacing']['padding']['right'] ); + return "{$width}px"; + } - public function parseStylesToArray(string $styles): array { - $styles = explode(';', $styles); - $parsedStyles = []; - foreach ($styles as $style) { - $style = explode(':', $style); - if (count($style) === 2) { - $parsedStyles[trim($style[0])] = trim($style[1]); - } - } - return $parsedStyles; - } + public function parseStylesToArray( string $styles ): array { + $styles = explode( ';', $styles ); + $parsedStyles = array(); + foreach ( $styles as $style ) { + $style = explode( ':', $style ); + if ( count( $style ) === 2 ) { + $parsedStyles[ trim( $style[0] ) ] = trim( $style[1] ); + } + } + return $parsedStyles; + } - public function parseNumberFromStringWithPixels(string $string): float { - return (float)str_replace('px', '', $string); - } + public function parseNumberFromStringWithPixels( string $string ): float { + return (float) str_replace( 'px', '', $string ); + } - public function getTheme(): \WP_Theme_JSON { - return $this->themeController->getTheme(); - } + public function getTheme(): \WP_Theme_JSON { + return $this->themeController->getTheme(); + } - public function translateSlugToFontSize(string $fontSize): string { - return $this->themeController->translateSlugToFontSize($fontSize); - } + public function translateSlugToFontSize( string $fontSize ): string { + return $this->themeController->translateSlugToFontSize( $fontSize ); + } - public function translateSlugToColor(string $colorSlug): string { - return $this->themeController->translateSlugToColor($colorSlug); - } + public function translateSlugToColor( string $colorSlug ): string { + return $this->themeController->translateSlugToColor( $colorSlug ); + } } diff --git a/packages/php/email-editor/src/Engine/class-theme-controller.php b/packages/php/email-editor/src/Engine/class-theme-controller.php index c51ed422d5..1d9cf4966e 100644 --- a/packages/php/email-editor/src/Engine/class-theme-controller.php +++ b/packages/php/email-editor/src/Engine/class-theme-controller.php @@ -11,233 +11,233 @@ use WP_Theme_JSON_Resolver; * This class is responsible for accessing data defined by the theme.json. */ class Theme_Controller { - private WP_Theme_JSON $coreTheme; - private WP_Theme_JSON $baseTheme; + private WP_Theme_JSON $coreTheme; + private WP_Theme_JSON $baseTheme; - public function __construct() { - $this->coreTheme = WP_Theme_JSON_Resolver::get_core_data(); - $this->baseTheme = new WP_Theme_JSON((array)json_decode((string)file_get_contents(dirname(__FILE__) . '/theme.json'), true), 'default'); - } + public function __construct() { + $this->coreTheme = WP_Theme_JSON_Resolver::get_core_data(); + $this->baseTheme = new WP_Theme_JSON( (array) json_decode( (string) file_get_contents( __DIR__ . '/theme.json' ), true ), 'default' ); + } - /** - * Gets combined theme data from the core and base theme, merged with the currently rendered template. - * - * @return WP_Theme_JSON - */ - public function getTheme(): WP_Theme_JSON { - $theme = new WP_Theme_JSON(); - $theme->merge($this->coreTheme); - $theme->merge($this->baseTheme); + /** + * Gets combined theme data from the core and base theme, merged with the currently rendered template. + * + * @return WP_Theme_JSON + */ + public function getTheme(): WP_Theme_JSON { + $theme = new WP_Theme_JSON(); + $theme->merge( $this->coreTheme ); + $theme->merge( $this->baseTheme ); - if (Renderer::getTheme() !== null) { - $theme->merge(Renderer::getTheme()); - } + if ( Renderer::getTheme() !== null ) { + $theme->merge( Renderer::getTheme() ); + } - return apply_filters('mailpoet_email_editor_theme_json', $theme); - } + return apply_filters( 'mailpoet_email_editor_theme_json', $theme ); + } - private function recursiveReplacePresets($values, $presets) { - foreach ($values as $key => $value) { - if (is_array($value)) { - $values[$key] = $this->recursiveReplacePresets($value, $presets); - } elseif (is_string($value)) { - $values[$key] = preg_replace(array_keys($presets), array_values($presets), $value); - } else { - $values[$key] = $value; - } - } - return $values; - } + private function recursiveReplacePresets( $values, $presets ) { + foreach ( $values as $key => $value ) { + if ( is_array( $value ) ) { + $values[ $key ] = $this->recursiveReplacePresets( $value, $presets ); + } elseif ( is_string( $value ) ) { + $values[ $key ] = preg_replace( array_keys( $presets ), array_values( $presets ), $value ); + } else { + $values[ $key ] = $value; + } + } + return $values; + } - private function recursiveExtractPresetVariables($styles) { - foreach ($styles as $key => $styleValue) { - if (is_array($styleValue)) { - $styles[$key] = $this->recursiveExtractPresetVariables($styleValue); - } elseif (strpos($styleValue, 'var:preset|') === 0) { - $styles[$key] = 'var(--wp--' . str_replace('|', '--', str_replace('var:', '', $styleValue)) . ')'; - } else { - $styles[$key] = $styleValue; - } - } - return $styles; - } + private function recursiveExtractPresetVariables( $styles ) { + foreach ( $styles as $key => $styleValue ) { + if ( is_array( $styleValue ) ) { + $styles[ $key ] = $this->recursiveExtractPresetVariables( $styleValue ); + } elseif ( strpos( $styleValue, 'var:preset|' ) === 0 ) { + $styles[ $key ] = 'var(--wp--' . str_replace( '|', '--', str_replace( 'var:', '', $styleValue ) ) . ')'; + } else { + $styles[ $key ] = $styleValue; + } + } + return $styles; + } - /** - * Get styles for the e-mail. - * - * @param \WP_Post|null $post Post object. - * @param \WP_Block_Template|null $template Template object. - * @param bool $convertPresets Convert presets to valid CSS values. - * @return array{ - * spacing: array{ - * blockGap: string, - * padding: array{bottom: string, left: string, right: string, top: string} - * }, - * color: array{ - * background: string - * }, - * typography: array{ - * fontFamily: string - * } - * } - */ - public function getStyles($post = null, $template = null): array { - $themeStyles = $this->getTheme()->get_data()['styles']; + /** + * Get styles for the e-mail. + * + * @param \WP_Post|null $post Post object. + * @param \WP_Block_Template|null $template Template object. + * @param bool $convertPresets Convert presets to valid CSS values. + * @return array{ + * spacing: array{ + * blockGap: string, + * padding: array{bottom: string, left: string, right: string, top: string} + * }, + * color: array{ + * background: string + * }, + * typography: array{ + * fontFamily: string + * } + * } + */ + public function getStyles( $post = null, $template = null ): array { + $themeStyles = $this->getTheme()->get_data()['styles']; - // Replace template styles. - if ($template && $template->wp_id) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $templateTheme = (array)get_post_meta($template->wp_id, 'mailpoet_email_theme', true); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $templateStyles = (array)($templateTheme['styles'] ?? []); - $themeStyles = array_replace_recursive($themeStyles, $templateStyles); - } + // Replace template styles. + if ( $template && $template->wp_id ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $templateTheme = (array) get_post_meta( $template->wp_id, 'mailpoet_email_theme', true ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $templateStyles = (array) ( $templateTheme['styles'] ?? array() ); + $themeStyles = array_replace_recursive( $themeStyles, $templateStyles ); + } - // Extract preset variables - $themeStyles = $this->recursiveExtractPresetVariables($themeStyles); + // Extract preset variables + $themeStyles = $this->recursiveExtractPresetVariables( $themeStyles ); - // Replace preset values. - $variables = $this->getVariablesValuesMap(); - $presets = []; + // Replace preset values. + $variables = $this->getVariablesValuesMap(); + $presets = array(); - foreach ($variables as $varName => $varValue) { - $varPattern = '/var\(' . preg_quote($varName, '/') . '\)/i'; - $presets[$varPattern] = $varValue; - } + foreach ( $variables as $varName => $varValue ) { + $varPattern = '/var\(' . preg_quote( $varName, '/' ) . '\)/i'; + $presets[ $varPattern ] = $varValue; + } - $themeStyles = $this->recursiveReplacePresets($themeStyles, $presets); + $themeStyles = $this->recursiveReplacePresets( $themeStyles, $presets ); - return $themeStyles; - } + return $themeStyles; + } - public function getSettings(): array { - $emailEditorThemeSettings = $this->getTheme()->get_settings(); - $siteThemeSettings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); - $emailEditorThemeSettings['color']['palette']['theme'] = []; - if (isset($siteThemeSettings['color']['palette']['theme'])) { - $emailEditorThemeSettings['color']['palette']['theme'] = $siteThemeSettings['color']['palette']['theme']; - } - return $emailEditorThemeSettings; - } + public function getSettings(): array { + $emailEditorThemeSettings = $this->getTheme()->get_settings(); + $siteThemeSettings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); + $emailEditorThemeSettings['color']['palette']['theme'] = array(); + if ( isset( $siteThemeSettings['color']['palette']['theme'] ) ) { + $emailEditorThemeSettings['color']['palette']['theme'] = $siteThemeSettings['color']['palette']['theme']; + } + return $emailEditorThemeSettings; + } - public function getLayoutSettings(): array { - return $this->getTheme()->get_settings()['layout']; - } + public function getLayoutSettings(): array { + return $this->getTheme()->get_settings()['layout']; + } - public function getStylesheetFromContext($context, $options = []): string { - return function_exists('gutenberg_style_engine_get_stylesheet_from_context') ? gutenberg_style_engine_get_stylesheet_from_context($context, $options) : wp_style_engine_get_stylesheet_from_context($context, $options); - } + public function getStylesheetFromContext( $context, $options = array() ): string { + return function_exists( 'gutenberg_style_engine_get_stylesheet_from_context' ) ? gutenberg_style_engine_get_stylesheet_from_context( $context, $options ) : wp_style_engine_get_stylesheet_from_context( $context, $options ); + } - public function getStylesheetForRendering($post = null, $template = null): string { - $emailThemeSettings = $this->getSettings(); + public function getStylesheetForRendering( $post = null, $template = null ): string { + $emailThemeSettings = $this->getSettings(); - $cssPresets = ''; - // Font family classes - foreach ($emailThemeSettings['typography']['fontFamilies']['default'] as $fontFamily) { - $cssPresets .= ".has-{$fontFamily['slug']}-font-family { font-family: {$fontFamily['fontFamily']}; } \n"; - } - // Font size classes - foreach ($emailThemeSettings['typography']['fontSizes']['default'] as $fontSize) { - $cssPresets .= ".has-{$fontSize['slug']}-font-size { font-size: {$fontSize['size']}; } \n"; - } - // Color palette classes - $colorDefinitions = array_merge($emailThemeSettings['color']['palette']['theme'], $emailThemeSettings['color']['palette']['default']); - foreach ($colorDefinitions as $color) { - $cssPresets .= ".has-{$color['slug']}-color { color: {$color['color']}; } \n"; - $cssPresets .= ".has-{$color['slug']}-background-color { background-color: {$color['color']}; } \n"; - $cssPresets .= ".has-{$color['slug']}-border-color { border-color: {$color['color']}; } \n"; - } + $cssPresets = ''; + // Font family classes + foreach ( $emailThemeSettings['typography']['fontFamilies']['default'] as $fontFamily ) { + $cssPresets .= ".has-{$fontFamily['slug']}-font-family { font-family: {$fontFamily['fontFamily']}; } \n"; + } + // Font size classes + foreach ( $emailThemeSettings['typography']['fontSizes']['default'] as $fontSize ) { + $cssPresets .= ".has-{$fontSize['slug']}-font-size { font-size: {$fontSize['size']}; } \n"; + } + // Color palette classes + $colorDefinitions = array_merge( $emailThemeSettings['color']['palette']['theme'], $emailThemeSettings['color']['palette']['default'] ); + foreach ( $colorDefinitions as $color ) { + $cssPresets .= ".has-{$color['slug']}-color { color: {$color['color']}; } \n"; + $cssPresets .= ".has-{$color['slug']}-background-color { background-color: {$color['color']}; } \n"; + $cssPresets .= ".has-{$color['slug']}-border-color { border-color: {$color['color']}; } \n"; + } - // Block specific styles - $cssBlocks = ''; - $blocks = $this->getTheme()->get_styles_block_nodes(); - foreach ($blocks as $blockMetadata) { - $cssBlocks .= $this->getTheme()->get_styles_for_block($blockMetadata); - } + // Block specific styles + $cssBlocks = ''; + $blocks = $this->getTheme()->get_styles_block_nodes(); + foreach ( $blocks as $blockMetadata ) { + $cssBlocks .= $this->getTheme()->get_styles_for_block( $blockMetadata ); + } - // Element specific styles - $elementsStyles = $this->getTheme()->get_raw_data()['styles']['elements'] ?? []; + // Element specific styles + $elementsStyles = $this->getTheme()->get_raw_data()['styles']['elements'] ?? array(); - // Because the section styles is not a part of the output the `get_styles_block_nodes` method, we need to get it separately - if ($template && $template->wp_id) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $templateTheme = (array)get_post_meta($template->wp_id, 'mailpoet_email_theme', true); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $templateStyles = (array)($templateTheme['styles'] ?? []); - $templateElements = $templateStyles['elements'] ?? []; - $elementsStyles = array_replace_recursive((array)$elementsStyles, (array)$templateElements); - } + // Because the section styles is not a part of the output the `get_styles_block_nodes` method, we need to get it separately + if ( $template && $template->wp_id ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $templateTheme = (array) get_post_meta( $template->wp_id, 'mailpoet_email_theme', true ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $templateStyles = (array) ( $templateTheme['styles'] ?? array() ); + $templateElements = $templateStyles['elements'] ?? array(); + $elementsStyles = array_replace_recursive( (array) $elementsStyles, (array) $templateElements ); + } - if ($post) { - $postTheme = (array)get_post_meta($post->ID, 'mailpoet_email_theme', true); - $postStyles = (array)($postTheme['styles'] ?? []); - $postElements = $postStyles['elements'] ?? []; - $elementsStyles = array_replace_recursive((array)$elementsStyles, (array)$postElements); - } + if ( $post ) { + $postTheme = (array) get_post_meta( $post->ID, 'mailpoet_email_theme', true ); + $postStyles = (array) ( $postTheme['styles'] ?? array() ); + $postElements = $postStyles['elements'] ?? array(); + $elementsStyles = array_replace_recursive( (array) $elementsStyles, (array) $postElements ); + } - $cssElements = ''; - foreach ($elementsStyles as $key => $elementsStyle) { - $selector = $key; + $cssElements = ''; + foreach ( $elementsStyles as $key => $elementsStyle ) { + $selector = $key; - if ($key === 'button') { - $selector = '.wp-block-button'; - $cssElements .= wp_style_engine_get_styles($elementsStyle, ['selector' => '.wp-block-button'])['css']; - // Add color to link element. - $cssElements .= wp_style_engine_get_styles(['color' => ['text' => $elementsStyle['color']['text'] ?? '']], ['selector' => '.wp-block-button a'])['css']; - continue; - } + if ( $key === 'button' ) { + $selector = '.wp-block-button'; + $cssElements .= wp_style_engine_get_styles( $elementsStyle, array( 'selector' => '.wp-block-button' ) )['css']; + // Add color to link element. + $cssElements .= wp_style_engine_get_styles( array( 'color' => array( 'text' => $elementsStyle['color']['text'] ?? '' ) ), array( 'selector' => '.wp-block-button a' ) )['css']; + continue; + } - switch ($key) { - case 'heading': - $selector = 'h1, h2, h3, h4, h5, h6'; - break; - case 'link': - $selector = 'a:not(.button-link)'; - break; - } + switch ( $key ) { + case 'heading': + $selector = 'h1, h2, h3, h4, h5, h6'; + break; + case 'link': + $selector = 'a:not(.button-link)'; + break; + } - $cssElements .= wp_style_engine_get_styles($elementsStyle, ['selector' => $selector])['css']; - } + $cssElements .= wp_style_engine_get_styles( $elementsStyle, array( 'selector' => $selector ) )['css']; + } - $result = $cssPresets . $cssBlocks . $cssElements; - // Because font-size can by defined by the clamp() function that is not supported in the e-mail clients, we need to replace it to the value. - // Regular expression to match clamp() function and capture its max value - $pattern = '/clamp\([^,]+,\s*[^,]+,\s*([^)]+)\)/'; - // Replace clamp() with its maximum value - $result = (string)preg_replace($pattern, '$1', $result); - return $result; - } + $result = $cssPresets . $cssBlocks . $cssElements; + // Because font-size can by defined by the clamp() function that is not supported in the e-mail clients, we need to replace it to the value. + // Regular expression to match clamp() function and capture its max value + $pattern = '/clamp\([^,]+,\s*[^,]+,\s*([^)]+)\)/'; + // Replace clamp() with its maximum value + $result = (string) preg_replace( $pattern, '$1', $result ); + return $result; + } - public function translateSlugToFontSize(string $fontSize): string { - $settings = $this->getSettings(); - foreach ($settings['typography']['fontSizes']['default'] as $fontSizeDefinition) { - if ($fontSizeDefinition['slug'] === $fontSize) { - return $fontSizeDefinition['size']; - } - } - return $fontSize; - } + public function translateSlugToFontSize( string $fontSize ): string { + $settings = $this->getSettings(); + foreach ( $settings['typography']['fontSizes']['default'] as $fontSizeDefinition ) { + if ( $fontSizeDefinition['slug'] === $fontSize ) { + return $fontSizeDefinition['size']; + } + } + return $fontSize; + } - public function translateSlugToColor(string $colorSlug): string { - $settings = $this->getSettings(); - $colorDefinitions = array_merge($settings['color']['palette']['theme'], $settings['color']['palette']['default']); - foreach ($colorDefinitions as $colorDefinition) { - if ($colorDefinition['slug'] === $colorSlug) { - return strtolower($colorDefinition['color']); - } - } - return $colorSlug; - } + public function translateSlugToColor( string $colorSlug ): string { + $settings = $this->getSettings(); + $colorDefinitions = array_merge( $settings['color']['palette']['theme'], $settings['color']['palette']['default'] ); + foreach ( $colorDefinitions as $colorDefinition ) { + if ( $colorDefinition['slug'] === $colorSlug ) { + return strtolower( $colorDefinition['color'] ); + } + } + return $colorSlug; + } - public function getVariablesValuesMap(): array { - $variablesCss = $this->getTheme()->get_stylesheet(['variables']); - $map = []; - // Regular expression to match CSS variable definitions - $pattern = '/--(.*?):\s*(.*?);/'; + public function getVariablesValuesMap(): array { + $variablesCss = $this->getTheme()->get_stylesheet( array( 'variables' ) ); + $map = array(); + // Regular expression to match CSS variable definitions + $pattern = '/--(.*?):\s*(.*?);/'; - if (preg_match_all($pattern, $variablesCss, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - // '--' . $match[1] is the variable name, $match[2] is the variable value - $map['--' . $match[1]] = $match[2]; - } - } + if ( preg_match_all( $pattern, $variablesCss, $matches, PREG_SET_ORDER ) ) { + foreach ( $matches as $match ) { + // '--' . $match[1] is the variable name, $match[2] is the variable value + $map[ '--' . $match[1] ] = $match[2]; + } + } - return $map; - } + return $map; + } } diff --git a/packages/php/email-editor/src/Engine/content-editor.css b/packages/php/email-editor/src/Engine/content-editor.css index 25027e318d..e87c44e5a8 100644 --- a/packages/php/email-editor/src/Engine/content-editor.css +++ b/packages/php/email-editor/src/Engine/content-editor.css @@ -1,28 +1,28 @@ /* - * Styles for the email editor. + * Styles for the email editor. */ /* * Flex layout used for buttons block for email editor. */ .is-layout-email-flex { - flex-wrap: nowrap; + flex-wrap: nowrap; } :where(body .is-layout-flex) { - gap: var(--wp--style--block-gap, 16px); + gap: var(--wp--style--block-gap, 16px); } .is-mobile-preview .is-layout-email-flex { - display: block; + display: block; } .is-mobile-preview .is-layout-email-flex .block-editor-block-list__block { - padding: 5px 0; - width: 100%; + padding: 5px 0; + width: 100%; } .is-mobile-preview .is-layout-email-flex .wp-block-button__link { - width: 100%; + width: 100%; } /* @@ -30,61 +30,61 @@ * This is needed because we disable layout for core/group, core/column and core/columns blocks, and .is-layout-flex is not applied. */ .wp-block-columns:not(.is-not-stacked-on-mobile) - > .wp-block-column - > .wp-block:first-child, + > .wp-block-column + > .wp-block:first-child, .wp-block-group > .wp-block:first-child { - margin-top: 0; + margin-top: 0; } .wp-block-columns:not(.is-not-stacked-on-mobile) > .wp-block-column > .wp-block, .wp-block-group > .wp-block { - margin-bottom: var(--wp--style--block-gap, 16px); - margin-top: var(--wp--style--block-gap, 16px); + margin-bottom: var(--wp--style--block-gap, 16px); + margin-top: var(--wp--style--block-gap, 16px); } .wp-block-columns:not(.is-not-stacked-on-mobile) - > .wp-block-column - > .wp-block:last-child, + > .wp-block-column + > .wp-block:last-child, .wp-block-group > .wp-block:last-child { - margin-bottom: 0; + margin-bottom: 0; } /* * Use box sizing border box for columns that have defined a width (they have flex-basis set). */ .wp-block-columns:not(.is-not-stacked-on-mobile) - > .wp-block-column[style*='flex-basis'] { - box-sizing: border-box; + > .wp-block-column[style*='flex-basis'] { + box-sizing: border-box; } /* * For the WYSIWYG experience we don't want to display any margins between blocks in the editor */ .wp-block { - clear: both; // for ensuring that floated elements (images) are cleared + clear: both; // for ensuring that floated elements (images) are cleared } /* * Image block enhancements */ .wp-block-image figcaption { - /* Resetting the margin for images in the editor to avoid unexpected spacing */ - margin: 0; + /* Resetting the margin for images in the editor to avoid unexpected spacing */ + margin: 0; } .wp-block-image.alignleft, .wp-block-image.alignright { - margin-inline: 0 0; - text-align: center; + margin-inline: 0 0; + text-align: center; } .wp-block-image.aligncenter { - margin-left: auto; - margin-right: auto; + margin-left: auto; + margin-right: auto; } .wp-block-image.alignright { - margin-left: auto; + margin-left: auto; } /* @@ -95,31 +95,31 @@ ul, ol, ul.has-background, ol.has-background { - padding-left: 40px; + padding-left: 40px; } /* * Override default button border radius which is set in core to 9999px */ .wp-block-button__link { - border-radius: 0; + border-radius: 0; } /* * Mobile preview fixes */ .is-mobile-preview figure > div { - height: auto !important; - width: auto !important; + height: auto !important; + width: auto !important; } .is-mobile-preview .wp-block-columns { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .is-mobile-preview .wp-block-column { - box-sizing: border-box; - /* override flex-basis set in style attribute to fix the height of the column in mobile preview. Blocks overriding is as a part of style.css in blocks-library */ - flex-basis: auto !important; + box-sizing: border-box; + /* override flex-basis set in style attribute to fix the height of the column in mobile preview. Blocks overriding is as a part of style.css in blocks-library */ + flex-basis: auto !important; } diff --git a/packages/php/email-editor/src/Engine/content-shared.css b/packages/php/email-editor/src/Engine/content-shared.css index 9c36c90c59..e550eb1bfc 100644 --- a/packages/php/email-editor/src/Engine/content-shared.css +++ b/packages/php/email-editor/src/Engine/content-shared.css @@ -11,5 +11,5 @@ h3.has-background, h4.has-background, h5.has-background, h6.has-background { - padding: 16px 24px; + padding: 16px 24px; } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php index 2c4cd6ee97..f8ee5e8124 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php @@ -10,56 +10,59 @@ use WP_Style_Engine; * Shared functionality for block renderers. */ abstract class Abstract_Block_Renderer implements Block_Renderer { - /** - * Wrapper for wp_style_engine_get_styles which ensures all values are returned. - * - * @param array $block_styles Array of block styles. - * @param bool $skip_convert_vars If true, --wp_preset--spacing--x type values will be left in the original var:preset:spacing:x format. - * @return array - */ - protected function getStylesFromBlock(array $block_styles, $skip_convert_vars = false) { - $styles = wp_style_engine_get_styles($block_styles, ['convert_vars_to_classnames' => $skip_convert_vars]); - return wp_parse_args($styles, [ - 'css' => '', - 'declarations' => [], - 'classnames' => '', - ]); - } + /** + * Wrapper for wp_style_engine_get_styles which ensures all values are returned. + * + * @param array $block_styles Array of block styles. + * @param bool $skip_convert_vars If true, --wp_preset--spacing--x type values will be left in the original var:preset:spacing:x format. + * @return array + */ + protected function getStylesFromBlock( array $block_styles, $skip_convert_vars = false ) { + $styles = wp_style_engine_get_styles( $block_styles, array( 'convert_vars_to_classnames' => $skip_convert_vars ) ); + return wp_parse_args( + $styles, + array( + 'css' => '', + 'declarations' => array(), + 'classnames' => '', + ) + ); + } - /** - * Compile objects containing CSS properties to a string. - * - * @param array ...$styles Style arrays to compile. - * @return string - */ - protected function compileCss(...$styles): string { - return WP_Style_Engine::compile_css(array_merge(...$styles), ''); - } + /** + * Compile objects containing CSS properties to a string. + * + * @param array ...$styles Style arrays to compile. + * @return string + */ + protected function compileCss( ...$styles ): string { + return WP_Style_Engine::compile_css( array_merge( ...$styles ), '' ); + } - protected function addSpacer($content, $emailAttrs): string { - $gapStyle = WP_Style_Engine::compile_css(array_intersect_key($emailAttrs, array_flip(['margin-top'])), ''); - $paddingStyle = WP_Style_Engine::compile_css(array_intersect_key($emailAttrs, array_flip(['padding-left', 'padding-right'])), ''); + protected function addSpacer( $content, $emailAttrs ): string { + $gapStyle = WP_Style_Engine::compile_css( array_intersect_key( $emailAttrs, array_flip( array( 'margin-top' ) ) ), '' ); + $paddingStyle = WP_Style_Engine::compile_css( array_intersect_key( $emailAttrs, array_flip( array( 'padding-left', 'padding-right' ) ) ), '' ); - if (!$gapStyle && !$paddingStyle) { - return $content; - } + if ( ! $gapStyle && ! $paddingStyle ) { + return $content; + } - return sprintf( - ' + return sprintf( + ' ', - $content, - esc_attr($gapStyle), - esc_attr($paddingStyle) - ); - } + $content, + esc_attr( $gapStyle ), + esc_attr( $paddingStyle ) + ); + } - public function render(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - return $this->addSpacer( - $this->renderContent($blockContent, $parsedBlock, $settingsController), - $parsedBlock['email_attrs'] ?? [] - ); - } + public function render( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + return $this->addSpacer( + $this->renderContent( $blockContent, $parsedBlock, $settingsController ), + $parsedBlock['email_attrs'] ?? array() + ); + } - abstract protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string; + abstract protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string; } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php index ead1a149fd..ba8cd3001c 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-button.php @@ -7,93 +7,107 @@ use MailPoet\EmailEditor\Integrations\Utils\Dom_Document_Helper; /** * Renders a button block. + * * @see https://www.activecampaign.com/blog/email-buttons * @see https://documentation.mjml.io/#mj-button */ class Button extends Abstract_Block_Renderer { - private function getWrapperStyles(array $blockStyles) { - $properties = ['border', 'color', 'typography', 'spacing']; - $styles = $this->getStylesFromBlock(array_intersect_key($blockStyles, array_flip($properties))); - return (object)[ - 'css' => $this->compileCss($styles['declarations'], ['word-break' => 'break-word', 'display' => 'block']), - 'classname' => $styles['classnames'], - ]; - } + private function getWrapperStyles( array $blockStyles ) { + $properties = array( 'border', 'color', 'typography', 'spacing' ); + $styles = $this->getStylesFromBlock( array_intersect_key( $blockStyles, array_flip( $properties ) ) ); + return (object) array( + 'css' => $this->compileCss( + $styles['declarations'], + array( + 'word-break' => 'break-word', + 'display' => 'block', + ) + ), + 'classname' => $styles['classnames'], + ); + } - private function getLinkStyles(array $blockStyles) { - $styles = $this->getStylesFromBlock([ - 'color' => [ - 'text' => $blockStyles['color']['text'] ?? '', - ], - 'typography' => $blockStyles['typography'] ?? [], - ]); - return (object)[ - 'css' => $this->compileCss($styles['declarations'], ['display' => 'block']), - 'classname' => $styles['classnames'], - ]; - } + private function getLinkStyles( array $blockStyles ) { + $styles = $this->getStylesFromBlock( + array( + 'color' => array( + 'text' => $blockStyles['color']['text'] ?? '', + ), + 'typography' => $blockStyles['typography'] ?? array(), + ) + ); + return (object) array( + 'css' => $this->compileCss( $styles['declarations'], array( 'display' => 'block' ) ), + 'classname' => $styles['classnames'], + ); + } - public function render(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - return $this->renderContent($blockContent, $parsedBlock, $settingsController); - } + public function render( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + return $this->renderContent( $blockContent, $parsedBlock, $settingsController ); + } - protected function renderContent($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - if (empty($parsedBlock['innerHTML'])) { - return ''; - } + protected function renderContent( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + if ( empty( $parsedBlock['innerHTML'] ) ) { + return ''; + } - $domHelper = new Dom_Document_Helper($parsedBlock['innerHTML']); - $blockClassname = $domHelper->getAttributeValueByTagName('div', 'class') ?? ''; - $buttonLink = $domHelper->findElement('a'); + $domHelper = new Dom_Document_Helper( $parsedBlock['innerHTML'] ); + $blockClassname = $domHelper->getAttributeValueByTagName( 'div', 'class' ) ?? ''; + $buttonLink = $domHelper->findElement( 'a' ); - if (!$buttonLink) { - return ''; - } + if ( ! $buttonLink ) { + return ''; + } - $buttonText = $domHelper->getElementInnerHTML($buttonLink) ?: ''; - $buttonUrl = $buttonLink->getAttribute('href') ?: '#'; + $buttonText = $domHelper->getElementInnerHTML( $buttonLink ) ?: ''; + $buttonUrl = $buttonLink->getAttribute( 'href' ) ?: '#'; - $blockAttributes = wp_parse_args($parsedBlock['attrs'] ?? [], [ - 'width' => '', - 'style' => [], - 'textAlign' => 'center', - 'backgroundColor' => '', - 'textColor' => '', - ]); + $blockAttributes = wp_parse_args( + $parsedBlock['attrs'] ?? array(), + array( + 'width' => '', + 'style' => array(), + 'textAlign' => 'center', + 'backgroundColor' => '', + 'textColor' => '', + ) + ); - $blockStyles = array_replace_recursive( - [ - 'color' => array_filter([ - 'background' => $blockAttributes['backgroundColor'] ? $settingsController->translateSlugToColor($blockAttributes['backgroundColor']) : null, - 'text' => $blockAttributes['textColor'] ? $settingsController->translateSlugToColor($blockAttributes['textColor']) : null, - ]), - ], - $blockAttributes['style'] ?? [] - ); + $blockStyles = array_replace_recursive( + array( + 'color' => array_filter( + array( + 'background' => $blockAttributes['backgroundColor'] ? $settingsController->translateSlugToColor( $blockAttributes['backgroundColor'] ) : null, + 'text' => $blockAttributes['textColor'] ? $settingsController->translateSlugToColor( $blockAttributes['textColor'] ) : null, + ) + ), + ), + $blockAttributes['style'] ?? array() + ); - if (!empty($blockStyles['border']) && empty($blockStyles['border']['style'])) { - $blockStyles['border']['style'] = 'solid'; - } + if ( ! empty( $blockStyles['border'] ) && empty( $blockStyles['border']['style'] ) ) { + $blockStyles['border']['style'] = 'solid'; + } - $wrapperStyles = $this->getWrapperStyles($blockStyles); - $linkStyles = $this->getLinkStyles($blockStyles); + $wrapperStyles = $this->getWrapperStyles( $blockStyles ); + $linkStyles = $this->getLinkStyles( $blockStyles ); - return sprintf( - ' + return sprintf( + '
', - esc_attr($blockAttributes['width'] ? '100%' : 'auto'), - esc_attr($blockAttributes['textAlign']), - esc_attr($wrapperStyles->classname . ' ' . $blockClassname), - esc_attr($wrapperStyles->css), - esc_attr($linkStyles->classname), - esc_attr($linkStyles->css), - esc_url($buttonUrl), - $buttonText, - ); - } + esc_attr( $blockAttributes['width'] ? '100%' : 'auto' ), + esc_attr( $blockAttributes['textAlign'] ), + esc_attr( $wrapperStyles->classname . ' ' . $blockClassname ), + esc_attr( $wrapperStyles->css ), + esc_attr( $linkStyles->classname ), + esc_attr( $linkStyles->css ), + esc_url( $buttonUrl ), + $buttonText, + ); + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php index f37ab9efd0..d178d935fa 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php @@ -6,22 +6,22 @@ use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Layout\Flex_Layout_Rend use MailPoet\EmailEditor\Engine\Settings_Controller; class Buttons extends Abstract_Block_Renderer { - /** @var Flex_Layout_Renderer */ - private $flexLayoutRenderer; + /** @var Flex_Layout_Renderer */ + private $flexLayoutRenderer; - public function __construct( - Flex_Layout_Renderer $flexLayoutRenderer - ) { - $this->flexLayoutRenderer = $flexLayoutRenderer; - } + public function __construct( + Flex_Layout_Renderer $flexLayoutRenderer + ) { + $this->flexLayoutRenderer = $flexLayoutRenderer; + } - protected function renderContent($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - // Ignore font size set on the buttons block - // We rely on TypographyPreprocessor to set the font size on the buttons - // Rendering font size on the wrapper causes unwanted whitespace below the buttons - if (isset($parsedBlock['attrs']['style']['typography']['fontSize'])) { - unset($parsedBlock['attrs']['style']['typography']['fontSize']); - } - return $this->flexLayoutRenderer->renderInnerBlocksInLayout($parsedBlock, $settingsController); - } + protected function renderContent( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + // Ignore font size set on the buttons block + // We rely on TypographyPreprocessor to set the font size on the buttons + // Rendering font size on the wrapper causes unwanted whitespace below the buttons + if ( isset( $parsedBlock['attrs']['style']['typography']['fontSize'] ) ) { + unset( $parsedBlock['attrs']['style']['typography']['fontSize'] ); + } + return $this->flexLayoutRenderer->renderInnerBlocksInLayout( $parsedBlock, $settingsController ); + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php index a0f694417c..a2a7c60589 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php @@ -7,79 +7,87 @@ use MailPoet\EmailEditor\Integrations\Utils\Dom_Document_Helper; use WP_Style_Engine; class Column extends Abstract_Block_Renderer { - /** - * Override this method to disable spacing (block gap) for columns. - * Spacing is applied on wrapping columns block. Columns are rendered side by side so no spacer is needed. - */ - protected function addSpacer($content, $emailAttrs): string { - return $content; - } + /** + * Override this method to disable spacing (block gap) for columns. + * Spacing is applied on wrapping columns block. Columns are rendered side by side so no spacer is needed. + */ + protected function addSpacer( $content, $emailAttrs ): string { + return $content; + } - protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $content = ''; - foreach ($parsedBlock['innerBlocks'] ?? [] as $block) { - $content .= render_block($block); - } + protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $content = ''; + foreach ( $parsedBlock['innerBlocks'] ?? array() as $block ) { + $content .= render_block( $block ); + } - return str_replace( - '{column_content}', - $content, - $this->getBlockWrapper($blockContent, $parsedBlock, $settingsController) - ); - } + return str_replace( + '{column_content}', + $content, + $this->getBlockWrapper( $blockContent, $parsedBlock, $settingsController ) + ); + } - /** - * Based on MJML - */ - private function getBlockWrapper(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $originalWrapperClassname = (new Dom_Document_Helper($blockContent))->getAttributeValueByTagName('div', 'class') ?? ''; - $block_attributes = wp_parse_args($parsedBlock['attrs'] ?? [], [ - 'verticalAlignment' => 'stretch', - 'width' => $settingsController->getLayoutWidthWithoutPadding(), - 'style' => [], - ]); + /** + * Based on MJML + */ + private function getBlockWrapper( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $originalWrapperClassname = ( new Dom_Document_Helper( $blockContent ) )->getAttributeValueByTagName( 'div', 'class' ) ?? ''; + $block_attributes = wp_parse_args( + $parsedBlock['attrs'] ?? array(), + array( + 'verticalAlignment' => 'stretch', + 'width' => $settingsController->getLayoutWidthWithoutPadding(), + 'style' => array(), + ) + ); - // The default column alignment is `stretch to fill` which means that we need to set the background color to the main cell - // to create a feeling of a stretched column. This also needs to apply to CSS classnames which can also apply styles. - $isStretched = empty($block_attributes['verticalAlignment']) || $block_attributes['verticalAlignment'] === 'stretch'; + // The default column alignment is `stretch to fill` which means that we need to set the background color to the main cell + // to create a feeling of a stretched column. This also needs to apply to CSS classnames which can also apply styles. + $isStretched = empty( $block_attributes['verticalAlignment'] ) || $block_attributes['verticalAlignment'] === 'stretch'; - $paddingCSS = $this->getStylesFromBlock(['spacing' => ['padding' => $block_attributes['style']['spacing']['padding'] ?? []]])['css']; - $cellStyles = $this->getStylesFromBlock([ - 'color' => $block_attributes['style']['color'] ?? [], - 'background' => $block_attributes['style']['background'] ?? [], - ])['declarations']; + $paddingCSS = $this->getStylesFromBlock( array( 'spacing' => array( 'padding' => $block_attributes['style']['spacing']['padding'] ?? array() ) ) )['css']; + $cellStyles = $this->getStylesFromBlock( + array( + 'color' => $block_attributes['style']['color'] ?? array(), + 'background' => $block_attributes['style']['background'] ?? array(), + ) + )['declarations']; - $borderStyles = $this->getStylesFromBlock(['border' => $block_attributes['style']['border'] ?? []])['declarations']; + $borderStyles = $this->getStylesFromBlock( array( 'border' => $block_attributes['style']['border'] ?? array() ) )['declarations']; - if (!empty($borderStyles)) { - $cellStyles = array_merge($cellStyles, ['border-style' => 'solid'], $borderStyles); - } + if ( ! empty( $borderStyles ) ) { + $cellStyles = array_merge( $cellStyles, array( 'border-style' => 'solid' ), $borderStyles ); + } - if (!empty($cellStyles['background-image']) && empty($cellStyles['background-size'])) { - $cellStyles['background-size'] = 'cover'; - } + if ( ! empty( $cellStyles['background-image'] ) && empty( $cellStyles['background-size'] ) ) { + $cellStyles['background-size'] = 'cover'; + } - $wrapperClassname = 'block wp-block-column email-block-column'; - $contentClassname = 'email-block-column-content'; - $wrapperCSS = WP_Style_Engine::compile_css([ - 'vertical-align' => $isStretched ? 'top' : $block_attributes['verticalAlignment'], - ], ''); - $contentCSS = 'vertical-align: top;'; + $wrapperClassname = 'block wp-block-column email-block-column'; + $contentClassname = 'email-block-column-content'; + $wrapperCSS = WP_Style_Engine::compile_css( + array( + 'vertical-align' => $isStretched ? 'top' : $block_attributes['verticalAlignment'], + ), + '' + ); + $contentCSS = 'vertical-align: top;'; - if ($isStretched) { - $wrapperClassname .= ' ' . $originalWrapperClassname; - $wrapperCSS .= ' ' . WP_Style_Engine::compile_css($cellStyles, ''); - } else { - $contentClassname .= ' ' . $originalWrapperClassname; - $contentCSS .= ' ' . WP_Style_Engine::compile_css($cellStyles, ''); - } + if ( $isStretched ) { + $wrapperClassname .= ' ' . $originalWrapperClassname; + $wrapperCSS .= ' ' . WP_Style_Engine::compile_css( $cellStyles, '' ); + } else { + $contentClassname .= ' ' . $originalWrapperClassname; + $contentCSS .= ' ' . WP_Style_Engine::compile_css( $cellStyles, '' ); + } - return ' - - + return ' + '; - } + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php index d009194b5c..fd21160ea8 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php @@ -8,68 +8,75 @@ use MailPoet\EmailEditor\Integrations\Utils\Dom_Document_Helper; use WP_Style_Engine; class Columns extends Abstract_Block_Renderer { - protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $content = ''; - foreach ($parsedBlock['innerBlocks'] ?? [] as $block) { - $content .= render_block($block); - } + protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $content = ''; + foreach ( $parsedBlock['innerBlocks'] ?? array() as $block ) { + $content .= render_block( $block ); + } - return str_replace( - '{columns_content}', - $content, - $this->getBlockWrapper($blockContent, $parsedBlock, $settingsController) - ); - } + return str_replace( + '{columns_content}', + $content, + $this->getBlockWrapper( $blockContent, $parsedBlock, $settingsController ) + ); + } - /** - * Based on MJML - */ - private function getBlockWrapper(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $originalWrapperClassname = (new Dom_Document_Helper($blockContent))->getAttributeValueByTagName('div', 'class') ?? ''; - $block_attributes = wp_parse_args($parsedBlock['attrs'] ?? [], [ - 'align' => null, - 'width' => $settingsController->getLayoutWidthWithoutPadding(), - 'style' => [], - ]); + /** + * Based on MJML + */ + private function getBlockWrapper( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $originalWrapperClassname = ( new Dom_Document_Helper( $blockContent ) )->getAttributeValueByTagName( 'div', 'class' ) ?? ''; + $block_attributes = wp_parse_args( + $parsedBlock['attrs'] ?? array(), + array( + 'align' => null, + 'width' => $settingsController->getLayoutWidthWithoutPadding(), + 'style' => array(), + ) + ); - $columnsStyles = $this->getStylesFromBlock([ - 'spacing' => [ 'padding' => $block_attributes['style']['spacing']['padding'] ?? [] ], - 'color' => $block_attributes['style']['color'] ?? [], - 'background' => $block_attributes['style']['background'] ?? [], - ])['declarations']; + $columnsStyles = $this->getStylesFromBlock( + array( + 'spacing' => array( 'padding' => $block_attributes['style']['spacing']['padding'] ?? array() ), + 'color' => $block_attributes['style']['color'] ?? array(), + 'background' => $block_attributes['style']['background'] ?? array(), + ) + )['declarations']; - $borderStyles = $this->getStylesFromBlock(['border' => $block_attributes['style']['border'] ?? []])['declarations']; + $borderStyles = $this->getStylesFromBlock( array( 'border' => $block_attributes['style']['border'] ?? array() ) )['declarations']; - if (!empty($borderStyles)) { - $columnsStyles = array_merge($columnsStyles, ['border-style' => 'solid'], $borderStyles); - } + if ( ! empty( $borderStyles ) ) { + $columnsStyles = array_merge( $columnsStyles, array( 'border-style' => 'solid' ), $borderStyles ); + } - if (empty($columnsStyles['background-size'])) { - $columnsStyles['background-size'] = 'cover'; - } + if ( empty( $columnsStyles['background-size'] ) ) { + $columnsStyles['background-size'] = 'cover'; + } - $renderedColumns = ' + $renderedColumns = '{columns_content}'; - // Margins are not supported well in outlook for tables, so wrap in another table. - $margins = $block_attributes['style']['spacing']['margin'] ?? []; + // Margins are not supported well in outlook for tables, so wrap in another table. + $margins = $block_attributes['style']['spacing']['margin'] ?? array(); - if (!empty($margins)) { - $marginToPaddingStyles = $this->getStylesFromBlock([ - 'spacing' => [ 'margin' => $margins ], - ])['css']; - $renderedColumns = ' + if ( ! empty( $margins ) ) { + $marginToPaddingStyles = $this->getStylesFromBlock( + array( + 'spacing' => array( 'margin' => $margins ), + ) + )['css']; + $renderedColumns = ''; - } + } - return $renderedColumns; - } + return $renderedColumns; + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-fallback.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-fallback.php index ea8447dfeb..96dcb639f6 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-fallback.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-fallback.php @@ -14,7 +14,7 @@ use MailPoet\EmailEditor\Engine\Settings_Controller; * We need to find a better abstraction/architecture for this. */ class Fallback extends Abstract_Block_Renderer { - protected function renderContent($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - return $blockContent; - } + protected function renderContent( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + return $blockContent; + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-group.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-group.php index 7d33b2021a..499d64234e 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-group.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-group.php @@ -8,55 +8,64 @@ use MailPoet\EmailEditor\Integrations\Utils\Dom_Document_Helper; use WP_Style_Engine; class Group extends Abstract_Block_Renderer { - protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $content = ''; - $innerBlocks = $parsedBlock['innerBlocks'] ?? []; + protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $content = ''; + $innerBlocks = $parsedBlock['innerBlocks'] ?? array(); - foreach ($innerBlocks as $block) { - $content .= render_block($block); - } + foreach ( $innerBlocks as $block ) { + $content .= render_block( $block ); + } - return str_replace( - '{group_content}', - $content, - $this->getBlockWrapper($blockContent, $parsedBlock, $settingsController) - ); - } + return str_replace( + '{group_content}', + $content, + $this->getBlockWrapper( $blockContent, $parsedBlock, $settingsController ) + ); + } - private function getBlockWrapper(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $originalClassname = (new Dom_Document_Helper($blockContent))->getAttributeValueByTagName('div', 'class') ?? ''; - $blockAttributes = wp_parse_args($parsedBlock['attrs'] ?? [], [ - 'style' => [], - 'backgroundColor' => '', - 'textColor' => '', - 'borderColor' => '', - 'layout' => [], - ]); + private function getBlockWrapper( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $originalClassname = ( new Dom_Document_Helper( $blockContent ) )->getAttributeValueByTagName( 'div', 'class' ) ?? ''; + $blockAttributes = wp_parse_args( + $parsedBlock['attrs'] ?? array(), + array( + 'style' => array(), + 'backgroundColor' => '', + 'textColor' => '', + 'borderColor' => '', + 'layout' => array(), + ) + ); - // Layout, background, borders need to be on the outer table element. - $tableStyles = $this->getStylesFromBlock([ - 'color' => array_filter([ - 'background' => $blockAttributes['backgroundColor'] ? $settingsController->translateSlugToColor($blockAttributes['backgroundColor']) : null, - 'text' => $blockAttributes['textColor'] ? $settingsController->translateSlugToColor($blockAttributes['textColor']) : null, - 'border' => $blockAttributes['borderColor'] ? $settingsController->translateSlugToColor($blockAttributes['borderColor']) : null, - ]), - 'background' => $blockAttributes['style']['background'] ?? [], - 'border' => $blockAttributes['style']['border'] ?? [], - 'spacing' => [ 'padding' => $blockAttributes['style']['spacing']['margin'] ?? [] ], - ])['declarations']; - $tableStyles['border-collapse'] = 'separate'; // Needed for the border radius to work. + // Layout, background, borders need to be on the outer table element. + $tableStyles = $this->getStylesFromBlock( + array( + 'color' => array_filter( + array( + 'background' => $blockAttributes['backgroundColor'] ? $settingsController->translateSlugToColor( $blockAttributes['backgroundColor'] ) : null, + 'text' => $blockAttributes['textColor'] ? $settingsController->translateSlugToColor( $blockAttributes['textColor'] ) : null, + 'border' => $blockAttributes['borderColor'] ? $settingsController->translateSlugToColor( $blockAttributes['borderColor'] ) : null, + ) + ), + 'background' => $blockAttributes['style']['background'] ?? array(), + 'border' => $blockAttributes['style']['border'] ?? array(), + 'spacing' => array( 'padding' => $blockAttributes['style']['spacing']['margin'] ?? array() ), + ) + )['declarations']; + $tableStyles['border-collapse'] = 'separate'; // Needed for the border radius to work. - // Padding properties need to be added to the table cell. - $cellStyles = $this->getStylesFromBlock([ - 'spacing' => [ 'padding' => $blockAttributes['style']['spacing']['padding'] ?? [] ], - ])['declarations']; + // Padding properties need to be added to the table cell. + $cellStyles = $this->getStylesFromBlock( + array( + 'spacing' => array( 'padding' => $blockAttributes['style']['spacing']['padding'] ?? array() ), + ) + )['declarations']; - $tableStyles['background-size'] = empty($tableStyles['background-size']) ? 'cover' : $tableStyles['background-size']; - $justifyContent = $blockAttributes['layout']['justifyContent'] ?? 'center'; - $width = $parsedBlock['email_attrs']['width'] ?? '100%'; + $tableStyles['background-size'] = empty( $tableStyles['background-size'] ) ? 'cover' : $tableStyles['background-size']; + $justifyContent = $blockAttributes['layout']['justifyContent'] ?? 'center'; + $width = $parsedBlock['email_attrs']['width'] ?? '100%'; - return sprintf( - ' + return sprintf( + '', - esc_attr(WP_Style_Engine::compile_css($tableStyles, '')), - esc_attr(WP_Style_Engine::compile_css($cellStyles, '')), - esc_attr($originalClassname), - esc_attr($justifyContent), - esc_attr($width), - ); - } + esc_attr( WP_Style_Engine::compile_css( $tableStyles, '' ) ), + esc_attr( WP_Style_Engine::compile_css( $cellStyles, '' ) ), + esc_attr( $originalClassname ), + esc_attr( $justifyContent ), + esc_attr( $width ), + ); + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php index aed5b189a8..14096ae350 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php @@ -6,190 +6,209 @@ use MailPoet\EmailEditor\Engine\Settings_Controller; use MailPoet\EmailEditor\Integrations\Utils\Dom_Document_Helper; class Image extends Abstract_Block_Renderer { - protected function renderContent($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $parsedHtml = $this->parseBlockContent($blockContent); + protected function renderContent( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $parsedHtml = $this->parseBlockContent( $blockContent ); - if (!$parsedHtml) { - return ''; - } + if ( ! $parsedHtml ) { + return ''; + } - $imageUrl = $parsedHtml['imageUrl']; - $image = $parsedHtml['image']; - $caption = $parsedHtml['caption']; - $class = $parsedHtml['class']; - $parsedBlock = $this->addImageSizeWhenMissing($parsedBlock, $imageUrl, $settingsController); - $image = $this->addImageDimensions($image, $parsedBlock, $settingsController); + $imageUrl = $parsedHtml['imageUrl']; + $image = $parsedHtml['image']; + $caption = $parsedHtml['caption']; + $class = $parsedHtml['class']; + $parsedBlock = $this->addImageSizeWhenMissing( $parsedBlock, $imageUrl, $settingsController ); + $image = $this->addImageDimensions( $image, $parsedBlock, $settingsController ); - $imageWithWrapper = str_replace( - ['{image_content}', '{caption_content}'], - [$image, $caption], - $this->getBlockWrapper($parsedBlock, $settingsController, $caption) - ); + $imageWithWrapper = str_replace( + array( '{image_content}', '{caption_content}' ), + array( $image, $caption ), + $this->getBlockWrapper( $parsedBlock, $settingsController, $caption ) + ); - $imageWithWrapper = $this->applyRoundedStyle($imageWithWrapper, $parsedBlock); - $imageWithWrapper = $this->applyImageBorderStyle($imageWithWrapper, $parsedBlock, $class); - return $imageWithWrapper; - } + $imageWithWrapper = $this->applyRoundedStyle( $imageWithWrapper, $parsedBlock ); + $imageWithWrapper = $this->applyImageBorderStyle( $imageWithWrapper, $parsedBlock, $class ); + return $imageWithWrapper; + } - private function applyRoundedStyle(string $blockContent, array $parsedBlock): string { - // Because the isn't an attribute for definition of rounded style, we have to check the class name - if (isset($parsedBlock['attrs']['className']) && strpos($parsedBlock['attrs']['className'], 'is-style-rounded') !== false) { - // If the image should be in a circle, we need to set the border-radius to 9999px to make it the same as is in the editor - // This style is applied to both wrapper and the image - $blockContent = $this->removeStyleAttributeFromElement($blockContent, ['tag_name' => 'td', 'class_name' => 'email-image-cell'], 'border-radius'); - $blockContent = $this->addStyleToElement($blockContent, ['tag_name' => 'td', 'class_name' => 'email-image-cell'], 'border-radius: 9999px;'); - $blockContent = $this->removeStyleAttributeFromElement($blockContent, ['tag_name' => 'img'], 'border-radius'); - $blockContent = $this->addStyleToElement($blockContent, ['tag_name' => 'img'], 'border-radius: 9999px;'); - } - return $blockContent; - } + private function applyRoundedStyle( string $blockContent, array $parsedBlock ): string { + // Because the isn't an attribute for definition of rounded style, we have to check the class name + if ( isset( $parsedBlock['attrs']['className'] ) && strpos( $parsedBlock['attrs']['className'], 'is-style-rounded' ) !== false ) { + // If the image should be in a circle, we need to set the border-radius to 9999px to make it the same as is in the editor + // This style is applied to both wrapper and the image + $blockContent = $this->removeStyleAttributeFromElement( + $blockContent, + array( + 'tag_name' => 'td', + 'class_name' => 'email-image-cell', + ), + 'border-radius' + ); + $blockContent = $this->addStyleToElement( + $blockContent, + array( + 'tag_name' => 'td', + 'class_name' => 'email-image-cell', + ), + 'border-radius: 9999px;' + ); + $blockContent = $this->removeStyleAttributeFromElement( $blockContent, array( 'tag_name' => 'img' ), 'border-radius' ); + $blockContent = $this->addStyleToElement( $blockContent, array( 'tag_name' => 'img' ), 'border-radius: 9999px;' ); + } + return $blockContent; + } - /** - * When the width is not set, it's important to get it for the image to be displayed correctly - */ - private function addImageSizeWhenMissing(array $parsedBlock, string $imageUrl, Settings_Controller $settingsController): array { - if (isset($parsedBlock['attrs']['width'])) { - return $parsedBlock; - } - // Can't determine any width let's go with 100% - if (!isset($parsedBlock['email_attrs']['width'])) { - $parsedBlock['attrs']['width'] = '100%'; - } - $maxWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width']); - $imageSize = wp_getimagesize($imageUrl); - $imageSize = $imageSize ? $imageSize[0] : $maxWidth; - $width = min($imageSize, $maxWidth); - $parsedBlock['attrs']['width'] = "{$width}px"; - return $parsedBlock; + /** + * When the width is not set, it's important to get it for the image to be displayed correctly + */ + private function addImageSizeWhenMissing( array $parsedBlock, string $imageUrl, Settings_Controller $settingsController ): array { + if ( isset( $parsedBlock['attrs']['width'] ) ) { + return $parsedBlock; + } + // Can't determine any width let's go with 100% + if ( ! isset( $parsedBlock['email_attrs']['width'] ) ) { + $parsedBlock['attrs']['width'] = '100%'; + } + $maxWidth = $settingsController->parseNumberFromStringWithPixels( $parsedBlock['email_attrs']['width'] ); + $imageSize = wp_getimagesize( $imageUrl ); + $imageSize = $imageSize ? $imageSize[0] : $maxWidth; + $width = min( $imageSize, $maxWidth ); + $parsedBlock['attrs']['width'] = "{$width}px"; + return $parsedBlock; + } - } + private function applyImageBorderStyle( string $blockContent, array $parsedBlock, string $class ): string { + // Getting individual border properties + $borderStyles = wp_style_engine_get_styles( array( 'border' => $parsedBlock['attrs']['style']['border'] ?? array() ) ); + $borderStyles = $borderStyles['declarations'] ?? array(); + if ( ! empty( $borderStyles ) ) { + $borderStyles['border-style'] = 'solid'; + $borderStyles['box-sizing'] = 'border-box'; + } + $borderElementTag = array( + 'tag_name' => 'td', + 'class_name' => 'email-image-cell', + ); + $contentWithBorderStyles = $this->addStyleToElement( $blockContent, $borderElementTag, \WP_Style_Engine::compile_css( $borderStyles, '' ) ); + // Add Border related classes to proper element. This is required for inlined border-color styles when defined via class + $borderClasses = array_filter( + explode( ' ', $class ), + function ( $className ) { + return strpos( $className, 'border' ) !== false; + } + ); + $html = new \WP_HTML_Tag_Processor( $contentWithBorderStyles ); + if ( $html->next_tag( $borderElementTag ) ) { + $class = $html->get_attribute( 'class' ) ?? ''; + $borderClasses[] = $class; + $html->set_attribute( 'class', implode( ' ', $borderClasses ) ); + } + return $html->get_updated_html(); + } - private function applyImageBorderStyle(string $blockContent, array $parsedBlock, string $class): string { - // Getting individual border properties - $borderStyles = wp_style_engine_get_styles(['border' => $parsedBlock['attrs']['style']['border'] ?? []]); - $borderStyles = $borderStyles['declarations'] ?? []; - if (!empty($borderStyles)) { - $borderStyles['border-style'] = 'solid'; - $borderStyles['box-sizing'] = 'border-box'; - } - $borderElementTag = ['tag_name' => 'td', 'class_name' => 'email-image-cell']; - $contentWithBorderStyles = $this->addStyleToElement($blockContent, $borderElementTag, \WP_Style_Engine::compile_css($borderStyles, '')); - // Add Border related classes to proper element. This is required for inlined border-color styles when defined via class - $borderClasses = array_filter(explode(' ', $class), function($className) { - return strpos($className, 'border') !== false; - }); - $html = new \WP_HTML_Tag_Processor($contentWithBorderStyles); - if ($html->next_tag($borderElementTag)) { - $class = $html->get_attribute('class') ?? ''; - $borderClasses[] = $class; - $html->set_attribute('class', implode(' ', $borderClasses)); - } - return $html->get_updated_html(); - } + /** + * Settings width and height attributes for images is important for MS Outlook. + */ + private function addImageDimensions( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $html = new \WP_HTML_Tag_Processor( $blockContent ); + if ( $html->next_tag( array( 'tag_name' => 'img' ) ) ) { + // Getting height from styles and if it's set, we set the height attribute + $styles = $html->get_attribute( 'style' ) ?? ''; + $styles = $settingsController->parseStylesToArray( $styles ); + $height = $styles['height'] ?? null; + if ( $height && $height !== 'auto' && is_numeric( $settingsController->parseNumberFromStringWithPixels( $height ) ) ) { + $height = $settingsController->parseNumberFromStringWithPixels( $height ); + $html->set_attribute( 'height', esc_attr( $height ) ); + } - /** - * Settings width and height attributes for images is important for MS Outlook. - */ - private function addImageDimensions($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $html = new \WP_HTML_Tag_Processor($blockContent); - if ($html->next_tag(['tag_name' => 'img'])) { - // Getting height from styles and if it's set, we set the height attribute - $styles = $html->get_attribute('style') ?? ''; - $styles = $settingsController->parseStylesToArray($styles); - $height = $styles['height'] ?? null; - if ($height && $height !== 'auto' && is_numeric($settingsController->parseNumberFromStringWithPixels($height))) { - $height = $settingsController->parseNumberFromStringWithPixels($height); - $html->set_attribute('height', esc_attr($height)); - } + if ( isset( $parsedBlock['attrs']['width'] ) ) { + $width = $settingsController->parseNumberFromStringWithPixels( $parsedBlock['attrs']['width'] ); + $html->set_attribute( 'width', esc_attr( $width ) ); + } + $blockContent = $html->get_updated_html(); + } - if (isset($parsedBlock['attrs']['width'])) { - $width = $settingsController->parseNumberFromStringWithPixels($parsedBlock['attrs']['width']); - $html->set_attribute('width', esc_attr($width)); - } - $blockContent = $html->get_updated_html(); - } + return $blockContent; + } - return $blockContent; - } + /** + * This method configure the font size of the caption because it's set to 0 for the parent element to avoid unexpected white spaces + * We try to use font-size passed down from the parent element $parsedBlock['email_attrs']['font-size'], but if it's not set, we use the default font-size from the email theme. + */ + private function getCaptionStyles( Settings_Controller $settingsController, array $parsedBlock ): string { + $themeData = $settingsController->getTheme()->get_data(); - /** - * This method configure the font size of the caption because it's set to 0 for the parent element to avoid unexpected white spaces - * We try to use font-size passed down from the parent element $parsedBlock['email_attrs']['font-size'], but if it's not set, we use the default font-size from the email theme. - */ - private function getCaptionStyles(Settings_Controller $settingsController, array $parsedBlock): string { - $themeData = $settingsController->getTheme()->get_data(); + $styles = array( + 'text-align' => isset( $parsedBlock['attrs']['align'] ) ? 'center' : 'left', + ); - $styles = [ - 'text-align' => isset($parsedBlock['attrs']['align']) ? 'center' : 'left', - ]; + $styles['font-size'] = $parsedBlock['email_attrs']['font-size'] ?? $themeData['styles']['typography']['fontSize']; + return \WP_Style_Engine::compile_css( $styles, '' ); + } - $styles['font-size'] = $parsedBlock['email_attrs']['font-size'] ?? $themeData['styles']['typography']['fontSize']; - return \WP_Style_Engine::compile_css($styles, ''); - } + /** + * Based on MJML but because MJML doesn't support captions, our solution is a bit different + */ + private function getBlockWrapper( array $parsedBlock, Settings_Controller $settingsController, ?string $caption ): string { + $styles = array( + 'border-collapse' => 'collapse', + 'border-spacing' => '0px', + 'font-size' => '0px', + 'vertical-align' => 'top', + 'width' => '100%', + ); - /** - * Based on MJML but because MJML doesn't support captions, our solution is a bit different - */ - private function getBlockWrapper(array $parsedBlock, Settings_Controller $settingsController, ?string $caption): string { - $styles = [ - 'border-collapse' => 'collapse', - 'border-spacing' => '0px', - 'font-size' => '0px', - 'vertical-align' => 'top', - 'width' => '100%', - ]; + $width = $parsedBlock['attrs']['width'] ?? '100%'; + $wrapperWidth = ( $width && $width !== '100%' ) ? $width : 'auto'; + $wrapperStyles = $styles; + $wrapperStyles['width'] = $wrapperWidth; + $wrapperStyles['border-collapse'] = 'separate'; // Needed because of border radius - $width = $parsedBlock['attrs']['width'] ?? '100%'; - $wrapperWidth = ($width && $width !== '100%') ? $width : 'auto'; - $wrapperStyles = $styles; - $wrapperStyles['width'] = $wrapperWidth; - $wrapperStyles['border-collapse'] = 'separate'; // Needed because of border radius - - $captionHtml = ''; - if ($caption) { - // When the image is not aligned, the wrapper is set to 100% width due to caption that can be longer than the image - $captionWidth = isset($parsedBlock['attrs']['align']) ? ($parsedBlock['attrs']['width'] ?? '100%') : '100%'; - $captionWrapperStyles = $styles; - $captionWrapperStyles['width'] = $captionWidth; - $captionStyles = $this->getCaptionStyles($settingsController, $parsedBlock); - $captionHtml = ' + $captionHtml = ''; + if ( $caption ) { + // When the image is not aligned, the wrapper is set to 100% width due to caption that can be longer than the image + $captionWidth = isset( $parsedBlock['attrs']['align'] ) ? ( $parsedBlock['attrs']['width'] ?? '100%' ) : '100%'; + $captionWrapperStyles = $styles; + $captionWrapperStyles['width'] = $captionWidth; + $captionStyles = $this->getCaptionStyles( $settingsController, $parsedBlock ); + $captionHtml = ' - + '; - } + } - $styles['width'] = '100%'; - $align = $parsedBlock['attrs']['align'] ?? 'left'; + $styles['width'] = '100%'; + $align = $parsedBlock['attrs']['align'] ?? 'left'; - return ' + return ' -
+ @@ -199,83 +218,83 @@ class Image extends Abstract_Block_Renderer { '; - } + } - /** - * @param array{tag_name: string, class_name?: string} $tag - * @param string $style - */ - private function addStyleToElement($blockContent, array $tag, string $style): string { - $html = new \WP_HTML_Tag_Processor($blockContent); - if ($html->next_tag($tag)) { - $elementStyle = $html->get_attribute('style') ?? ''; - $elementStyle = !empty($elementStyle) ? (rtrim($elementStyle, ';') . ';') : ''; // Adding semicolon if it's missing - $elementStyle .= $style; - $html->set_attribute('style', esc_attr($elementStyle)); - $blockContent = $html->get_updated_html(); - } + /** + * @param array{tag_name: string, class_name?: string} $tag + * @param string $style + */ + private function addStyleToElement( $blockContent, array $tag, string $style ): string { + $html = new \WP_HTML_Tag_Processor( $blockContent ); + if ( $html->next_tag( $tag ) ) { + $elementStyle = $html->get_attribute( 'style' ) ?? ''; + $elementStyle = ! empty( $elementStyle ) ? ( rtrim( $elementStyle, ';' ) . ';' ) : ''; // Adding semicolon if it's missing + $elementStyle .= $style; + $html->set_attribute( 'style', esc_attr( $elementStyle ) ); + $blockContent = $html->get_updated_html(); + } - return $blockContent; - } + return $blockContent; + } - /** - * @param array{tag_name: string, class_name?: string} $tag - */ - private function removeStyleAttributeFromElement($blockContent, array $tag, string $styleName): string { - $html = new \WP_HTML_Tag_Processor($blockContent); - if ($html->next_tag($tag)) { - $elementStyle = $html->get_attribute('style') ?? ''; - $elementStyle = preg_replace('/' . $styleName . ':(.?[0-9]+px)+;?/', '', $elementStyle); - $html->set_attribute('style', esc_attr($elementStyle)); - $blockContent = $html->get_updated_html(); - } + /** + * @param array{tag_name: string, class_name?: string} $tag + */ + private function removeStyleAttributeFromElement( $blockContent, array $tag, string $styleName ): string { + $html = new \WP_HTML_Tag_Processor( $blockContent ); + if ( $html->next_tag( $tag ) ) { + $elementStyle = $html->get_attribute( 'style' ) ?? ''; + $elementStyle = preg_replace( '/' . $styleName . ':(.?[0-9]+px)+;?/', '', $elementStyle ); + $html->set_attribute( 'style', esc_attr( $elementStyle ) ); + $blockContent = $html->get_updated_html(); + } - return $blockContent; - } + return $blockContent; + } - /** - * @param string $blockContent - * @return array{imageUrl: string, image: string, caption: string}|null - */ - private function parseBlockContent(string $blockContent): ?array { - // If block's image is not set, we don't need to parse the content - if (empty($blockContent)) { - return null; - } + /** + * @param string $blockContent + * @return array{imageUrl: string, image: string, caption: string}|null + */ + private function parseBlockContent( string $blockContent ): ?array { + // If block's image is not set, we don't need to parse the content + if ( empty( $blockContent ) ) { + return null; + } - $domHelper = new Dom_Document_Helper($blockContent); + $domHelper = new Dom_Document_Helper( $blockContent ); - $figureTag = $domHelper->findElement('figure'); - if (!$figureTag) { - return null; - } + $figureTag = $domHelper->findElement( 'figure' ); + if ( ! $figureTag ) { + return null; + } - $imgTag = $domHelper->findElement('img'); - if (!$imgTag) { - return null; - } + $imgTag = $domHelper->findElement( 'img' ); + if ( ! $imgTag ) { + return null; + } - $imageSrc = $domHelper->getAttributeValue($imgTag, 'src'); - $imageClass = $domHelper->getAttributeValue($imgTag, 'class'); - $imageHtml = $domHelper->getOuterHtml($imgTag); - $figcaption = $domHelper->findElement('figcaption'); - $figcaptionHtml = $figcaption ? $domHelper->getOuterHtml($figcaption) : ''; - $figcaptionHtml = str_replace([''], [''], $figcaptionHtml); + $imageSrc = $domHelper->getAttributeValue( $imgTag, 'src' ); + $imageClass = $domHelper->getAttributeValue( $imgTag, 'class' ); + $imageHtml = $domHelper->getOuterHtml( $imgTag ); + $figcaption = $domHelper->findElement( 'figcaption' ); + $figcaptionHtml = $figcaption ? $domHelper->getOuterHtml( $figcaption ) : ''; + $figcaptionHtml = str_replace( array( '' ), array( '' ), $figcaptionHtml ); - return [ - 'imageUrl' => $imageSrc ?: '', - 'image' => $this->cleanupImageHtml($imageHtml), - 'caption' => $figcaptionHtml ?: '', - 'class' => $imageClass ?: '', - ]; - } + return array( + 'imageUrl' => $imageSrc ?: '', + 'image' => $this->cleanupImageHtml( $imageHtml ), + 'caption' => $figcaptionHtml ?: '', + 'class' => $imageClass ?: '', + ); + } - private function cleanupImageHtml(string $contentHtml): string { - $html = new \WP_HTML_Tag_Processor($contentHtml); - if ($html->next_tag(['tag_name' => 'img'])) { - $html->remove_attribute('srcset'); - $html->remove_attribute('class'); - } - return $html->get_updated_html(); - } + private function cleanupImageHtml( string $contentHtml ): string { + $html = new \WP_HTML_Tag_Processor( $contentHtml ); + if ( $html->next_tag( array( 'tag_name' => 'img' ) ) ) { + $html->remove_attribute( 'srcset' ); + $html->remove_attribute( 'class' ); + } + return $html->get_updated_html(); + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php index 6bb347585b..fd4c4287e8 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php @@ -6,37 +6,40 @@ use MailPoet\EmailEditor\Engine\Settings_Controller; // We have to avoid using keyword `List` class List_Block extends Abstract_Block_Renderer { - protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - $html = new \WP_HTML_Tag_Processor($blockContent); - $tagName = ($parsedBlock['attrs']['ordered'] ?? false) ? 'ol' : 'ul'; - if ($html->next_tag(['tag_name' => $tagName])) { - $styles = $html->get_attribute('style') ?? ''; - $styles = $settingsController->parseStylesToArray($styles); + protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + $html = new \WP_HTML_Tag_Processor( $blockContent ); + $tagName = ( $parsedBlock['attrs']['ordered'] ?? false ) ? 'ol' : 'ul'; + if ( $html->next_tag( array( 'tag_name' => $tagName ) ) ) { + $styles = $html->get_attribute( 'style' ) ?? ''; + $styles = $settingsController->parseStylesToArray( $styles ); - // Font size - if (isset($parsedBlock['email_attrs']['font-size'])) { - $styles['font-size'] = $parsedBlock['email_attrs']['font-size']; - } else { - // Use font-size from email theme when those properties are not set - $themeData = $settingsController->getTheme()->get_data(); - $styles['font-size'] = $themeData['styles']['typography']['fontSize']; - } + // Font size + if ( isset( $parsedBlock['email_attrs']['font-size'] ) ) { + $styles['font-size'] = $parsedBlock['email_attrs']['font-size']; + } else { + // Use font-size from email theme when those properties are not set + $themeData = $settingsController->getTheme()->get_data(); + $styles['font-size'] = $themeData['styles']['typography']['fontSize']; + } - $html->set_attribute('style', esc_attr(\WP_Style_Engine::compile_css($styles, ''))); - $blockContent = $html->get_updated_html(); - } + $html->set_attribute( 'style', esc_attr( \WP_Style_Engine::compile_css( $styles, '' ) ) ); + $blockContent = $html->get_updated_html(); + } - $wrapperStyle = \WP_Style_Engine::compile_css([ - 'margin-top' => $parsedBlock['email_attrs']['margin-top'] ?? '0px', - ], ''); + $wrapperStyle = \WP_Style_Engine::compile_css( + array( + 'margin-top' => $parsedBlock['email_attrs']['margin-top'] ?? '0px', + ), + '' + ); - // \WP_HTML_Tag_Processor escapes the content, so we have to replace it back - $blockContent = str_replace(''', "'", $blockContent); + // \WP_HTML_Tag_Processor escapes the content, so we have to replace it back + $blockContent = str_replace( ''', "'", $blockContent ); - return sprintf( - '
%2$s
', - esc_attr($wrapperStyle), - $blockContent - ); - } + return sprintf( + '
%2$s
', + esc_attr( $wrapperStyle ), + $blockContent + ); + } } diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-item.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-item.php index 4b420fa574..c8c508daae 100644 --- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-item.php +++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-item.php @@ -5,14 +5,14 @@ namespace MailPoet\EmailEditor\Integrations\Core\Renderer\Blocks; use MailPoet\EmailEditor\Engine\Settings_Controller; class List_Item extends Abstract_Block_Renderer { - /** - * Override this method to disable spacing (block gap) for list items. - */ - protected function addSpacer($content, $emailAttrs): string { - return $content; - } + /** + * Override this method to disable spacing (block gap) for list items. + */ + protected function addSpacer( $content, $emailAttrs ): string { + return $content; + } - protected function renderContent($blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - return $blockContent; - } + protected function renderContent( $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + return $blockContent; + } } 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 71729c728d..b474f1f1bd 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 @@ -8,53 +8,58 @@ use MailPoet\EmailEditor\Engine\Settings_Controller; * This renderer covers both core/paragraph and core/heading blocks */ class Text extends Abstract_Block_Renderer { - protected function renderContent(string $blockContent, array $parsedBlock, Settings_Controller $settingsController): string { - // Do not render empty blocks. - if (empty(trim(strip_tags($blockContent)))) { - return ''; - } + protected function renderContent( string $blockContent, array $parsedBlock, Settings_Controller $settingsController ): string { + // Do not render empty blocks. + if ( empty( trim( strip_tags( $blockContent ) ) ) ) { + return ''; + } - $blockContent = $this->adjustStyleAttribute($blockContent); - $blockAttributes = wp_parse_args($parsedBlock['attrs'] ?? [], [ - 'textAlign' => 'left', - 'style' => [], - ]); - $html = new \WP_HTML_Tag_Processor($blockContent); - $classes = 'email-text-block'; - if ($html->next_tag()) { - $blockClasses = $html->get_attribute('class') ?? ''; - $classes .= ' ' . $blockClasses; - // remove has-background to prevent double padding applied for wrapper and inner element - $blockClasses = str_replace('has-background', '', $blockClasses); - // remove border related classes because we handle border on wrapping table cell - $blockClasses = preg_replace('/[a-z-]+-border-[a-z-]+/', '', $blockClasses); - $html->set_attribute('class', trim($blockClasses)); - $blockContent = $html->get_updated_html(); - } + $blockContent = $this->adjustStyleAttribute( $blockContent ); + $blockAttributes = wp_parse_args( + $parsedBlock['attrs'] ?? array(), + array( + 'textAlign' => 'left', + 'style' => array(), + ) + ); + $html = new \WP_HTML_Tag_Processor( $blockContent ); + $classes = 'email-text-block'; + if ( $html->next_tag() ) { + $blockClasses = $html->get_attribute( 'class' ) ?? ''; + $classes .= ' ' . $blockClasses; + // remove has-background to prevent double padding applied for wrapper and inner element + $blockClasses = str_replace( 'has-background', '', $blockClasses ); + // remove border related classes because we handle border on wrapping table cell + $blockClasses = preg_replace( '/[a-z-]+-border-[a-z-]+/', '', $blockClasses ); + $html->set_attribute( 'class', trim( $blockClasses ) ); + $blockContent = $html->get_updated_html(); + } - $blockStyles = $this->getStylesFromBlock([ - 'color' => $blockAttributes['style']['color'] ?? [], - 'spacing' => $blockAttributes['style']['spacing'] ?? [], - 'typography' => $blockAttributes['style']['typography'] ?? [], - 'border' => $blockAttributes['style']['border'] ?? [], - ]); + $blockStyles = $this->getStylesFromBlock( + array( + 'color' => $blockAttributes['style']['color'] ?? array(), + 'spacing' => $blockAttributes['style']['spacing'] ?? array(), + 'typography' => $blockAttributes['style']['typography'] ?? array(), + 'border' => $blockAttributes['style']['border'] ?? array(), + ) + ); - $styles = [ - 'min-width' => '100%', // prevent Gmail App from shrinking the table on mobile devices - ]; + $styles = array( + 'min-width' => '100%', // prevent Gmail App from shrinking the table on mobile devices + ); - $styles['text-align'] = 'left'; - if (isset($parsedBlock['attrs']['textAlign'])) { - $styles['text-align'] = $parsedBlock['attrs']['textAlign']; - } elseif (in_array($parsedBlock['attrs']['align'] ?? null, ['left', 'center', 'right'])) { - $styles['text-align'] = $parsedBlock['attrs']['align']; - } + $styles['text-align'] = 'left'; + if ( isset( $parsedBlock['attrs']['textAlign'] ) ) { + $styles['text-align'] = $parsedBlock['attrs']['textAlign']; + } elseif ( in_array( $parsedBlock['attrs']['align'] ?? null, array( 'left', 'center', 'right' ) ) ) { + $styles['text-align'] = $parsedBlock['attrs']['align']; + } - $compiledStyles = $this->compileCss($blockStyles['declarations'], $styles); - $tableStyles = 'border-collapse: separate;'; // Needed because of border radius + $compiledStyles = $this->compileCss( $blockStyles['declarations'], $styles ); + $tableStyles = 'border-collapse: separate;'; // Needed because of border radius - return sprintf( - '%5$s
', - esc_attr($tableStyles), - esc_attr($classes), - esc_attr($compiledStyles), - esc_attr($styles['text-align'] ?? 'left'), - $blockContent - ); - } + esc_attr( $tableStyles ), + esc_attr( $classes ), + esc_attr( $compiledStyles ), + esc_attr( $styles['text-align'] ?? 'left' ), + $blockContent + ); + } - /** - * 1) We need to remove padding because we render padding on wrapping table cell - * 2) We also need to replace font-size to avoid clamp() because clamp() is not supported in many email clients. - * The font size values is automatically converted to clamp() when WP site theme is configured to use fluid layouts. - * Currently (WP 6.5), there is no way to disable this behavior. - */ - private function adjustStyleAttribute(string $blockContent): string { - $html = new \WP_HTML_Tag_Processor($blockContent); + /** + * 1) We need to remove padding because we render padding on wrapping table cell + * 2) We also need to replace font-size to avoid clamp() because clamp() is not supported in many email clients. + * The font size values is automatically converted to clamp() when WP site theme is configured to use fluid layouts. + * Currently (WP 6.5), there is no way to disable this behavior. + */ + private function adjustStyleAttribute( string $blockContent ): string { + $html = new \WP_HTML_Tag_Processor( $blockContent ); - if ($html->next_tag()) { - $elementStyle = $html->get_attribute('style') ?? ''; - // Padding may contain value like 10px or variable like var(--spacing-10) - $elementStyle = preg_replace('/padding[^:]*:.?[0-9a-z-()]+;?/', '', $elementStyle); + if ( $html->next_tag() ) { + $elementStyle = $html->get_attribute( 'style' ) ?? ''; + // Padding may contain value like 10px or variable like var(--spacing-10) + $elementStyle = preg_replace( '/padding[^:]*:.?[0-9a-z-()]+;?/', '', $elementStyle ); - // Remove border styles. We apply border styles on the wrapping table cell - $elementStyle = preg_replace('/border[^:]*:.?[0-9a-z-()#]+;?/', '', $elementStyle); + // Remove border styles. We apply border styles on the wrapping table cell + $elementStyle = preg_replace( '/border[^:]*:.?[0-9a-z-()#]+;?/', '', $elementStyle ); - // 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 - $elementStyle = preg_replace('/font-size:[^;]+;?/', 'font-size: inherit;', $elementStyle); - $html->set_attribute('style', esc_attr($elementStyle)); - $blockContent = $html->get_updated_html(); - } + // 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 + $elementStyle = preg_replace( '/font-size:[^;]+;?/', 'font-size: inherit;', $elementStyle ); + $html->set_attribute( 'style', esc_attr( $elementStyle ) ); + $blockContent = $html->get_updated_html(); + } - return $blockContent; - } + return $blockContent; + } } diff --git a/packages/php/email-editor/src/Integrations/Core/class-initializer.php b/packages/php/email-editor/src/Integrations/Core/class-initializer.php index 56edc741c1..878ecf8a2a 100644 --- a/packages/php/email-editor/src/Integrations/Core/class-initializer.php +++ b/packages/php/email-editor/src/Integrations/Core/class-initializer.php @@ -6,49 +6,49 @@ use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Blocks_Registry; use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Layout\Flex_Layout_Renderer; class Initializer { - public function initialize(): void { - add_action('mailpoet_blocks_renderer_initialized', [$this, 'registerCoreBlocksRenderers'], 10, 1); - add_filter('mailpoet_email_editor_theme_json', [$this, 'adjustThemeJson'], 10, 1); - add_filter('safe_style_css', [$this, 'allowStyles']); - } + public function initialize(): void { + add_action( 'mailpoet_blocks_renderer_initialized', array( $this, 'registerCoreBlocksRenderers' ), 10, 1 ); + add_filter( 'mailpoet_email_editor_theme_json', array( $this, 'adjustThemeJson' ), 10, 1 ); + add_filter( 'safe_style_css', array( $this, 'allowStyles' ) ); + } - /** - * Register core blocks email renderers when the blocks renderer is initialized. - */ - public function registerCoreBlocksRenderers(Blocks_Registry $blocksRegistry): void { - $blocksRegistry->addBlockRenderer('core/paragraph', new Renderer\Blocks\Text()); - $blocksRegistry->addBlockRenderer('core/heading', new Renderer\Blocks\Text()); - $blocksRegistry->addBlockRenderer('core/column', new Renderer\Blocks\Column()); - $blocksRegistry->addBlockRenderer('core/columns', new Renderer\Blocks\Columns()); - $blocksRegistry->addBlockRenderer('core/list', new Renderer\Blocks\List_Block()); - $blocksRegistry->addBlockRenderer('core/list-item', new Renderer\Blocks\List_Item()); - $blocksRegistry->addBlockRenderer('core/image', new Renderer\Blocks\Image()); - $blocksRegistry->addBlockRenderer('core/buttons', new Renderer\Blocks\Buttons(new Flex_Layout_Renderer())); - $blocksRegistry->addBlockRenderer('core/button', new Renderer\Blocks\Button()); - $blocksRegistry->addBlockRenderer('core/group', new Renderer\Blocks\Group()); - // Render used for all other blocks - $blocksRegistry->addFallbackRenderer(new Renderer\Blocks\Fallback()); - } + /** + * Register core blocks email renderers when the blocks renderer is initialized. + */ + public function registerCoreBlocksRenderers( Blocks_Registry $blocksRegistry ): void { + $blocksRegistry->addBlockRenderer( 'core/paragraph', new Renderer\Blocks\Text() ); + $blocksRegistry->addBlockRenderer( 'core/heading', new Renderer\Blocks\Text() ); + $blocksRegistry->addBlockRenderer( 'core/column', new Renderer\Blocks\Column() ); + $blocksRegistry->addBlockRenderer( 'core/columns', new Renderer\Blocks\Columns() ); + $blocksRegistry->addBlockRenderer( 'core/list', new Renderer\Blocks\List_Block() ); + $blocksRegistry->addBlockRenderer( 'core/list-item', new Renderer\Blocks\List_Item() ); + $blocksRegistry->addBlockRenderer( 'core/image', new Renderer\Blocks\Image() ); + $blocksRegistry->addBlockRenderer( 'core/buttons', new Renderer\Blocks\Buttons( new Flex_Layout_Renderer() ) ); + $blocksRegistry->addBlockRenderer( 'core/button', new Renderer\Blocks\Button() ); + $blocksRegistry->addBlockRenderer( 'core/group', new Renderer\Blocks\Group() ); + // Render used for all other blocks + $blocksRegistry->addFallbackRenderer( new Renderer\Blocks\Fallback() ); + } - /** - * Adjusts the editor's theme to add blocks specific settings for core blocks. - */ - public function adjustThemeJson(\WP_Theme_JSON $editorThemeJson): \WP_Theme_JSON { - $themeJson = (string)file_get_contents(dirname(__FILE__) . '/theme.json'); - $themeJson = json_decode($themeJson, true); - /** @var array $themeJson */ - $editorThemeJson->merge(new \WP_Theme_JSON($themeJson, 'default')); - return $editorThemeJson; - } + /** + * Adjusts the editor's theme to add blocks specific settings for core blocks. + */ + public function adjustThemeJson( \WP_Theme_JSON $editorThemeJson ): \WP_Theme_JSON { + $themeJson = (string) file_get_contents( __DIR__ . '/theme.json' ); + $themeJson = json_decode( $themeJson, true ); + /** @var array $themeJson */ + $editorThemeJson->merge( new \WP_Theme_JSON( $themeJson, 'default' ) ); + return $editorThemeJson; + } - /** - * Allow styles for the email editor. - */ - public function allowStyles(array $allowedStyles): array { - $allowedStyles[] = 'display'; - $allowedStyles[] = 'mso-padding-alt'; - $allowedStyles[] = 'mso-font-width'; - $allowedStyles[] = 'mso-text-raise'; - return $allowedStyles; - } + /** + * Allow styles for the email editor. + */ + public function allowStyles( array $allowedStyles ): array { + $allowedStyles[] = 'display'; + $allowedStyles[] = 'mso-padding-alt'; + $allowedStyles[] = 'mso-font-width'; + $allowedStyles[] = 'mso-text-raise'; + return $allowedStyles; + } } diff --git a/packages/php/email-editor/src/Integrations/Utils/class-dom-document-helper.php b/packages/php/email-editor/src/Integrations/Utils/class-dom-document-helper.php index 9e864d6f62..5fd8aa9802 100644 --- a/packages/php/email-editor/src/Integrations/Utils/class-dom-document-helper.php +++ b/packages/php/email-editor/src/Integrations/Utils/class-dom-document-helper.php @@ -6,55 +6,57 @@ namespace MailPoet\EmailEditor\Integrations\Utils; * This class should guarantee that our work with the DOMDocument is unified and safe. */ class Dom_Document_Helper { - private \DOMDocument $dom; + private \DOMDocument $dom; - public function __construct( - string $htmlContent - ) { - $this->loadHtml($htmlContent); - } + public function __construct( + string $htmlContent + ) { + $this->loadHtml( $htmlContent ); + } - private function loadHtml(string $htmlContent): void { - libxml_use_internal_errors(true); - $this->dom = new \DOMDocument(); - if (!empty($htmlContent)) { - // prefixing the content with the XML declaration to force the input encoding to UTF-8 - $this->dom->loadHTML('' . $htmlContent, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); - } - libxml_clear_errors(); - } + private function loadHtml( string $htmlContent ): void { + libxml_use_internal_errors( true ); + $this->dom = new \DOMDocument(); + if ( ! empty( $htmlContent ) ) { + // prefixing the content with the XML declaration to force the input encoding to UTF-8 + $this->dom->loadHTML( '' . $htmlContent, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); + } + libxml_clear_errors(); + } - public function findElement(string $tagName): ?\DOMElement { - $elements = $this->dom->getElementsByTagName($tagName); - return $elements->item(0) ?: null; - } + public function findElement( string $tagName ): ?\DOMElement { + $elements = $this->dom->getElementsByTagName( $tagName ); + return $elements->item( 0 ) ?: null; + } - public function getAttributeValue(\DOMElement $element, string $attribute): string { - return $element->hasAttribute($attribute) ? $element->getAttribute($attribute) : ''; - } + public function getAttributeValue( \DOMElement $element, string $attribute ): string { + return $element->hasAttribute( $attribute ) ? $element->getAttribute( $attribute ) : ''; + } - /** - * Searches for the first appearance of the given tag name and returns the value of specified attribute. - */ - public function getAttributeValueByTagName(string $tagName, string $attribute): ?string { - $element = $this->findElement($tagName); - if (!$element) { - return null; - } - return $this->getAttributeValue($element, $attribute); - } + /** + * Searches for the first appearance of the given tag name and returns the value of specified attribute. + */ + public function getAttributeValueByTagName( string $tagName, string $attribute ): ?string { + $element = $this->findElement( $tagName ); + if ( ! $element ) { + return null; + } + return $this->getAttributeValue( $element, $attribute ); + } - public function getOuterHtml(\DOMElement $element): string { - return (string)$this->dom->saveHTML($element); - } + public function getOuterHtml( \DOMElement $element ): string { + return (string) $this->dom->saveHTML( $element ); + } - public function getElementInnerHTML(\DOMElement $element): string { - $innerHTML = ''; - $children = $element->childNodes; - foreach ($children as $child) { - if (!$child instanceof \DOMNode) continue; - $innerHTML .= $this->dom->saveHTML($child); - } - return $innerHTML; - } + public function getElementInnerHTML( \DOMElement $element ): string { + $innerHTML = ''; + $children = $element->childNodes; + foreach ( $children as $child ) { + if ( ! $child instanceof \DOMNode ) { + continue; + } + $innerHTML .= $this->dom->saveHTML( $child ); + } + return $innerHTML; + } } diff --git a/packages/php/email-editor/src/Utils/class-cdn-asset-url.php b/packages/php/email-editor/src/Utils/class-cdn-asset-url.php index b2900bec67..03c934ae19 100644 --- a/packages/php/email-editor/src/Utils/class-cdn-asset-url.php +++ b/packages/php/email-editor/src/Utils/class-cdn-asset-url.php @@ -3,18 +3,18 @@ namespace MailPoet\EmailEditor\Utils; class Cdn_Asset_Url { - const CDN_URL = 'https://ps.w.org/mailpoet/'; - /** @var string */ - private $baseUrl; + const CDN_URL = 'https://ps.w.org/mailpoet/'; + /** @var string */ + private $baseUrl; - public function __construct( - string $baseUrl - ) { - $this->baseUrl = $baseUrl; - } + public function __construct( + string $baseUrl + ) { + $this->baseUrl = $baseUrl; + } - public function generateCdnUrl($path) { - $useCdn = defined('MAILPOET_USE_CDN') ? MAILPOET_USE_CDN : true; - return ($useCdn ? self::CDN_URL : $this->baseUrl . '/plugin_repository/') . "assets/$path"; - } + public function generateCdnUrl( $path ) { + $useCdn = defined( 'MAILPOET_USE_CDN' ) ? MAILPOET_USE_CDN : true; + return ( $useCdn ? self::CDN_URL : $this->baseUrl . '/plugin_repository/' ) . "assets/$path"; + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php b/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php index 823e99d937..fca9e7ea06 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php @@ -6,32 +6,35 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#oneof-and-anyof class Any_Of_Schema extends Schema { - protected $schema = [ - 'anyOf' => [], - ]; + protected $schema = array( + 'anyOf' => array(), + ); - /** @param Schema[] $schemas */ - public function __construct( - array $schemas - ) { - foreach ($schemas as $schema) { - $this->schema['anyOf'][] = $schema->toArray(); - } - } + /** @param Schema[] $schemas */ + public function __construct( + array $schemas + ) { + foreach ( $schemas as $schema ) { + $this->schema['anyOf'][] = $schema->toArray(); + } + } - public function nullable(): self { - $null = ['type' => 'null']; - $anyOf = $this->schema['anyOf']; - $value = in_array($null, $anyOf, true) ? $anyOf : array_merge($anyOf, [$null]); - return $this->updateSchemaProperty('anyOf', $value); - } + public function nullable(): self { + $null = array( 'type' => 'null' ); + $anyOf = $this->schema['anyOf']; + $value = in_array( $null, $anyOf, true ) ? $anyOf : array_merge( $anyOf, array( $null ) ); + return $this->updateSchemaProperty( 'anyOf', $value ); + } - public function nonNullable(): self { - $null = ['type' => 'null']; - $anyOf = $this->schema['anyOf']; - $value = array_filter($anyOf, function ($item) use ($null) { - return $item !== $null; - }); - return $this->updateSchemaProperty('anyOf', $value); - } + public function nonNullable(): self { + $null = array( 'type' => 'null' ); + $anyOf = $this->schema['anyOf']; + $value = array_filter( + $anyOf, + function ( $item ) use ( $null ) { + return $item !== $null; + } + ); + return $this->updateSchemaProperty( 'anyOf', $value ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-array-schema.php b/packages/php/email-editor/src/Validator/Schema/class-array-schema.php index 9860d01fb3..7d3500950a 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-array-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-array-schema.php @@ -6,23 +6,23 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#arrays class Array_Schema extends Schema { - protected $schema = [ - 'type' => 'array', - ]; + protected $schema = array( + 'type' => 'array', + ); - public function items(Schema $schema): self { - return $this->updateSchemaProperty('items', $schema->toArray()); - } + public function items( Schema $schema ): self { + return $this->updateSchemaProperty( 'items', $schema->toArray() ); + } - public function minItems(int $value): self { - return $this->updateSchemaProperty('minItems', $value); - } + public function minItems( int $value ): self { + return $this->updateSchemaProperty( 'minItems', $value ); + } - public function maxItems(int $value): self { - return $this->updateSchemaProperty('maxItems', $value); - } + public function maxItems( int $value ): self { + return $this->updateSchemaProperty( 'maxItems', $value ); + } - public function uniqueItems(): self { - return $this->updateSchemaProperty('uniqueItems', true); - } + public function uniqueItems(): self { + return $this->updateSchemaProperty( 'uniqueItems', true ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php b/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php index 023ea2fcd2..e314e1567d 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php @@ -6,7 +6,7 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types class Boolean_Schema extends Schema { - protected $schema = [ - 'type' => 'boolean', - ]; + protected $schema = array( + 'type' => 'boolean', + ); } diff --git a/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php b/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php index 753bd6213d..14bcf32359 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php @@ -6,31 +6,31 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#numbers class Integer_Schema extends Schema { - protected $schema = [ - 'type' => 'integer', - ]; + protected $schema = array( + 'type' => 'integer', + ); - public function minimum(int $value): self { - return $this->updateSchemaProperty('minimum', $value) - ->unsetSchemaProperty('exclusiveMinimum'); - } + public function minimum( int $value ): self { + return $this->updateSchemaProperty( 'minimum', $value ) + ->unsetSchemaProperty( 'exclusiveMinimum' ); + } - public function exclusiveMinimum(int $value): self { - return $this->updateSchemaProperty('minimum', $value) - ->updateSchemaProperty('exclusiveMinimum', true); - } + public function exclusiveMinimum( int $value ): self { + return $this->updateSchemaProperty( 'minimum', $value ) + ->updateSchemaProperty( 'exclusiveMinimum', true ); + } - public function maximum(int $value): self { - return $this->updateSchemaProperty('maximum', $value) - ->unsetSchemaProperty('exclusiveMaximum'); - } + public function maximum( int $value ): self { + return $this->updateSchemaProperty( 'maximum', $value ) + ->unsetSchemaProperty( 'exclusiveMaximum' ); + } - public function exclusiveMaximum(int $value): self { - return $this->updateSchemaProperty('maximum', $value) - ->updateSchemaProperty('exclusiveMaximum', true); - } + public function exclusiveMaximum( int $value ): self { + return $this->updateSchemaProperty( 'maximum', $value ) + ->updateSchemaProperty( 'exclusiveMaximum', true ); + } - public function multipleOf(int $value): self { - return $this->updateSchemaProperty('multipleOf', $value); - } + public function multipleOf( int $value ): self { + return $this->updateSchemaProperty( 'multipleOf', $value ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-null-schema.php b/packages/php/email-editor/src/Validator/Schema/class-null-schema.php index 329c4d7ff6..4d5bf7e84e 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-null-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-null-schema.php @@ -6,7 +6,7 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types class Null_Schema extends Schema { - protected $schema = [ - 'type' => 'null', - ]; + protected $schema = array( + 'type' => 'null', + ); } diff --git a/packages/php/email-editor/src/Validator/Schema/class-number-schema.php b/packages/php/email-editor/src/Validator/Schema/class-number-schema.php index 11dac4c519..69423a2bd8 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-number-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-number-schema.php @@ -6,31 +6,31 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#numbers class Number_Schema extends Schema { - protected $schema = [ - 'type' => 'number', - ]; + protected $schema = array( + 'type' => 'number', + ); - public function minimum(float $value): self { - return $this->updateSchemaProperty('minimum', $value) - ->unsetSchemaProperty('exclusiveMinimum'); - } + public function minimum( float $value ): self { + return $this->updateSchemaProperty( 'minimum', $value ) + ->unsetSchemaProperty( 'exclusiveMinimum' ); + } - public function exclusiveMinimum(float $value): self { - return $this->updateSchemaProperty('minimum', $value) - ->updateSchemaProperty('exclusiveMinimum', true); - } + public function exclusiveMinimum( float $value ): self { + return $this->updateSchemaProperty( 'minimum', $value ) + ->updateSchemaProperty( 'exclusiveMinimum', true ); + } - public function maximum(float $value): self { - return $this->updateSchemaProperty('maximum', $value) - ->unsetSchemaProperty('exclusiveMaximum'); - } + public function maximum( float $value ): self { + return $this->updateSchemaProperty( 'maximum', $value ) + ->unsetSchemaProperty( 'exclusiveMaximum' ); + } - public function exclusiveMaximum(float $value): self { - return $this->updateSchemaProperty('maximum', $value) - ->updateSchemaProperty('exclusiveMaximum', true); - } + public function exclusiveMaximum( float $value ): self { + return $this->updateSchemaProperty( 'maximum', $value ) + ->updateSchemaProperty( 'exclusiveMaximum', true ); + } - public function multipleOf(float $value): self { - return $this->updateSchemaProperty('multipleOf', $value); - } + public function multipleOf( float $value ): self { + return $this->updateSchemaProperty( 'multipleOf', $value ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-object-schema.php b/packages/php/email-editor/src/Validator/Schema/class-object-schema.php index c8e666f850..6e47da9f69 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-object-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-object-schema.php @@ -6,48 +6,51 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#objects class Object_Schema extends Schema { - protected $schema = [ - 'type' => 'object', - ]; + protected $schema = array( + 'type' => 'object', + ); - /** @param array $properties */ - public function properties(array $properties): self { - return $this->updateSchemaProperty('properties', array_map( - function (Schema $property) { - return $property->toArray(); - }, - $properties - )); - } + /** @param array $properties */ + public function properties( array $properties ): self { + return $this->updateSchemaProperty( + 'properties', + array_map( + function ( Schema $property ) { + return $property->toArray(); + }, + $properties + ) + ); + } - public function additionalProperties(Schema $schema): self { - return $this->updateSchemaProperty('additionalProperties', $schema->toArray()); - } + public function additionalProperties( Schema $schema ): self { + return $this->updateSchemaProperty( 'additionalProperties', $schema->toArray() ); + } - public function disableAdditionalProperties(): self { - return $this->updateSchemaProperty('additionalProperties', false); - } + public function disableAdditionalProperties(): self { + return $this->updateSchemaProperty( 'additionalProperties', false ); + } - /** - * Keys of $properties are regular expressions without leading/trailing delimiters. - * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#patternproperties - * - * @param array $properties - */ - public function patternProperties(array $properties): self { - $patternProperties = []; - foreach ($properties as $key => $value) { - $this->validatePattern($key); - $patternProperties[$key] = $value->toArray(); - } - return $this->updateSchemaProperty('patternProperties', $patternProperties); - } + /** + * Keys of $properties are regular expressions without leading/trailing delimiters. + * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#patternproperties + * + * @param array $properties + */ + public function patternProperties( array $properties ): self { + $patternProperties = array(); + foreach ( $properties as $key => $value ) { + $this->validatePattern( $key ); + $patternProperties[ $key ] = $value->toArray(); + } + return $this->updateSchemaProperty( 'patternProperties', $patternProperties ); + } - public function minProperties(int $value): self { - return $this->updateSchemaProperty('minProperties', $value); - } + public function minProperties( int $value ): self { + return $this->updateSchemaProperty( 'minProperties', $value ); + } - public function maxProperties(int $value): self { - return $this->updateSchemaProperty('maxProperties', $value); - } + public function maxProperties( int $value ): self { + return $this->updateSchemaProperty( 'maxProperties', $value ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php b/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php index 5d1e540e2e..4162375394 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php @@ -6,32 +6,35 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#oneof-and-anyof class One_Of_Schema extends Schema { - protected $schema = [ - 'oneOf' => [], - ]; + protected $schema = array( + 'oneOf' => array(), + ); - /** @param Schema[] $schemas */ - public function __construct( - array $schemas - ) { - foreach ($schemas as $schema) { - $this->schema['oneOf'][] = $schema->toArray(); - } - } + /** @param Schema[] $schemas */ + public function __construct( + array $schemas + ) { + foreach ( $schemas as $schema ) { + $this->schema['oneOf'][] = $schema->toArray(); + } + } - public function nullable(): self { - $null = ['type' => 'null']; - $oneOf = $this->schema['oneOf']; - $value = in_array($null, $oneOf, true) ? $oneOf : array_merge($oneOf, [$null]); - return $this->updateSchemaProperty('oneOf', $value); - } + public function nullable(): self { + $null = array( 'type' => 'null' ); + $oneOf = $this->schema['oneOf']; + $value = in_array( $null, $oneOf, true ) ? $oneOf : array_merge( $oneOf, array( $null ) ); + return $this->updateSchemaProperty( 'oneOf', $value ); + } - public function nonNullable(): self { - $null = ['type' => 'null']; - $oneOf = $this->schema['oneOf']; - $value = array_filter($oneOf, function ($item) use ($null) { - return $item !== $null; - }); - return $this->updateSchemaProperty('oneOf', $value); - } + public function nonNullable(): self { + $null = array( 'type' => 'null' ); + $oneOf = $this->schema['oneOf']; + $value = array_filter( + $oneOf, + function ( $item ) use ( $null ) { + return $item !== $null; + } + ); + return $this->updateSchemaProperty( 'oneOf', $value ); + } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-string-schema.php b/packages/php/email-editor/src/Validator/Schema/class-string-schema.php index b253f770dc..dd444bd4d0 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-string-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-string-schema.php @@ -6,48 +6,48 @@ use MailPoet\EmailEditor\Validator\Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#strings class String_Schema extends Schema { - protected $schema = [ - 'type' => 'string', - ]; + protected $schema = array( + 'type' => 'string', + ); - public function minLength(int $value): self { - return $this->updateSchemaProperty('minLength', $value); - } + public function minLength( int $value ): self { + return $this->updateSchemaProperty( 'minLength', $value ); + } - public function maxLength(int $value): self { - return $this->updateSchemaProperty('maxLength', $value); - } + public function maxLength( int $value ): self { + return $this->updateSchemaProperty( 'maxLength', $value ); + } - /** - * Parameter $pattern is a regular expression without leading/trailing delimiters. - * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#pattern - */ - public function pattern(string $pattern): self { - $this->validatePattern($pattern); - return $this->updateSchemaProperty('pattern', $pattern); - } + /** + * Parameter $pattern is a regular expression without leading/trailing delimiters. + * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#pattern + */ + public function pattern( string $pattern ): self { + $this->validatePattern( $pattern ); + return $this->updateSchemaProperty( 'pattern', $pattern ); + } - public function formatDateTime(): self { - return $this->updateSchemaProperty('format', 'date-time'); - } + public function formatDateTime(): self { + return $this->updateSchemaProperty( 'format', 'date-time' ); + } - public function formatEmail(): self { - return $this->updateSchemaProperty('format', 'email'); - } + public function formatEmail(): self { + return $this->updateSchemaProperty( 'format', 'email' ); + } - public function formatHexColor(): self { - return $this->updateSchemaProperty('format', 'hex-color'); - } + public function formatHexColor(): self { + return $this->updateSchemaProperty( 'format', 'hex-color' ); + } - public function formatIp(): self { - return $this->updateSchemaProperty('format', 'ip'); - } + public function formatIp(): self { + return $this->updateSchemaProperty( 'format', 'ip' ); + } - public function formatUri(): self { - return $this->updateSchemaProperty('format', 'uri'); - } + public function formatUri(): self { + return $this->updateSchemaProperty( 'format', 'uri' ); + } - public function formatUuid(): self { - return $this->updateSchemaProperty('format', 'uuid'); - } + public function formatUuid(): self { + return $this->updateSchemaProperty( 'format', 'uuid' ); + } } diff --git a/packages/php/email-editor/src/Validator/class-builder.php b/packages/php/email-editor/src/Validator/class-builder.php index 0a8ce356ba..cbfd47f923 100644 --- a/packages/php/email-editor/src/Validator/class-builder.php +++ b/packages/php/email-editor/src/Validator/class-builder.php @@ -14,44 +14,44 @@ use MailPoet\EmailEditor\Validator\Schema\String_Schema; // See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/ class Builder { - public static function string(): String_Schema { - return new String_Schema(); - } + public static function string(): String_Schema { + return new String_Schema(); + } - public static function number(): Number_Schema { - return new Number_Schema(); - } + public static function number(): Number_Schema { + return new Number_Schema(); + } - public static function integer(): Integer_Schema { - return new Integer_Schema(); - } + public static function integer(): Integer_Schema { + return new Integer_Schema(); + } - public static function boolean(): Boolean_Schema { - return new Boolean_Schema(); - } + public static function boolean(): Boolean_Schema { + return new Boolean_Schema(); + } - public static function null(): Null_Schema { - return new Null_Schema(); - } + public static function null(): Null_Schema { + return new Null_Schema(); + } - public static function array(Schema $items = null): Array_Schema { - $array = new Array_Schema(); - return $items ? $array->items($items) : $array; - } + public static function array( Schema $items = null ): Array_Schema { + $array = new Array_Schema(); + return $items ? $array->items( $items ) : $array; + } - /** @param array|null $properties */ - public static function object(array $properties = null): Object_Schema { - $object = new Object_Schema(); - return $properties === null ? $object : $object->properties($properties); - } + /** @param array|null $properties */ + public static function object( array $properties = null ): Object_Schema { + $object = new Object_Schema(); + return $properties === null ? $object : $object->properties( $properties ); + } - /** @param Schema[] $schemas */ - public static function oneOf(array $schemas): One_Of_Schema { - return new One_Of_Schema($schemas); - } + /** @param Schema[] $schemas */ + public static function oneOf( array $schemas ): One_Of_Schema { + return new One_Of_Schema( $schemas ); + } - /** @param Schema[] $schemas */ - public static function anyOf(array $schemas): Any_Of_Schema { - return new Any_Of_Schema($schemas); - } + /** @param Schema[] $schemas */ + public static function anyOf( array $schemas ): Any_Of_Schema { + return new Any_Of_Schema( $schemas ); + } } diff --git a/packages/php/email-editor/src/Validator/class-schema.php b/packages/php/email-editor/src/Validator/class-schema.php index 6890c21d30..a87e948059 100644 --- a/packages/php/email-editor/src/Validator/class-schema.php +++ b/packages/php/email-editor/src/Validator/class-schema.php @@ -2,96 +2,95 @@ namespace MailPoet\EmailEditor\Validator; - use function json_encode; use function rest_get_allowed_schema_keywords; abstract class Schema { - protected $schema = []; + protected $schema = array(); - /** @return static */ - public function nullable() { - $type = $this->schema['type'] ?? ['null']; - return $this->updateSchemaProperty('type', is_array($type) ? $type : [$type, 'null']); - } + /** @return static */ + public function nullable() { + $type = $this->schema['type'] ?? array( 'null' ); + return $this->updateSchemaProperty( 'type', is_array( $type ) ? $type : array( $type, 'null' ) ); + } - /** @return static */ - public function nonNullable() { - $type = $this->schema['type'] ?? null; - return $type === null - ? $this->unsetSchemaProperty('type') - : $this->updateSchemaProperty('type', is_array($type) ? $type[0] : $type); - } + /** @return static */ + public function nonNullable() { + $type = $this->schema['type'] ?? null; + return $type === null + ? $this->unsetSchemaProperty( 'type' ) + : $this->updateSchemaProperty( 'type', is_array( $type ) ? $type[0] : $type ); + } - /** @return static */ - public function required() { - return $this->updateSchemaProperty('required', true); - } + /** @return static */ + public function required() { + return $this->updateSchemaProperty( 'required', true ); + } - /** @return static */ - public function optional() { - return $this->unsetSchemaProperty('required'); - } + /** @return static */ + public function optional() { + return $this->unsetSchemaProperty( 'required' ); + } - /** @return static */ - public function title(string $title) { - return $this->updateSchemaProperty('title', $title); - } + /** @return static */ + public function title( string $title ) { + return $this->updateSchemaProperty( 'title', $title ); + } - /** @return static */ - public function description(string $description) { - return $this->updateSchemaProperty('description', $description); - } + /** @return static */ + public function description( string $description ) { + return $this->updateSchemaProperty( 'description', $description ); + } - /** @return static */ - public function default($default) { - return $this->updateSchemaProperty('default', $default); - } + /** @return static */ + public function default( $default ) { + return $this->updateSchemaProperty( 'default', $default ); + } - /** @return static */ - public function field(string $name, $value) { - if (in_array($name, $this->getReservedKeywords(), true)) { - throw new \Exception("Field name '$name' is reserved"); - } - return $this->updateSchemaProperty($name, $value); - } + /** @return static */ + public function field( string $name, $value ) { + if ( in_array( $name, $this->getReservedKeywords(), true ) ) { + throw new \Exception( "Field name '$name' is reserved" ); + } + return $this->updateSchemaProperty( $name, $value ); + } - public function toArray(): array { - return $this->schema; - } + public function toArray(): array { + return $this->schema; + } - public function toString(): string { - $json = json_encode($this->schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION); - $error = json_last_error(); - if ($error || $json === false) { - throw new \Exception(json_last_error_msg(), (string)$error); - } - return $json; - } + public function toString(): string { + $json = json_encode( $this->schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION ); + $error = json_last_error(); + if ( $error || $json === false ) { + throw new \Exception( json_last_error_msg(), (string) $error ); + } + return $json; + } - /** @return static */ - protected function updateSchemaProperty(string $name, $value) { - $clone = clone $this; - $clone->schema[$name] = $value; - return $clone; - } + /** @return static */ + protected function updateSchemaProperty( string $name, $value ) { + $clone = clone $this; + $clone->schema[ $name ] = $value; + return $clone; + } - /** @return static */ - protected function unsetSchemaProperty(string $name) { - $clone = clone $this; - unset($clone->schema[$name]); - return $clone; - } + /** @return static */ + protected function unsetSchemaProperty( string $name ) { + $clone = clone $this; + unset( $clone->schema[ $name ] ); + return $clone; + } - protected function getReservedKeywords(): array { - return rest_get_allowed_schema_keywords(); - } + protected function getReservedKeywords(): array { + return rest_get_allowed_schema_keywords(); + } - protected function validatePattern(string $pattern): void { - $escaped = str_replace('#', '\\#', $pattern); - $regex = "#$escaped#u"; - if (@preg_match($regex, '') === false) { - throw new \Exception("Invalid regular expression '$regex'"); - } - } + protected function validatePattern( string $pattern ): void { + $escaped = str_replace( '#', '\\#', $pattern ); + $regex = "#$escaped#u"; + if ( @preg_match( $regex, '' ) === false ) { + throw new \Exception( "Invalid regular expression '$regex'" ); + } + } } diff --git a/packages/php/email-editor/src/Validator/class-validation-exception.php b/packages/php/email-editor/src/Validator/class-validation-exception.php index 72d27df852..ccb69633c6 100644 --- a/packages/php/email-editor/src/Validator/class-validation-exception.php +++ b/packages/php/email-editor/src/Validator/class-validation-exception.php @@ -6,17 +6,17 @@ use MailPoet\UnexpectedValueException; use WP_Error; class Validation_Exception extends UnexpectedValueException { - /** @var WP_Error */ - protected $wpError; + /** @var WP_Error */ + protected $wpError; - public static function createFromWpError(WP_Error $wpError): self { - $exception = self::create() - ->withMessage($wpError->get_error_message()); - $exception->wpError = $wpError; - return $exception; - } + public static function createFromWpError( WP_Error $wpError ): self { + $exception = self::create() + ->withMessage( $wpError->get_error_message() ); + $exception->wpError = $wpError; + return $exception; + } - public function getWpError(): WP_Error { - return $this->wpError; - } + public function getWpError(): WP_Error { + return $this->wpError; + } } diff --git a/packages/php/email-editor/src/Validator/class-validator.php b/packages/php/email-editor/src/Validator/class-validator.php index 2c0d601ce6..617486312a 100644 --- a/packages/php/email-editor/src/Validator/class-validator.php +++ b/packages/php/email-editor/src/Validator/class-validator.php @@ -8,202 +8,210 @@ use stdClass; use WP_Error; class Validator { - /** @var WPFunctions */ - private $wp; + /** @var WPFunctions */ + private $wp; - public function __construct( - WPFunctions $wp - ) { - $this->wp = $wp; - } + public function __construct( + WPFunctions $wp + ) { + $this->wp = $wp; + } - /** - * Strict validation & sanitization implementation. - * It only coerces int to float (e.g. 5 to 5.0). - * - * @param mixed $value - * @return mixed - */ - public function validate(Schema $schema, $value, string $paramName = 'value') { - return $this->validateSchemaArray($schema->toArray(), $value, $paramName); - } + /** + * Strict validation & sanitization implementation. + * It only coerces int to float (e.g. 5 to 5.0). + * + * @param mixed $value + * @return mixed + */ + public function validate( Schema $schema, $value, string $paramName = 'value' ) { + return $this->validateSchemaArray( $schema->toArray(), $value, $paramName ); + } - /** - * Strict validation & sanitization implementation. - * It only coerces int to float (e.g. 5 to 5.0). - * - * @param array $schema. The array must follow the format, which is returned from Schema::toArray(). - * @param mixed $value - * @return mixed - */ - public function validateSchemaArray(array $schema, $value, string $paramName = 'value') { - $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName); - if ($result instanceof WP_Error) { - throw Validation_Exception::createFromWpError($result); - } - return $result; - } + /** + * Strict validation & sanitization implementation. + * It only coerces int to float (e.g. 5 to 5.0). + * + * @param array $schema. The array must follow the format, which is returned from Schema::toArray(). + * @param mixed $value + * @return mixed + */ + public function validateSchemaArray( array $schema, $value, string $paramName = 'value' ) { + $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + if ( $result instanceof WP_Error ) { + throw Validation_Exception::createFromWpError( $result ); + } + return $result; + } - /** - * Mirrors rest_validate_value_from_schema() and rest_sanitize_value_from_schema(). - * - * @param mixed $value - * @param array $schema - * @param string $paramName - * @return mixed|WP_Error - */ - private function validateAndSanitizeValueFromSchema($value, array $schema, string $paramName) { - // nullable - $fullType = $schema['type'] ?? null; - if (is_array($fullType) && in_array('null', $fullType, true) && $value === null) { - return null; - } + /** + * Mirrors rest_validate_value_from_schema() and rest_sanitize_value_from_schema(). + * + * @param mixed $value + * @param array $schema + * @param string $paramName + * @return mixed|WP_Error + */ + private function validateAndSanitizeValueFromSchema( $value, array $schema, string $paramName ) { + // nullable + $fullType = $schema['type'] ?? null; + if ( is_array( $fullType ) && in_array( 'null', $fullType, true ) && $value === null ) { + return null; + } - // anyOf, oneOf - if (isset($schema['anyOf'])) { - return $this->validateAndSanitizeAnyOf($value, $schema, $paramName); - } elseif (isset($schema['oneOf'])) { - return $this->validateAndSanitizeOneOf($value, $schema, $paramName); - } + // anyOf, oneOf + if ( isset( $schema['anyOf'] ) ) { + return $this->validateAndSanitizeAnyOf( $value, $schema, $paramName ); + } elseif ( isset( $schema['oneOf'] ) ) { + return $this->validateAndSanitizeOneOf( $value, $schema, $paramName ); + } - // make types strict - $type = is_array($fullType) ? $fullType[0] : $fullType; - switch ($type) { - case 'number': - if (!is_float($value) && !is_int($value)) { - return $this->getTypeError($paramName, $fullType); - } - break; - case 'integer': - if (!is_int($value)) { - return $this->getTypeError($paramName, $fullType); - } - break; - case 'boolean': - if (!is_bool($value)) { - return $this->getTypeError($paramName, $fullType); - } - break; - case 'array': - if (!is_array($value)) { - return $this->getTypeError($paramName, $fullType); - } + // make types strict + $type = is_array( $fullType ) ? $fullType[0] : $fullType; + switch ( $type ) { + case 'number': + if ( ! is_float( $value ) && ! is_int( $value ) ) { + return $this->getTypeError( $paramName, $fullType ); + } + break; + case 'integer': + if ( ! is_int( $value ) ) { + return $this->getTypeError( $paramName, $fullType ); + } + break; + case 'boolean': + if ( ! is_bool( $value ) ) { + return $this->getTypeError( $paramName, $fullType ); + } + break; + case 'array': + if ( ! is_array( $value ) ) { + return $this->getTypeError( $paramName, $fullType ); + } - if (isset($schema['items'])) { - foreach ($value as $i => $v) { - $result = $this->validateAndSanitizeValueFromSchema($v, $schema['items'], $paramName . '[' . $i . ']'); - if ($this->wp->isWpError($result)) { - return $result; - } - } - } - break; - case 'object': - if (!is_array($value) && !$value instanceof stdClass && !$value instanceof JsonSerializable) { - return $this->getTypeError($paramName, $fullType); - } + if ( isset( $schema['items'] ) ) { + foreach ( $value as $i => $v ) { + $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['items'], $paramName . '[' . $i . ']' ); + if ( $this->wp->isWpError( $result ) ) { + return $result; + } + } + } + break; + case 'object': + if ( ! is_array( $value ) && ! $value instanceof stdClass && ! $value instanceof JsonSerializable ) { + return $this->getTypeError( $paramName, $fullType ); + } - // ensure string keys - $value = (array)($value instanceof JsonSerializable ? $value->jsonSerialize() : $value); - if (count(array_filter(array_keys($value), 'is_string')) !== count($value)) { - return $this->getTypeError($paramName, $fullType); - } + // ensure string keys + $value = (array) ( $value instanceof JsonSerializable ? $value->jsonSerialize() : $value ); + if ( count( array_filter( array_keys( $value ), 'is_string' ) ) !== count( $value ) ) { + return $this->getTypeError( $paramName, $fullType ); + } - // validate object properties - foreach ($value as $k => $v) { - if (isset($schema['properties'][$k])) { - $result = $this->validateAndSanitizeValueFromSchema($v, $schema['properties'][$k], $paramName . '[' . $k . ']'); - if ($this->wp->isWpError($result)) { - return $result; - } - continue; - } + // validate object properties + foreach ( $value as $k => $v ) { + if ( isset( $schema['properties'][ $k ] ) ) { + $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['properties'][ $k ], $paramName . '[' . $k . ']' ); + if ( $this->wp->isWpError( $result ) ) { + return $result; + } + continue; + } - $patternPropertySchema = $this->wp->restFindMatchingPatternPropertySchema($k, $schema); - if ($patternPropertySchema) { - $result = $this->validateAndSanitizeValueFromSchema($v, $patternPropertySchema, $paramName . '[' . $k . ']'); - if ($this->wp->isWpError($result)) { - return $result; - } - continue; - } + $patternPropertySchema = $this->wp->restFindMatchingPatternPropertySchema( $k, $schema ); + if ( $patternPropertySchema ) { + $result = $this->validateAndSanitizeValueFromSchema( $v, $patternPropertySchema, $paramName . '[' . $k . ']' ); + if ( $this->wp->isWpError( $result ) ) { + return $result; + } + continue; + } - if (isset($schema['additionalProperties']) && is_array($schema['additionalProperties'])) { - $result = $this->validateAndSanitizeValueFromSchema($v, $schema['additionalProperties'], $paramName . '[' . $k . ']'); - if ($this->wp->isWpError($result)) { - return $result; - } - } - } - break; - } + if ( isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ) ) { + $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['additionalProperties'], $paramName . '[' . $k . ']' ); + if ( $this->wp->isWpError( $result ) ) { + return $result; + } + } + } + break; + } - $result = $this->wp->restValidateValueFromSchema($value, $schema, $paramName); - if ($this->wp->isWpError($result)) { - return $result; - } - return $this->wp->restSanitizeValueFromSchema($value, $schema, $paramName); - } + $result = $this->wp->restValidateValueFromSchema( $value, $schema, $paramName ); + if ( $this->wp->isWpError( $result ) ) { + return $result; + } + return $this->wp->restSanitizeValueFromSchema( $value, $schema, $paramName ); + } - /** - * Mirrors rest_find_any_matching_schema(). - * - * @param mixed $value - * @return mixed|WP_Error - */ - private function validateAndSanitizeAnyOf($value, array $anyOfSchema, string $paramName) { - $errors = []; - foreach ($anyOfSchema['anyOf'] as $index => $schema) { - $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName); - if (!$this->wp->isWpError($result)) { - return $result; - } - $errors[] = ['error_object' => $result, 'schema' => $schema, 'index' => $index]; - } - return $this->wp->restGetCombiningOperationError($value, $paramName, $errors); - } + /** + * Mirrors rest_find_any_matching_schema(). + * + * @param mixed $value + * @return mixed|WP_Error + */ + private function validateAndSanitizeAnyOf( $value, array $anyOfSchema, string $paramName ) { + $errors = array(); + foreach ( $anyOfSchema['anyOf'] as $index => $schema ) { + $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + if ( ! $this->wp->isWpError( $result ) ) { + return $result; + } + $errors[] = array( + 'error_object' => $result, + 'schema' => $schema, + 'index' => $index, + ); + } + return $this->wp->restGetCombiningOperationError( $value, $paramName, $errors ); + } - /** - * Mirrors rest_find_one_matching_schema(). - * - * @param mixed $value - * @return mixed|WP_Error - */ - private function validateAndSanitizeOneOf($value, array $oneOfSchema, string $paramName) { - $matchingSchemas = []; - $errors = []; - $data = null; - foreach ($oneOfSchema['oneOf'] as $index => $schema) { - $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName); - if ($this->wp->isWpError($result)) { - $errors[] = ['error_object' => $result, 'schema' => $schema, 'index' => $index]; - } else { - $data = $result; - $matchingSchemas[$index] = $schema; - } - } + /** + * Mirrors rest_find_one_matching_schema(). + * + * @param mixed $value + * @return mixed|WP_Error + */ + private function validateAndSanitizeOneOf( $value, array $oneOfSchema, string $paramName ) { + $matchingSchemas = array(); + $errors = array(); + $data = null; + foreach ( $oneOfSchema['oneOf'] as $index => $schema ) { + $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + if ( $this->wp->isWpError( $result ) ) { + $errors[] = array( + 'error_object' => $result, + 'schema' => $schema, + 'index' => $index, + ); + } else { + $data = $result; + $matchingSchemas[ $index ] = $schema; + } + } - if (!$matchingSchemas) { - return $this->wp->restGetCombiningOperationError($value, $paramName, $errors); - } + if ( ! $matchingSchemas ) { + return $this->wp->restGetCombiningOperationError( $value, $paramName, $errors ); + } - if (count($matchingSchemas) > 1) { - // reuse WP method to generate detailed error - $invalidSchema = ['type' => []]; - $oneOf = array_replace(array_fill(0, count($oneOfSchema['oneOf']), $invalidSchema), $matchingSchemas); - return $this->wp->restFindOneMatchingSchema($value, ['oneOf' => $oneOf], $paramName); - } - return $data; - } + if ( count( $matchingSchemas ) > 1 ) { + // reuse WP method to generate detailed error + $invalidSchema = array( 'type' => array() ); + $oneOf = array_replace( array_fill( 0, count( $oneOfSchema['oneOf'] ), $invalidSchema ), $matchingSchemas ); + return $this->wp->restFindOneMatchingSchema( $value, array( 'oneOf' => $oneOf ), $paramName ); + } + return $data; + } - /** @param string|string[] $type */ - private function getTypeError(string $param, $type): WP_Error { - $type = is_array($type) ? $type : [$type]; - return new WP_Error( - 'rest_invalid_type', - // translators: %1$s is the current parameter and %2$s a comma-separated list of the allowed types. - sprintf(__('%1$s is not of type %2$s.', 'mailpoet'), $param, implode(',', $type)), - ['param' => $param] - ); - } + /** @param string|string[] $type */ + private function getTypeError( string $param, $type ): WP_Error { + $type = is_array( $type ) ? $type : array( $type ); + return new WP_Error( + 'rest_invalid_type', + // translators: %1$s is the current parameter and %2$s a comma-separated list of the allowed types. + sprintf( __( '%1$s is not of type %2$s.', 'mailpoet' ), $param, implode( ',', $type ) ), + array( 'param' => $param ) + ); + } } diff --git a/packages/php/email-editor/src/class-container.php b/packages/php/email-editor/src/class-container.php index a25c162de2..9cbc833398 100644 --- a/packages/php/email-editor/src/class-container.php +++ b/packages/php/email-editor/src/class-container.php @@ -3,31 +3,31 @@ namespace MailPoet\EmailEditor; class Container { - protected array $services = []; - protected array $instances = []; + protected array $services = array(); + protected array $instances = array(); - public function set(string $name, callable $callable): void { - $this->services[$name] = $callable; - } + public function set( string $name, callable $callable ): void { + $this->services[ $name ] = $callable; + } - /** - * @template T - * @param class-string $name - * @return T - */ - public function get(string $name) { - // Check if the service is already instantiated - if (isset($this->instances[$name])) { - return $this->instances[$name]; - } + /** + * @template T + * @param class-string $name + * @return T + */ + public function get( string $name ) { + // Check if the service is already instantiated + if ( isset( $this->instances[ $name ] ) ) { + return $this->instances[ $name ]; + } - // Check if the service is registered - if (!isset($this->services[$name])) { - throw new \Exception("Service not found: $name"); - } + // Check if the service is registered + if ( ! isset( $this->services[ $name ] ) ) { + throw new \Exception( "Service not found: $name" ); + } - $this->instances[$name] = $this->services[$name]($this); + $this->instances[ $name ] = $this->services[ $name ]( $this ); - return $this->instances[$name]; - } + return $this->instances[ $name ]; + } }