cssParser = $cssParser; $this->htmlBeforeContent = ''; $this->htmlAfterContent = ''; $this->renderer = $renderer; $this->shortcodes = $shortcodes; } public function render(NewsletterEntity $newsletter, ?string $subject = null) { $preparedNewsletter = $this->prepareNewsletterForRendering($newsletter); $renderedNewsletter = $this->renderer->renderAsPreview($preparedNewsletter, 'html', $subject); $headingText = $subject ?? ''; $renderedHtml = $this->processShortcodes($preparedNewsletter, $renderedNewsletter); $renderedHtml = str_replace(ContentPreprocessor::WC_HEADING_PLACEHOLDER, $headingText, $renderedHtml); $html = explode(ContentPreprocessor::WC_CONTENT_PLACEHOLDER, $renderedHtml); $this->htmlBeforeContent = $html[0]; $this->htmlAfterContent = $html[1]; } public function getHTMLBeforeContent() { if (empty($this->htmlBeforeContent)) { throw new \Exception("You should call 'render' before 'getHTMLBeforeContent'"); } return $this->htmlBeforeContent . '
'; } public function getHTMLAfterContent() { if (empty($this->htmlAfterContent)) { throw new \Exception("You should call 'render' before 'getHTMLAfterContent'"); } return '
' . $this->htmlAfterContent; } /** * In this method we alter the rendered content that is output when processing the WooCommerce email template. * - We update inlined font-family rules in the content block generated by Woo */ public function updateRenderedContent(NewsletterEntity $newsletter, string $content): string { $isSavedWithStyledWooBlock = $newsletter->getGlobalStyle('woocommerce', 'isSavedWithUpdatedStyles'); // For Backward compatibility do not apply styles for content unless the template was edited with the editor // and user visually checked and is aware of updated styles feature. if (!$isSavedWithStyledWooBlock) { return $content; } $contentParts = explode('', $content); if (count($contentParts) !== 3) { return $content; } [$beforeWooContent, $wooContent, $afterWooContent] = $contentParts; $fontFamily = $newsletter->getGlobalStyle('text', 'fontFamily'); $replaceFontFamilyCallback = function ($matches) use ($fontFamily) { $pattern = '/font-family\s*:\s*[^;]+;/i'; $style = $matches[1]; $style = preg_replace($pattern, "font-family:$fontFamily;", $style); return 'style="' . esc_attr($style) . '"'; }; $stylePattern = '/style="(.*?)"/i'; $wooContent = (string)preg_replace_callback($stylePattern, $replaceFontFamilyCallback, $wooContent); return implode('', [$beforeWooContent, $wooContent, $afterWooContent]); } /** * In this method we alter CSS that is later inlined into the WooCommerce email template. WooCommerce use Emogrifier to inline CSS. * The inlining is called after the rendering and after the modifications we apply to the rendered content in self::updateRenderedContent * - We prefix the original selectors to avoid inlining those rules into content added int the MailPoet's editor. * - We update the font-family in the original CSS if it's set in the editor. * - We update the font-size for the inner content if it's set in the editor. */ public function enhanceCss(string $css, NewsletterEntity $newsletter): string { $this->cssParser->settings['compress_colors'] = false; $this->cssParser->parse($css); foreach ($this->cssParser->css as $index => $rules) { $this->cssParser->css[$index] = []; foreach ($rules as $selectors => $properties) { $properties = $this->updateStyleDefinition($selectors, $newsletter, $properties); $selectors = explode(',', $selectors); $selectors = array_map(function($selector) { return '#' . self::CONTENT_CONTAINER_ID . ' ' . $selector; }, $selectors); $selectors = implode(',', $selectors); $this->cssParser->css[$index][$selectors] = $properties; } } /** @var csstidy_print */ $print = $this->cssParser->print; $css = $print->plain(); // Enforce the special heading color for the WooCommerce email header $wooHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'headingFontColor'); if ($wooHeadingColor) { $css .= "#mailpoet-woo-email-header { color: $wooHeadingColor !important; }"; } return $css; } private function processShortcodes(NewsletterEntity $newsletter, $content) { $this->shortcodes->setQueue(null); $this->shortcodes->setSubscriber(null); $this->shortcodes->setNewsletter($newsletter); return $this->shortcodes->replace($content); } /** * This method prepares the newsletter for rendering * - We ensure that the font-family and branding color are used as default for all headings */ private function prepareNewsletterForRendering(NewsletterEntity $newsletter): NewsletterEntity { $newsletterClone = clone($newsletter); $headingFontFamily = $newsletter->getGlobalStyle('woocommerce', 'headingFontFamily'); if ($headingFontFamily) { $newsletterClone->setGlobalStyle('h1', 'fontFamily', $headingFontFamily); $newsletterClone->setGlobalStyle('h2', 'fontFamily', $headingFontFamily); $newsletterClone->setGlobalStyle('h3', 'fontFamily', $headingFontFamily); } $brandingColor = $newsletter->getGlobalStyle('woocommerce', 'brandingColor'); $contentHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'contentHeadingFontColor') ?? $brandingColor; if ($contentHeadingColor) { $newsletterClone->setGlobalStyle('h1', 'color', $contentHeadingColor); $newsletterClone->setGlobalStyle('h2', 'color', $contentHeadingColor); $newsletterClone->setGlobalStyle('h3', 'color', $contentHeadingColor); } return $newsletterClone; } private function updateStyleDefinition(string $selectors, NewsletterEntity $newsletter, $properties) { // For Backward compatibility do not apply styles for content unless the template was edited with the editor // and user visually checked and is aware of updated styles feature. $isSavedWithStyledWooBlock = $newsletter->getGlobalStyle('woocommerce', 'isSavedWithUpdatedStyles'); if (!$isSavedWithStyledWooBlock) { return $properties; } if (!is_array($properties)) { $properties = []; } $fontFamily = $newsletter->getGlobalStyle('text', 'fontFamily'); $headingFontFamily = $newsletter->getGlobalStyle('woocommerce', 'headingFontFamily'); $fontSize = $newsletter->getGlobalStyle('text', 'fontSize'); $brandingColor = $newsletter->getGlobalStyle('woocommerce', 'brandingColor'); $contentHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'contentHeadingFontColor') ?? $brandingColor; // Update font family if it's set in the editor if ($fontFamily && !empty($properties['font-family'])) { $properties['font-family'] = $fontFamily; } // Update font size for inner content if ($fontSize && ($selectors === '#body_content_inner')) { $properties['font-size'] = $fontSize; } // Update heading font sizes and font family $supportedHeadings = ['h1', 'h2', 'h3']; foreach ($supportedHeadings as $heading) { $headingFontSize = $newsletter->getGlobalStyle($heading, 'fontSize'); if ($headingFontSize && ($selectors === $heading)) { $properties['font-size'] = $headingFontSize; } if ($headingFontFamily && ($selectors === $heading)) { $properties['font-family'] = $headingFontFamily; } if ($contentHeadingColor && ($selectors === $heading)) { $properties['color'] = $contentHeadingColor; } } return $properties; } }