diff --git a/mailpoet/lib/DI/ContainerConfigurator.php b/mailpoet/lib/DI/ContainerConfigurator.php
index d00678ad29..29cd1d3e4c 100644
--- a/mailpoet/lib/DI/ContainerConfigurator.php
+++ b/mailpoet/lib/DI/ContainerConfigurator.php
@@ -315,6 +315,7 @@ class ContainerConfigurator implements IContainerConfigurator {
// Email Editor
$container->autowire(\MailPoet\EmailEditor\Engine\EmailEditor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\AssetsCleaner::class)->setPublic(true);
+ $container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Renderer::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\BodyRenderer::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Integrations\MailPoet\EmailEditor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Integrations\MailPoet\EmailApiController::class)->setPublic(true);
diff --git a/mailpoet/lib/EmailEditor/Engine/Renderer/BodyRenderer.php b/mailpoet/lib/EmailEditor/Engine/Renderer/BodyRenderer.php
index 95543a960e..9885fe6b84 100644
--- a/mailpoet/lib/EmailEditor/Engine/Renderer/BodyRenderer.php
+++ b/mailpoet/lib/EmailEditor/Engine/Renderer/BodyRenderer.php
@@ -3,11 +3,11 @@
namespace MailPoet\EmailEditor\Engine\Renderer;
class BodyRenderer {
- public function renderBody(\WP_Post $emailPost): string {
+ public function renderBody(string $postContent): string {
// @todo Parse blocks \WP_Block_Parser
// @todo We need to wrap top level blocks which are not in columns into a column
// @todo Add rendering of columns (inspire by/reuse code from mailpoet/lib/Newsletter/Renderer/Columns/Renderer)
// @todo Add rendering of blocks
- return $emailPost->post_content ?: ''; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
+ return $postContent ?: '';
}
}
diff --git a/mailpoet/lib/EmailEditor/Engine/Renderer/Renderer.php b/mailpoet/lib/EmailEditor/Engine/Renderer/Renderer.php
new file mode 100644
index 0000000000..2e3cccf788
--- /dev/null
+++ b/mailpoet/lib/EmailEditor/Engine/Renderer/Renderer.php
@@ -0,0 +1,99 @@
+cssInliner = $cssInliner;
+ $this->bodyRenderer = $bodyRenderer;
+ }
+
+ public function render(string $postContent, string $subject, string $preHeader, string $language, $metaRobots = ''): array {
+ $renderedBody = $this->bodyRenderer->renderBody($postContent);
+ $styles = (string)file_get_contents(dirname(__FILE__) . '/' . self::STYLES_FILE);
+
+ /**
+ * {{email_language}}
+ * {{email_subject}}
+ * {{email_meta_robots}}
+ * {{email_styles}}
+ * {{email_preheader}}
+ * {{email_body}}
+ */
+ $templateWithContents = $this->injectContentIntoTemplate(
+ (string)file_get_contents(dirname(__FILE__) . '/' . self::TEMPLATE_FILE),
+ [
+ $language,
+ esc_html($subject),
+ $metaRobots,
+ $styles,
+ esc_html($preHeader),
+ $renderedBody,
+ ]
+ );
+
+ $templateWithContentsDom = $this->inlineCSSStyles($templateWithContents);
+ $templateWithContents = $this->postProcessTemplate($templateWithContentsDom);
+ return [
+ 'html' => $templateWithContents,
+ 'text' => $this->renderTextVersion($templateWithContents),
+ ];
+ }
+
+ private function injectContentIntoTemplate($template, array $content) {
+ return preg_replace_callback('/{{\w+}}/', function($matches) use (&$content) {
+ return array_shift($content);
+ }, $template);
+ }
+
+ /**
+ * @param string $template
+ * @return DomNode
+ */
+ private function inlineCSSStyles($template) {
+ return $this->cssInliner->inlineCSS($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);
+ }
+
+ /**
+ * @param DomNode $templateDom
+ * @return string
+ */
+ private function postProcessTemplate(DomNode $templateDom) {
+ // replace spaces in image tag URLs
+ foreach ($templateDom->query('img') as $image) {
+ $image->src = str_replace(' ', '%20', $image->src);
+ }
+ // because tburry/pquery contains a bug and replaces the opening non mso condition incorrectly we have to replace the opening tag with correct value
+ $template = $templateDom->__toString();
+ $template = str_replace('', '', $template);
+ return $template;
+ }
+}
diff --git a/mailpoet/lib/EmailEditor/Engine/Renderer/styles.css b/mailpoet/lib/EmailEditor/Engine/Renderer/styles.css
new file mode 100644
index 0000000000..831b9cb444
--- /dev/null
+++ b/mailpoet/lib/EmailEditor/Engine/Renderer/styles.css
@@ -0,0 +1,61 @@
+/* Base CSS rules to be applied to all emails */
+/* Created based on original MailPoet template for rendering emails */
+/* StyleLint is disabled because some rules contain properties that linter marks as unknown, 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% */
+}
+
+table,
+td {
+ border-collapse: collapse;
+ mso-table-lspace: 0;
+ mso-table-rspace: 0;
+}
+
+img {
+ border: 0;
+ height: auto;
+ -ms-interpolation-mode: bicubic;
+ line-height: 100%;
+ outline: none;
+ text-decoration: none;
+}
+
+p {
+ display: block;
+ margin: 13px 0;
+}
+
+/* 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;
+ visibility: hidden;
+}
+
+@media screen and (max-width: 480px) {
+ .email_button {
+ width: 100% !important;
+ }
+}
+
+@media screen and (max-width: 599px) {
+ .email_button {
+ box-sizing: border-box !important;
+ padding: 5px 0 !important;
+ width: 100% !important;
+ }
+}
+/* stylelint-enable property-no-unknown */
diff --git a/mailpoet/lib/EmailEditor/Engine/Renderer/template.html b/mailpoet/lib/EmailEditor/Engine/Renderer/template.html
new file mode 100644
index 0000000000..c118c97574
--- /dev/null
+++ b/mailpoet/lib/EmailEditor/Engine/Renderer/template.html
@@ -0,0 +1,78 @@
+
+
+
{{newsletter_subject}}
+
+
+
+
+ {{email_meta_robots}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/mailpoet/lib/Newsletter/Renderer/Renderer.php b/mailpoet/lib/Newsletter/Renderer/Renderer.php
index 6cb6808146..3031a631c9 100644
--- a/mailpoet/lib/Newsletter/Renderer/Renderer.php
+++ b/mailpoet/lib/Newsletter/Renderer/Renderer.php
@@ -4,7 +4,7 @@ namespace MailPoet\Newsletter\Renderer;
use MailPoet\Config\Env;
use MailPoet\Config\ServicesChecker;
-use MailPoet\EmailEditor\Engine\Renderer\BodyRenderer as GuntenbergBodyRenderer;
+use MailPoet\EmailEditor\Engine\Renderer\Renderer as GuntenbergRenderer;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Features\FeaturesController;
use MailPoet\Logging\LoggerFactory;
@@ -24,8 +24,8 @@ class Renderer {
/** @var BodyRenderer */
private $bodyRenderer;
- /** @var GuntenbergBodyRenderer */
- private $guntenbergBodyRenderer;
+ /** @var GuntenbergRenderer */
+ private $guntenbergRenderer;
/** @var Preprocessor */
private $preprocessor;
@@ -53,7 +53,7 @@ class Renderer {
public function __construct(
BodyRenderer $bodyRenderer,
- GuntenbergBodyRenderer $guntenbergBodyRenderer,
+ GuntenbergRenderer $guntenbergRenderer,
Preprocessor $preprocessor,
\MailPoetVendor\CSS $cSSInliner,
ServicesChecker $servicesChecker,
@@ -64,7 +64,7 @@ class Renderer {
FeaturesController $featuresController
) {
$this->bodyRenderer = $bodyRenderer;
- $this->guntenbergBodyRenderer = $guntenbergBodyRenderer;
+ $this->guntenbergRenderer = $guntenbergRenderer;
$this->preprocessor = $preprocessor;
$this->cSSInliner = $cSSInliner;
$this->servicesChecker = $servicesChecker;
@@ -84,72 +84,69 @@ class Renderer {
}
private function _render(NewsletterEntity $newsletter, SendingTask $sendingTask = null, $type = false, $preview = false, $subject = null) {
- $body = (is_array($newsletter->getBody()))
- ? $newsletter->getBody()
- : [];
- $content = (array_key_exists('content', $body))
- ? $body['content']
- : [];
- $styles = (array_key_exists('globalStyles', $body))
- ? $body['globalStyles']
- : [];
-
- if (
- !$this->servicesChecker->isUserActivelyPaying() && !$preview
- ) {
- $content = $this->addMailpoetLogoContentBlock($content, $styles);
- }
-
$language = $this->wp->getBloginfo('language');
$metaRobots = $preview ? '' : '';
- $renderedBody = "";
- try {
- $content = $this->preprocessor->process($newsletter, $content, $preview, $sendingTask);
- if ($this->featuresController->isSupported(FeaturesController::GUTENBERG_EMAIL_EDITOR) && $newsletter->getWpPostId()) {
- $post = $newsletter->getWpPost();
- if (!$post instanceof \WP_Post) {
- throw new NewsletterProcessingException('Missing email post object');
- }
- $renderedBody = $this->guntenbergBodyRenderer->renderBody($post);
- } else {
+ $subject = $subject ?: $newsletter->getSubject();
+ $wpPost = $newsletter->getWpPost();
+ if ($this->featuresController->isSupported(FeaturesController::GUTENBERG_EMAIL_EDITOR) && $wpPost instanceof \WP_Post) {
+ $renderedNewsletter = $this->guntenbergRenderer->render($wpPost->post_content, $subject, $newsletter->getPreheader(), $language, $metaRobots); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
+ } else {
+ $body = (is_array($newsletter->getBody()))
+ ? $newsletter->getBody()
+ : [];
+ $content = (array_key_exists('content', $body))
+ ? $body['content']
+ : [];
+ $styles = (array_key_exists('globalStyles', $body))
+ ? $body['globalStyles']
+ : [];
+
+ if (
+ !$this->servicesChecker->isUserActivelyPaying() && !$preview
+ ) {
+ $content = $this->addMailpoetLogoContentBlock($content, $styles);
+ }
+
+ $renderedBody = "";
+ try {
+ $content = $this->preprocessor->process($newsletter, $content, $preview, $sendingTask);
$renderedBody = $this->bodyRenderer->renderBody($newsletter, $content);
+ } catch (NewsletterProcessingException $e) {
+ $this->loggerFactory->getLogger(LoggerFactory::TOPIC_COUPONS)->error(
+ $e->getMessage(),
+ ['newsletter_id' => $newsletter->getId()]
+ );
+ $this->newslettersRepository->setAsCorrupt($newsletter);
+ if ($newsletter->getLatestQueue()) {
+ $this->sendingQueuesRepository->pause($newsletter->getLatestQueue());
+ }
}
+ $renderedStyles = $this->renderStyles($styles);
+ $customFontsLinks = StylesHelper::getCustomFontsLinks($styles);
- } catch (NewsletterProcessingException $e) {
- $this->loggerFactory->getLogger(LoggerFactory::TOPIC_COUPONS)->error(
- $e->getMessage(),
- ['newsletter_id' => $newsletter->getId()]
+ $template = $this->injectContentIntoTemplate(
+ (string)file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE),
+ [
+ $language,
+ $metaRobots,
+ htmlspecialchars($subject),
+ $renderedStyles,
+ $customFontsLinks,
+ EHelper::escapeHtmlText($newsletter->getPreheader()),
+ $renderedBody,
+ ]
);
- $this->newslettersRepository->setAsCorrupt($newsletter);
- if ($newsletter->getLatestQueue()) {
- $this->sendingQueuesRepository->pause($newsletter->getLatestQueue());
+ if ($template === null) {
+ $template = '';
}
- }
- $renderedStyles = $this->renderStyles($styles);
- $customFontsLinks = StylesHelper::getCustomFontsLinks($styles);
+ $templateDom = $this->inlineCSSStyles($template);
+ $template = $this->postProcessTemplate($templateDom);
- $template = $this->injectContentIntoTemplate(
- (string)file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE),
- [
- $language,
- $metaRobots,
- htmlspecialchars($subject ?: $newsletter->getSubject()),
- $renderedStyles,
- $customFontsLinks,
- EHelper::escapeHtmlText($newsletter->getPreheader()),
- $renderedBody,
- ]
- );
- if ($template === null) {
- $template = '';
+ $renderedNewsletter = [
+ 'html' => $template,
+ 'text' => $this->renderTextVersion($template),
+ ];
}
- $templateDom = $this->inlineCSSStyles($template);
- $template = $this->postProcessTemplate($templateDom);
-
- $renderedNewsletter = [
- 'html' => $template,
- 'text' => $this->renderTextVersion($template),
- ];
return ($type && !empty($renderedNewsletter[$type])) ?
$renderedNewsletter[$type] :
diff --git a/mailpoet/tests/integration/Newsletter/RendererTest.php b/mailpoet/tests/integration/Newsletter/RendererTest.php
index 35be8e28f1..6fe313bddb 100644
--- a/mailpoet/tests/integration/Newsletter/RendererTest.php
+++ b/mailpoet/tests/integration/Newsletter/RendererTest.php
@@ -55,7 +55,7 @@ class RendererTest extends \MailPoetTest {
$this->servicesChecker = $this->createMock(ServicesChecker::class);
$this->renderer = new Renderer(
$this->diContainer->get(BodyRenderer::class),
- $this->diContainer->get(\MailPoet\EmailEditor\Engine\Renderer\BodyRenderer::class),
+ $this->diContainer->get(\MailPoet\EmailEditor\Engine\Renderer\Renderer::class),
$this->diContainer->get(Preprocessor::class),
$this->diContainer->get(\MailPoetVendor\CSS::class),
$this->servicesChecker,
diff --git a/mailpoet/tests/integration/WooCommerce/TransactionalEmails/RendererTest.php b/mailpoet/tests/integration/WooCommerce/TransactionalEmails/RendererTest.php
index 0a68ce9c1d..f56ee2e075 100644
--- a/mailpoet/tests/integration/WooCommerce/TransactionalEmails/RendererTest.php
+++ b/mailpoet/tests/integration/WooCommerce/TransactionalEmails/RendererTest.php
@@ -139,7 +139,7 @@ class RendererTest extends \MailPoetTest {
));
return new NewsletterRenderer(
$this->diContainer->get(\MailPoet\Newsletter\Renderer\BodyRenderer::class),
- $this->diContainer->get(\MailPoet\EmailEditor\Engine\Renderer\BodyRenderer::class),
+ $this->diContainer->get(\MailPoet\EmailEditor\Engine\Renderer\Renderer::class),
new Preprocessor(
$this->diContainer->get(\MailPoet\Newsletter\Renderer\Blocks\AbandonedCartContent::class),
$this->diContainer->get(\MailPoet\Newsletter\Renderer\Blocks\AutomatedLatestContentBlock::class),