Files
piratepoet/mailpoet/lib/Newsletter/Renderer/Renderer.php
Rostislav Wolny 46a481ec24 Add email renderer and template to the renderer engine
In this commit, I copied the code for processing the rendering of emails
from the current renderer.
This will allow us to use different base templates and styles.
Ideally, we should be able to add hooks and reuse the renderer from the engine namespace in
the current renderer.
[MAILPOET-5540]
2023-09-21 14:20:35 +02:00

279 lines
8.9 KiB
PHP

<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Newsletter\Renderer;
use MailPoet\Config\Env;
use MailPoet\Config\ServicesChecker;
use MailPoet\EmailEditor\Engine\Renderer\Renderer as GuntenbergRenderer;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Features\FeaturesController;
use MailPoet\Logging\LoggerFactory;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
use MailPoet\NewsletterProcessingException;
use MailPoet\Tasks\Sending as SendingTask;
use MailPoet\Util\pQuery\DomNode;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Html2Text\Html2Text;
class Renderer {
const NEWSLETTER_TEMPLATE = 'Template.html';
const FILTER_POST_PROCESS = 'mailpoet_rendering_post_process';
/** @var BodyRenderer */
private $bodyRenderer;
/** @var GuntenbergRenderer */
private $guntenbergRenderer;
/** @var Preprocessor */
private $preprocessor;
/** @var \MailPoetVendor\CSS */
private $cSSInliner;
/** @var ServicesChecker */
private $servicesChecker;
/** @var WPFunctions */
private $wp;
/*** @var LoggerFactory */
private $loggerFactory;
/*** @var NewslettersRepository */
private $newslettersRepository;
/*** @var SendingQueuesRepository */
private $sendingQueuesRepository;
/** @var FeaturesController */
private $featuresController;
public function __construct(
BodyRenderer $bodyRenderer,
GuntenbergRenderer $guntenbergRenderer,
Preprocessor $preprocessor,
\MailPoetVendor\CSS $cSSInliner,
ServicesChecker $servicesChecker,
WPFunctions $wp,
LoggerFactory $loggerFactory,
NewslettersRepository $newslettersRepository,
SendingQueuesRepository $sendingQueuesRepository,
FeaturesController $featuresController
) {
$this->bodyRenderer = $bodyRenderer;
$this->guntenbergRenderer = $guntenbergRenderer;
$this->preprocessor = $preprocessor;
$this->cSSInliner = $cSSInliner;
$this->servicesChecker = $servicesChecker;
$this->wp = $wp;
$this->loggerFactory = $loggerFactory;
$this->newslettersRepository = $newslettersRepository;
$this->sendingQueuesRepository = $sendingQueuesRepository;
$this->featuresController = $featuresController;
}
public function render(NewsletterEntity $newsletter, SendingTask $sendingTask = null, $type = false) {
return $this->_render($newsletter, $sendingTask, $type);
}
public function renderAsPreview(NewsletterEntity $newsletter, $type = false, ?string $subject = null) {
return $this->_render($newsletter, null, $type, true, $subject);
}
private function _render(NewsletterEntity $newsletter, SendingTask $sendingTask = null, $type = false, $preview = false, $subject = null) {
$language = $this->wp->getBloginfo('language');
$metaRobots = $preview ? '<meta name="robots" content="noindex, nofollow" />' : '';
$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);
$template = $this->injectContentIntoTemplate(
(string)file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE),
[
$language,
$metaRobots,
htmlspecialchars($subject),
$renderedStyles,
$customFontsLinks,
EHelper::escapeHtmlText($newsletter->getPreheader()),
$renderedBody,
]
);
if ($template === null) {
$template = '';
}
$templateDom = $this->inlineCSSStyles($template);
$template = $this->postProcessTemplate($templateDom);
$renderedNewsletter = [
'html' => $template,
'text' => $this->renderTextVersion($template),
];
}
return ($type && !empty($renderedNewsletter[$type])) ?
$renderedNewsletter[$type] :
$renderedNewsletter;
}
/**
* @param array $styles
* @return string
*/
private function renderStyles(array $styles) {
$css = '';
foreach ($styles as $selector => $style) {
switch ($selector) {
case 'text':
$selector = 'td.mailpoet_paragraph, td.mailpoet_blockquote, li.mailpoet_paragraph';
break;
case 'body':
$selector = 'body, .mailpoet-wrapper';
break;
case 'link':
$selector = '.mailpoet-wrapper a';
break;
case 'wrapper':
$selector = '.mailpoet_content-wrapper';
break;
}
if (!is_array($style)) {
continue;
}
$css .= StylesHelper::setStyle($style, $selector);
}
return $css;
}
/**
* @param string $template
* @param string[] $content
* @return string|null
*/
private function injectContentIntoTemplate($template, $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('<!--[if !mso]><![endif]-->', '<!--[if !mso]><!-- -->', $template);
$template = $this->wp->applyFilters(
self::FILTER_POST_PROCESS,
$template
);
return $template;
}
/**
* @param array $content
* @param array $styles
* @return array
*/
private function addMailpoetLogoContentBlock(array $content, array $styles) {
if (empty($content['blocks'])) return $content;
$content['blocks'][] = [
'type' => 'container',
'orientation' => 'horizontal',
'styles' => [
'block' => [
'backgroundColor' => (!empty($styles['body']['backgroundColor'])) ?
$styles['body']['backgroundColor'] :
'transparent',
],
],
'blocks' => [
[
'type' => 'container',
'orientation' => 'vertical',
'styles' => [
],
'blocks' => [
[
'type' => 'image',
'link' => 'https://www.mailpoet.com/?ref=free-plan-user-email&utm_source=free_plan_user_email&utm_medium=email',
'src' => Env::$assetsUrl . '/img/mailpoet_logo_newsletter.png',
'fullWidth' => false,
'alt' => 'Email Marketing Powered by MailPoet',
'width' => '108px',
'height' => '65px',
'styles' => [
'block' => [
'textAlign' => 'center',
],
],
],
],
],
],
];
return $content;
}
}