<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Newsletter\ViewInBrowser;
if (!defined('ABSPATH')) exit;
use Automattic\WooCommerce\EmailEditor\Email_Editor_Container;
use Automattic\WooCommerce\EmailEditor\Engine\Personalizer;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\SendingQueueEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Newsletter\Links\Links;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\Newsletter\Url as NewsletterUrl;
use MailPoet\Settings\TrackingConfig;
use MailPoet\WP\Emoji;
class ViewInBrowserRenderer {
private const OPTION_RENDER_AS_PREVIEW = 'renderAsPreview';
private const OPTION_SANITIZE_RENDERED_QUEUE_LINKS = 'sanitizeRenderedQueueLinks';
private const OPTION_SHORTCODES_PREVIEW = 'shortcodesPreview';
private const OPTION_REPLACE_SUBSCRIBER_TRACKING = 'replaceSubscriberTracking';
private const OPTION_PERSONALIZER_PREVIEW = 'personalizerPreview';
private const OPTION_REPLACE_VIEW_IN_BROWSER_URL = 'replaceViewInBrowserUrlWithPublicShareUrl';
/** @var Emoji */
private $emoji;
/** @var TrackingConfig */
private $trackingConfig;
/** @var Renderer */
private $renderer;
/** @var Shortcodes */
private $shortcodes;
/** @var Links */
private $links;
/** @var NewsletterUrl */
private $newsletterUrl;
private Personalizer $personalizer;
public function __construct(
Emoji $emoji,
TrackingConfig $trackingConfig,
Shortcodes $shortcodes,
Renderer $renderer,
Links $links,
NewsletterUrl $newsletterUrl
) {
$this->emoji = $emoji;
$this->trackingConfig = $trackingConfig;
$this->renderer = $renderer;
$this->shortcodes = $shortcodes;
$this->links = $links;
$this->newsletterUrl = $newsletterUrl;
$this->personalizer = Email_Editor_Container::container()->get(Personalizer::class);
}
public function render(
bool $isPreview,
NewsletterEntity $newsletter,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null
) {
return $this->renderWithContext(
$newsletter,
$subscriber,
$queue,
$this->buildRenderOptionsForPreview($isPreview)
);
}
public function renderPublicShare(
NewsletterEntity $newsletter,
?SendingQueueEntity $queue = null
): string {
// An empty SubscriberEntity (not null) is intentional: the subscriber
// shortcode handler treats null as "leave the shortcode in place" but
// resolves `default:` arguments against any SubscriberEntity instance.
return $this->renderWithContext(
$newsletter,
new SubscriberEntity(),
$queue,
$this->buildRenderOptionsForPublicShare()
);
}
/**
* @param array{
* renderAsPreview: bool,
* sanitizeRenderedQueueLinks: bool,
* shortcodesPreview: bool,
* replaceSubscriberTracking: bool,
* personalizerPreview: bool,
* replaceViewInBrowserUrlWithPublicShareUrl: bool
* } $options
*/
private function renderWithContext(
NewsletterEntity $newsletter,
?SubscriberEntity $subscriber,
?SendingQueueEntity $queue,
array $options
): string {
$renderAsPreview = $options[self::OPTION_RENDER_AS_PREVIEW];
$sanitizeRenderedQueueLinks = $options[self::OPTION_SANITIZE_RENDERED_QUEUE_LINKS];
$shortcodesPreview = $options[self::OPTION_SHORTCODES_PREVIEW];
$replaceSubscriberTracking = $options[self::OPTION_REPLACE_SUBSCRIBER_TRACKING];
$personalizerPreview = $options[self::OPTION_PERSONALIZER_PREVIEW];
$replaceViewInBrowserUrlWithPublicShareUrl = $options[self::OPTION_REPLACE_VIEW_IN_BROWSER_URL];
$isTrackingEnabled = $this->trackingConfig->isEmailTrackingEnabled();
if ($queue && $queue->getNewsletterRenderedBody()) {
$body = $queue->getNewsletterRenderedBody();
if (is_array($body)) {
$newsletterBody = $body['html'];
} else {
$newsletterBody = '';
}
$newsletterBody = $this->emoji->decodeEmojisInBody($newsletterBody);
// rendered newsletter body has shortcodes converted to links; we need to
// isolate "view in browser", "unsubscribe" and "manage subscription" links
// and convert them to shortcodes, which later will be replaced with "#" when
// newsletter is previewed
if ($sanitizeRenderedQueueLinks && preg_match($this->links->getLinkRegex(), $newsletterBody)) {
$newsletterBody = $this->links->convertHashedLinksToShortcodesAndUrls(
$newsletterBody,
$queue->getId(),
$convertAll = true
);
// remove open tracking link
$newsletterBody = str_replace(Links::DATA_TAG_OPEN, '', $newsletterBody);
}
} else {
if ($renderAsPreview) {
$newsletterBody = $this->renderer->renderAsPreview($newsletter, 'html');
} else {
$newsletterBody = $this->renderer->render($newsletter, $sendingTask = null, 'html');
}
}
$this->prepareShortcodes(
$newsletter,
$subscriber,
$queue,
$shortcodesPreview
);
$renderedNewsletter = $this->shortcodes->replace($newsletterBody);
if ($replaceViewInBrowserUrlWithPublicShareUrl) {
$renderedNewsletter = str_replace(
$this->newsletterUrl->getViewInBrowserUrl($newsletter, null, $queue, true),
$this->newsletterUrl->getPublicShareUrl($newsletter),
$renderedNewsletter
);
}
if ($replaceSubscriberTracking && $queue && $subscriber && $isTrackingEnabled) {
$renderedNewsletter = $this->links->replaceSubscriberData(
$subscriber->getId(),
$queue->getId(),
$renderedNewsletter
);
}
if ($newsletter->getWpPostId() !== null) {
$this->personalizer->set_context([
'recipient_email' => $subscriber ? $subscriber->getEmail() : null,
'is_user_preview' => $personalizerPreview,
'newsletter_id' => $newsletter->getId(),
'queue_id' => $queue ? $queue->getId() : null,
]);
$renderedNewsletter = $this->personalizer->personalize_content($renderedNewsletter);
}
return $renderedNewsletter;
}
/**
* @return array{
* renderAsPreview: bool,
* sanitizeRenderedQueueLinks: bool,
* shortcodesPreview: bool,
* replaceSubscriberTracking: bool,
* personalizerPreview: bool,
* replaceViewInBrowserUrlWithPublicShareUrl: bool
* }
*/
private function buildRenderOptionsForPreview(bool $isPreview): array {
return [
self::OPTION_RENDER_AS_PREVIEW => $isPreview,
self::OPTION_SANITIZE_RENDERED_QUEUE_LINKS => $isPreview,
self::OPTION_SHORTCODES_PREVIEW => $isPreview,
self::OPTION_REPLACE_SUBSCRIBER_TRACKING => !$isPreview,
self::OPTION_PERSONALIZER_PREVIEW => $isPreview,
self::OPTION_REPLACE_VIEW_IN_BROWSER_URL => false,
];
}
/**
* @return array{
* renderAsPreview: bool,
* sanitizeRenderedQueueLinks: bool,
* shortcodesPreview: bool,
* replaceSubscriberTracking: bool,
* personalizerPreview: bool,
* replaceViewInBrowserUrlWithPublicShareUrl: bool
* }
*/
private function buildRenderOptionsForPublicShare(): array {
return [
// Keep no-queue public share fallback in preview mode to avoid
// send-time preprocessing side effects and keep shortcode behavior
// consistent with the rendering mode.
self::OPTION_RENDER_AS_PREVIEW => true,
self::OPTION_SANITIZE_RENDERED_QUEUE_LINKS => true,
self::OPTION_SHORTCODES_PREVIEW => true,
self::OPTION_REPLACE_SUBSCRIBER_TRACKING => false,
self::OPTION_PERSONALIZER_PREVIEW => true,
self::OPTION_REPLACE_VIEW_IN_BROWSER_URL => true,
];
}
private function prepareShortcodes(
NewsletterEntity $newsletter,
?SubscriberEntity $subscriber,
?SendingQueueEntity $queue,
bool $wpUserPreview
) {
$this->shortcodes->setQueue($queue);
$this->shortcodes->setNewsletter($newsletter);
$this->shortcodes->setWpUserPreview($wpUserPreview);
$this->shortcodes->setSubscriber($subscriber);
}
}