GlobalSettings/Settings.php000064400000002517150732267700012014 0ustar00app = $app; } /** * Render the page for native global settings components * * @throws \Exception */ public function render() { $this->enqueue(); $this->app->view->render('admin.globalSettings.settings'); } /** * Enqueue necessary resources. * * @throws \Exception */ public function enqueue() { wp_enqueue_script('fluentform-global-settings-js'); wp_localize_script('fluentform-global-settings-js', 'FluentFormApp', [ 'plugin' => $this->app->config->get('app.slug'), 'akismet_activated' => AkismetHandler::isPluginEnabled(), 'has_pro' => defined('FLUENTFORMPRO'), 'form_settings_str' => TranslationString::getGlobalSettingsI18n() ]); } } Preprocessor.php000064400000006115150732301320007742 0ustar00 '; const WC_HEADING_AFTER = ' '; /** @var AbandonedCartContent */ private $abandonedCartContent; /** @var AutomatedLatestContentBlock */ private $automatedLatestContent; /** @var ContentPreprocessor */ private $wooCommerceContentPreprocessor; /*** @var CouponPreProcessor */ private $couponPreProcessor; public function __construct( AbandonedCartContent $abandonedCartContent, AutomatedLatestContentBlock $automatedLatestContent, ContentPreprocessor $wooCommerceContentPreprocessor, CouponPreProcessor $couponPreProcessor ) { $this->abandonedCartContent = $abandonedCartContent; $this->automatedLatestContent = $automatedLatestContent; $this->wooCommerceContentPreprocessor = $wooCommerceContentPreprocessor; $this->couponPreProcessor = $couponPreProcessor; } /** * @param array $content * @param NewsletterEntity $newsletter * @return array */ public function process(NewsletterEntity $newsletter, $content, bool $preview = false, SendingQueueEntity $sendingQueue = null) { if (!array_key_exists('blocks', $content)) { return $content; } $blocks = []; $contentBlocks = $content['blocks']; $contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview); foreach ($contentBlocks as $block) { $processedBlock = $this->processBlock($newsletter, $block, $preview, $sendingQueue); if (!empty($processedBlock)) { $blocks = array_merge($blocks, $processedBlock); } } $content['blocks'] = $blocks; return $content; } public function processBlock(NewsletterEntity $newsletter, array $block, bool $preview = false, SendingQueueEntity $sendingQueue = null): array { switch ($block['type']) { case 'abandonedCartContent': return $this->abandonedCartContent->render($newsletter, $block, $preview, $sendingQueue); case 'automatedLatestContentLayout': return $this->automatedLatestContent->render($newsletter, $block); case 'woocommerceHeading': return $this->wooCommerceContentPreprocessor->preprocessHeader(); case 'woocommerceContent': return $this->wooCommerceContentPreprocessor->preprocessContent(); } return [$block]; } } BodyRenderer.php000064400000002072150732301320007636 0ustar00blocksRenderer = $blocksRenderer; $this->columnsRenderer = $columnsRenderer; } /** * @param NewsletterEntity $newsletter * @param array $content * @return string */ public function renderBody(NewsletterEntity $newsletter, array $content) { $blocks = (array_key_exists('blocks', $content)) ? $content['blocks'] : []; $renderedContent = []; foreach ($blocks as $contentBlock) { $columnsData = $this->blocksRenderer->render($newsletter, $contentBlock); $renderedContent[] = $this->columnsRenderer->render( $contentBlock, $columnsData ); } return implode('', $renderedContent); } } index.php000064400000000006150732301320006354 0ustar00 {{newsletter_meta_robots}} {{newsletter_subject}} {{newsletter_custom_fonts}}
{{newsletter_preheader}}
{{newsletter_body}}
Blocks/Footer.php000064400000003657150732301320007737 0ustar00', $element['text']); $element['text'] = preg_replace('/(<\/?p.*?>)/i', '', $element['text']); $lineHeight = sprintf( '%spx', StylesHelper::$defaultLineHeight * (int)$element['styles']['text']['fontSize'] ); if (!is_string($element['text'])) { throw new \RuntimeException('$element[\'text\'] should be a string.'); } $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($element['text']); if (isset($element['styles']['link'])) { $links = $DOM->query('a'); if ($links->count()) { $css = new CSS(); foreach ($links as $link) { $elementLinkStyles = StylesHelper::getStyles($element['styles'], 'link'); $link->style = $css->mergeInlineStyles($elementLinkStyles, $link->style); } } } $backgroundColor = $element['styles']['block']['backgroundColor']; $backgroundColor = ($backgroundColor !== 'transparent') ? 'bgcolor="' . $backgroundColor . '"' : false; if (!$backgroundColor) unset($element['styles']['block']['backgroundColor']); $style = 'line-height: ' . $lineHeight . ';' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text'); $style = EHelper::escapeHtmlStyleAttr($style); $template = ' ' . str_replace('&', '&', $DOM->html()) . ' '; return $template; } } Blocks/Divider.php000064400000003277150732301320010065 0ustar00
'; return $template; } } Blocks/index.php000064400000000006150732301320007571 0ustar00ALCBlock = $ALCBlock; } public function render( NewsletterEntity $newsletter, array $args, bool $preview = false, SendingQueueEntity $sendingQueue = null ): array { if ( !in_array( $newsletter->getType(), [ NewsletterEntity::TYPE_AUTOMATIC, NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL, NewsletterEntity::TYPE_AUTOMATION, ], true ) ) { // Do not display the block if not an automatic email return []; } $groupOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption = null) { if (!$newsletterOption) return false; $optionField = $newsletterOption->getOptionField(); return $optionField && $optionField->getName() === 'group'; })->first(); $eventOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption = null) { if (!$newsletterOption) return false; $optionField = $newsletterOption->getOptionField(); return $optionField && $optionField->getName() === 'event'; })->first(); if ( ($groupOption instanceof NewsletterOptionEntity && $groupOption->getValue() !== WooCommerceEmail::SLUG) || ($eventOption instanceof NewsletterOptionEntity && $eventOption->getValue() !== AbandonedCart::SLUG) ) { // Do not display the block if not an AbandonedCart email return []; } if ($preview) { // Display latest products for preview (no 'posts' argument specified) return $this->ALCBlock->render($newsletter, $args); } if (!$sendingQueue) { // Do not display the block if we're not sending an email return []; } $meta = $sendingQueue->getMeta(); if (empty($meta[AbandonedCart::TASK_META_NAME])) { // Do not display the block if a cart is empty return []; } $args['amount'] = 50; $args['posts'] = $meta[AbandonedCart::TASK_META_NAME]; return $this->ALCBlock->render($newsletter, $args); } } Blocks/AutomatedLatestContentBlock.php000064400000004615150732301320014102 0ustar00renderedPostsInNewsletter = []; $this->ALC = $ALC; $this->newsletterPostsRepository = $newsletterPostsRepository; } public function render(NewsletterEntity $newsletter, $args) { $newerThanTimestamp = false; $newsletterId = false; if ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION_HISTORY) { $parent = $newsletter->getParent(); if ($parent instanceof NewsletterEntity) { $newsletterId = $parent->getId(); $lastPost = $this->newsletterPostsRepository->findOneBy(['newsletter' => $parent], ['createdAt' => 'desc']); if ($lastPost instanceof NewsletterPostEntity) { $newerThanTimestamp = $lastPost->getCreatedAt(); } } } $postsToExclude = $this->getRenderedPosts((int)$newsletterId); $query = new BlockPostQuery([ 'args' => $args, 'postsToExclude' => $postsToExclude, 'newsletterId' => $newsletterId, 'newerThanTimestamp' => $newerThanTimestamp, 'dynamic' => true, ]); $aLCPosts = $this->ALC->getPosts($query); foreach ($aLCPosts as $post) { $postsToExclude[] = $post->ID; } $this->setRenderedPosts((int)$newsletterId, $postsToExclude); return $this->ALC->transformPosts($args, $aLCPosts); } private function getRenderedPosts(int $newsletterId) { return $this->renderedPostsInNewsletter[$newsletterId] ?? []; } private function setRenderedPosts(int $newsletterId, array $posts) { return $this->renderedPostsInNewsletter[$newsletterId] = $posts; } } Blocks/Spacer.php000064400000001275150732301320007710 0ustar00 '; return $template; } } Blocks/Coupon.php000064400000010212150732301320007725 0ustar00helper = $helper; } public function render($element, $columnBaseWidth) { $couponCode = self::CODE_PLACEHOLDER; if (!empty($element['couponId'])) { try { $couponCode = $this->helper->wcGetCouponCodeById((int)$element['couponId']); } catch (\Exception $e) { if (!$this->helper->isWooCommerceActive()) { throw NewsletterProcessingException::create()->withMessage(__('WooCommerce is not active', 'mailpoet')); } else { throw NewsletterProcessingException::create()->withMessage($e->getMessage())->withCode($e->getCode()); } } if (empty($couponCode)) { throw NewsletterProcessingException::create()->withMessage(__('Couldn\'t find the coupon. Please update the email if the coupon was removed.', 'mailpoet')); } } $element['styles']['block']['width'] = $this->calculateWidth($element, $columnBaseWidth); $styles = 'display:inline-block;-webkit-text-size-adjust:none;mso-hide:all;text-decoration:none;text-align:center;' . StylesHelper::getBlockStyles($element, $exclude = ['textAlign']); $styles = EHelper::escapeHtmlStyleAttr($styles); $template = '
' . EHelper::escapeHtmlText($couponCode) . '
'; return $template; } public function calculateWidth($element, $columnBaseWidth): string { $columnWidth = $columnBaseWidth - (StylesHelper::$paddingWidth * 2); $borderWidth = (int)$element['styles']['block']['borderWidth']; $width = (int)$element['styles']['block']['width']; $width = ($width > $columnWidth) ? $columnWidth : $width; return ($width - (2 * $borderWidth) . 'px'); } } Blocks/Social.php000064400000002734150732301320007706 0ustar00 $icon) { if (empty($icon['image'])) { continue; } $style = 'width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;'; $iconsBlock .= '' . EHelper::escapeHtmlAttr($icon['iconType']) . ' '; } } $alignment = isset($element['styles']['block']['textAlign']) ? $element['styles']['block']['textAlign'] : 'center'; if (!empty($iconsBlock)) { $template = ' ' . $iconsBlock . ' '; return $template; } } } Blocks/Text.php000064400000015465150732301320007425 0ustar00convertBlockquotesToTables($html); $html = $this->convertParagraphsToTables($html); $html = $this->styleLists($html); $html = $this->styleHeadings($html); $html = $this->removeLastLineBreak($html); $template = ' ' . $html . ' '; return $template; } public function convertBlockquotesToTables($html) { $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($html); $blockquotes = $DOM->query('blockquote'); foreach ($blockquotes as $blockquote) { $contents = []; $paragraphs = $blockquote->query('p, h1, h2, h3, h4', 0); foreach ($paragraphs as $index => $paragraph) { if (preg_match('/h\d/', $paragraph->getTag())) { $contents[] = $paragraph->getOuterText(); } else { $contents[] = $paragraph->toString(true, true, 1); } if ($index + 1 < $paragraphs->count()) $contents[] = '
'; $paragraph->remove(); } if (empty($contents)) continue; $blockquote->setTag('table'); $blockquote->addClass('mailpoet_blockquote'); $blockquote->width = '100%'; $blockquote->spacing = 0; $blockquote->border = 0; $blockquote->cellpadding = 0; $blockquote->html('
' . implode('', $contents) . '
' ); $blockquote = $this->insertLineBreak($blockquote); } return $DOM->__toString(); } public function convertParagraphsToTables($html) { $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($html); $paragraphs = $DOM->query('p'); if (!$paragraphs->count()) return $html; foreach ($paragraphs as $paragraph) { // process empty paragraphs if (!trim($paragraph->html())) { $nextElement = ($paragraph->getNextSibling()) ? trim($paragraph->getNextSibling()->text()) : false; $previousElement = ($paragraph->getPreviousSibling()) ? trim($paragraph->getPreviousSibling()->text()) : false; $previousElementTag = ($previousElement) ? $paragraph->getPreviousSibling()->tag : false; // if previous or next paragraphs are empty OR previous paragraph // is a heading, insert a break line if ( !$nextElement || !$previousElement || (preg_match('/h\d+/', $previousElementTag)) ) { $paragraph = $this->insertLineBreak($paragraph); } $paragraph->remove(); continue; } $style = (string)$paragraph->style; if (!preg_match('/text-align/i', $style)) { $style = 'text-align: left;' . $style; } $contents = $paragraph->toString(true, true, 1); $paragraph->setTag('table'); $paragraph->style = 'border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;'; $paragraph->width = '100%'; $paragraph->cellpadding = 0; $nextElement = $paragraph->getNextSibling(); // unless this is the last element in column, add double line breaks $lineBreaks = ($nextElement && !trim($nextElement->text())) ? '

' : ''; // if this element is followed by a list, add single line break $lineBreaks = ($nextElement && preg_match('/
  • getOuterText())) ? '
    ' : $lineBreaks; if ($paragraph->hasClass(PostContentManager::WP_POST_CLASS)) { $paragraph->removeClass(PostContentManager::WP_POST_CLASS); // if this element is followed by a paragraph or heading, add double line breaks $lineBreaks = ($nextElement && preg_match('/<(p|h[1-6]{1})/i', $nextElement->getOuterText())) ? '

    ' : $lineBreaks; } $paragraph->html(' ' . $contents . $lineBreaks . ' ' ); } return $DOM->__toString(); } public function styleLists($html) { $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($html); $lists = $DOM->query('ol, ul, li'); if (!$lists->count()) return $html; foreach ($lists as $list) { if ($list->tag === 'li') { $list->setInnertext($list->toString(true, true, 1)); $list->class = 'mailpoet_paragraph'; } else { $list->class = 'mailpoet_paragraph'; $list->style = StylesHelper::joinStyles($list->style, 'padding-top:0;padding-bottom:0;margin-top:10px;'); } $list->style = StylesHelper::applyTextAlignment($list->style); $list->style = StylesHelper::joinStyles($list->style, 'margin-bottom:10px;'); $list->style = EHelper::escapeHtmlStyleAttr($list->style); } return $DOM->__toString(); } public function styleHeadings($html) { $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($html); $headings = $DOM->query('h1, h2, h3, h4'); if (!$headings->count()) return $html; foreach ($headings as $heading) { $heading->style = StylesHelper::applyTextAlignment($heading->style); $heading->style = StylesHelper::joinStyles($heading->style, 'padding:0;font-style:normal;font-weight:normal;'); $heading->style = EHelper::escapeHtmlStyleAttr($heading->style); } return $DOM->__toString(); } public function removeLastLineBreak($html) { return preg_replace('/(^)?(]*?\/?>)+$/i', '', $html); } public function insertLineBreak($element) { $element->parent->insertChild( [ 'tag_name' => 'br', 'self_close' => true, 'attributes' => [], ], $element->index() + 1 ); return $element; } } Blocks/Header.php000064400000003657150732301320007671 0ustar00', $element['text']); $element['text'] = preg_replace('/(<\/?p.*?>)/i', '', $element['text']); $lineHeight = sprintf( '%spx', StylesHelper::$defaultLineHeight * (int)$element['styles']['text']['fontSize'] ); if (!is_string($element['text'])) { throw new \RuntimeException('$element[\'text\'] should be a string.'); } $dOMParser = new pQuery(); $DOM = $dOMParser->parseStr($element['text']); if (isset($element['styles']['link'])) { $links = $DOM->query('a'); if ($links->count()) { $css = new CSS(); foreach ($links as $link) { $elementLinkStyles = StylesHelper::getStyles($element['styles'], 'link'); $link->style = $css->mergeInlineStyles($elementLinkStyles, $link->style); } } } $backgroundColor = $element['styles']['block']['backgroundColor']; $backgroundColor = ($backgroundColor !== 'transparent') ? 'bgcolor="' . $backgroundColor . '"' : false; if (!$backgroundColor) unset($element['styles']['block']['backgroundColor']); $style = 'line-height: ' . $lineHeight . ';' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text'); $style = EHelper::escapeHtmlStyleAttr($style); $template = ' ' . str_replace('&', '&', $DOM->html()) . ' '; return $template; } } Blocks/Image.php000064400000006012150732301320007507 0ustar00getOption('siteurl') . $element['src']; } $element['width'] = str_replace('px', '', $element['width']); $element['height'] = str_replace('px', '', $element['height']); $originalWidth = 0; if (is_numeric($element['width']) && is_numeric($element['height'])) { $element['width'] = (int)$element['width']; $element['height'] = (int)$element['height']; $originalWidth = $element['width']; $element = $this->adjustImageDimensions($element, $columnBaseWidth); } // If image was downsized because of column width set width to aways fill full column (e.g. on mobile) $style = ''; if ($element['fullWidth'] === true && $originalWidth > $element['width']) { $style = 'style="width:100%"'; } $imageTemplate = ' ' . EHelper::escapeHtmlAttr($element['alt']) . ' '; if (!empty($element['link'])) { $imageTemplate = '' . trim($imageTemplate) . ''; } $align = 'center'; if (!empty($element['styles']['block']['textAlign']) && in_array($element['styles']['block']['textAlign'], ['left', 'right'])) { $align = $element['styles']['block']['textAlign']; } $template = ' ' . trim($imageTemplate) . ' '; return $template; } public function adjustImageDimensions($element, $columnBaseWidth) { $paddedWidth = StylesHelper::$paddingWidth * 2; // scale image to fit column width if ($element['width'] > $columnBaseWidth) { $ratio = $element['width'] / $columnBaseWidth; $element['width'] = $columnBaseWidth; $element['height'] = (int)ceil($element['height'] / $ratio); } // resize image if the image is padded and wider than padded column width if ( $element['fullWidth'] === false && $element['width'] > ($columnBaseWidth - $paddedWidth) ) { $ratio = $element['width'] / ($columnBaseWidth - $paddedWidth); $element['width'] = $columnBaseWidth - $paddedWidth; $element['height'] = (int)ceil($element['height'] / $ratio); } return $element; } } Blocks/Renderer.php000064400000010417150732301320010237 0ustar00ALC = $ALC; $this->button = $button; $this->divider = $divider; $this->footer = $footer; $this->header = $header; $this->image = $image; $this->social = $social; $this->spacer = $spacer; $this->text = $text; $this->placeholder = $placeholder; $this->coupon = $coupon; } public function render(NewsletterEntity $newsletter, $data) { if (is_null($data['blocks']) && isset($data['type'])) { return null; } $columnCount = count($data['blocks']); $columnsLayout = isset($data['columnLayout']) ? $data['columnLayout'] : null; $columnWidths = ColumnsHelper::columnWidth($columnCount, $columnsLayout); $columnContent = []; foreach ($data['blocks'] as $index => $columnBlocks) { $renderedBlockElement = $this->renderBlocksInColumn($newsletter, $columnBlocks, $columnWidths[$index]); $columnContent[] = $renderedBlockElement; } return $columnContent; } private function renderBlocksInColumn(NewsletterEntity $newsletter, $block, $columnBaseWidth) { $blockContent = ''; $_this = $this; array_map(function($block) use (&$blockContent, $columnBaseWidth, $newsletter, $_this) { $renderedBlockElement = $_this->createElementFromBlockType($newsletter, $block, $columnBaseWidth); if (isset($block['blocks'])) { $renderedBlockElement = $_this->renderBlocksInColumn($newsletter, $block, $columnBaseWidth); // nested vertical column container is rendered as an array if (is_array($renderedBlockElement)) { $renderedBlockElement = implode('', $renderedBlockElement); } } $blockContent .= $renderedBlockElement; }, $block['blocks']); return $blockContent; } public function createElementFromBlockType(NewsletterEntity $newsletter, $block, $columnBaseWidth) { if ($block['type'] === 'automatedLatestContent') { return $this->processAutomatedLatestContent($newsletter, $block, $columnBaseWidth); } $block = StylesHelper::applyTextAlignment($block); switch ($block['type']) { case 'button': return $this->button->render($block, $columnBaseWidth); case 'divider': return $this->divider->render($block); case 'footer': return $this->footer->render($block); case 'header': return $this->header->render($block); case 'image': return $this->image->render($block, $columnBaseWidth); case 'social': return $this->social->render($block); case 'spacer': return $this->spacer->render($block); case 'text': return $this->text->render($block); case 'placeholder': return $this->placeholder->render($block); case Coupon::TYPE: return $this->coupon->render($block, $columnBaseWidth); } return ""; } public function processAutomatedLatestContent(NewsletterEntity $newsletter, $args, $columnBaseWidth) { $transformedPosts = [ 'blocks' => $this->ALC->render($newsletter, $args), ]; $transformedPosts = StylesHelper::applyTextAlignment($transformedPosts); return $this->renderBlocksInColumn($newsletter, $transformedPosts, $columnBaseWidth); } } Blocks/Placeholder.php000064400000001244150732301320010711 0ustar00wp = $wp; } public function render($element): string { $placeholder = $element['placeholder']; $class = $element['class'] ?? ''; $style = $element['style'] ?? ''; return ' ' . $this->wp->escHtml($placeholder) . ' '; } } Blocks/Button.php000064400000006627150732301320007754 0ustar00calculateWidth($element, $columnBaseWidth); $styles = 'display:inline-block;-webkit-text-size-adjust:none;mso-hide:all;text-decoration:none;text-align:center;' . StylesHelper::getBlockStyles($element, $exclude = ['textAlign']); $styles = EHelper::escapeHtmlStyleAttr($styles); $template = '
    ' . EHelper::escapeHtmlText($element['text']) . '
    '; return $template; } public function calculateWidth($element, $columnBaseWidth) { $columnWidth = $columnBaseWidth - (StylesHelper::$paddingWidth * 2); $borderWidth = (int)$element['styles']['block']['borderWidth']; $buttonWidth = (int)$element['styles']['block']['width']; $buttonWidth = ($buttonWidth > $columnWidth) ? $columnWidth : $buttonWidth; $buttonWidth = $buttonWidth - (2 * $borderWidth) . 'px'; return $buttonWidth; } } Columns/index.php000064400000000006150732301320007774 0ustar00"; } $columnsCount = count($contentBlock['blocks']); if ($columnsCount === 1) { return $this->renderOneColumn($contentBlock, $columnsData[0]); } return $this->renderMultipleColumns($contentBlock, $columnsData); } private function renderOneColumn($contentBlock, $content) { $template = $this->getOneColumnTemplate( $contentBlock['styles']['block'], isset($contentBlock['image']) ? $contentBlock['image'] : null ); return $template['content_start'] . $content . $template['content_end']; } public function getOneColumnTemplate($styles, $image) { $backgroundCss = $this->getBackgroundCss($styles, $image); $template['content_start'] = ' getBgColorAttribute($styles, $image) . '>
    '; $template['content_end'] = '
    '; return $template; } private function renderMultipleColumns($contentBlock, $columnsData) { $columnsCount = count($contentBlock['blocks']); $columnsLayout = isset($contentBlock['columnLayout']) ? $contentBlock['columnLayout'] : null; $widths = ColumnsHelper::columnWidth($columnsCount, $columnsLayout); $class = ColumnsHelper::columnClass($columnsCount); $alignment = ColumnsHelper::columnAlignment($columnsCount); $index = 0; $result = $this->getMultipleColumnsContainerStart($class, $contentBlock['styles']['block'], isset($contentBlock['image']) ? $contentBlock['image'] : null); foreach ($columnsData as $content) { $result .= $this->getMultipleColumnsContentStart($widths[$index++], $alignment, $class); $result .= $content; $result .= $this->getMultipleColumnsContentEnd(); } $result .= $this->getMultipleColumnsContainerEnd(); return $result; } private function getMultipleColumnsContainerStart($class, $styles, $image) { return ' getBgColorAttribute($styles, $image) . '>
    '; } private function getMultipleColumnsContentEnd() { return '
    '; } private function getBackgroundCss($styles, $image) { if ($image !== null && $image['src'] !== null) { $backgroundColor = isset($styles['backgroundColor']) && $styles['backgroundColor'] !== 'transparent' ? $styles['backgroundColor'] : '#ffffff'; $repeat = $image['display'] === 'tile' ? 'repeat' : 'no-repeat'; $size = $image['display'] === 'scale' ? 'cover' : 'contain'; $style = sprintf( 'background: %s url(%s) %s center/%s;background-color: %s;background-image: url(%s);background-repeat: %s;background-position: center;background-size: %s;', $backgroundColor, $image['src'], $repeat, $size, $backgroundColor, $image['src'], $repeat, $size ); return EHelper::escapeHtmlStyleAttr($style); } else { if (!isset($styles['backgroundColor'])) return false; $backgroundColor = $styles['backgroundColor']; return ($backgroundColor !== 'transparent') ? EHelper::escapeHtmlStyleAttr(sprintf('background-color:%s!important;', $backgroundColor)) : false; } } private function getBgColorAttribute($styles, $image) { if ( ($image === null || $image['src'] === null) && isset($styles['backgroundColor']) && $styles['backgroundColor'] !== 'transparent' ) { return 'bgcolor="' . EHelper::escapeHtmlAttr($styles['backgroundColor']) . '"'; } return null; } } Columns/ColumnsHelper.php000064400000002131150732301320011446 0ustar00 [660], 2 => [330, 330], "1_2" => [220, 440], "2_1" => [440, 220], 3 => [220, 220, 220], ]; public static $columnsClass = [ 1 => 'cols-one', 2 => 'cols-two', 3 => 'cols-three', ]; public static $columnsAlignment = [ 1 => null, 2 => 'left', 3 => 'right', ]; /** @return int[] */ public static function columnWidth($columnsCount, $columnsLayout) { if (isset(self::$columnsWidth[$columnsLayout])) { return self::$columnsWidth[$columnsLayout]; } return self::$columnsWidth[$columnsCount]; } public static function columnClass($columnsCount) { return self::$columnsClass[$columnsCount]; } public static function columnClasses() { return self::$columnsClass; } public static function columnAlignment($columnsCount) { return self::$columnsAlignment[$columnsCount]; } } EscapeHelper.php000064400000002554150732301320007617 0ustar00bodyRenderer = $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, SendingQueueEntity $sendingQueue = null, $type = false) { return $this->_render($newsletter, $sendingQueue, $type); } public function renderAsPreview(NewsletterEntity $newsletter, $type = false, ?string $subject = null) { return $this->_render($newsletter, null, $type, true, $subject); } private function _render(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue = null, $type = false, $preview = false, $subject = null) { $language = $this->wp->getBloginfo('language'); $metaRobots = $preview ? '' : ''; $subject = $subject ?: $newsletter->getSubject(); $wpPostEntity = $newsletter->getWpPost(); $wpPost = $wpPostEntity ? $wpPostEntity->getWpPostInstance() : null; if ($this->featuresController->isSupported(FeaturesController::GUTENBERG_EMAIL_EDITOR) && $wpPost instanceof \WP_Post) { $renderedNewsletter = $this->guntenbergRenderer->render($wpPost, $subject, $newsletter->getPreheader(), $language, $metaRobots); } 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, $sendingQueue); $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 ($sendingQueue) { $this->sendingQueuesRepository->pause($sendingQueue); } } $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('', '', $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; } } StylesHelper.php000064400000016413150732301320007701 0ustar00 'background-color', 'fontColor' => 'color', 'fontFamily' => 'font-family', 'textDecoration' => 'text-decoration', 'textAlign' => 'text-align', 'fontSize' => 'font-size', 'fontWeight' => 'font-weight', 'borderWidth' => 'border-width', 'borderStyle' => 'border-style', 'borderColor' => 'border-color', 'borderRadius' => 'border-radius', 'lineHeight' => 'line-height', 'msoLineHeightAlt' => 'mso-line-height-alt', 'msoFontSize' => 'mso-ansi-font-size', ]; public static $font = [ 'Arial' => "Arial, 'Helvetica Neue', Helvetica, sans-serif", 'Comic Sans MS' => "'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif", 'Courier New' => "'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace", 'Georgia' => "Georgia, Times, 'Times New Roman', serif", 'Lucida' => "'Lucida Sans Unicode', 'Lucida Grande', sans-serif", 'Tahoma' => 'Tahoma, Verdana, Segoe, sans-serif', 'Times New Roman' => "'Times New Roman', Times, Baskerville, Georgia, serif", 'Trebuchet MS' => "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif", 'Verdana' => 'Verdana, Geneva, sans-serif', 'Arvo' => 'arvo, courier, georgia, serif', 'Lato' => "lato, 'helvetica neue', helvetica, arial, sans-serif", 'Lora' => "lora, georgia, 'times new roman', serif", 'Merriweather' => "merriweather, georgia, 'times new roman', serif", 'Merriweather Sans' => "'merriweather sans', 'helvetica neue', helvetica, arial, sans-serif", 'Noticia Text' => "'noticia text', georgia, 'times new roman', serif", 'Open Sans' => "'open sans', 'helvetica neue', helvetica, arial, sans-serif", 'Playfair Display' => "'playfair display', georgia, 'times new roman', serif", 'Roboto' => "roboto, 'helvetica neue', helvetica, arial, sans-serif", 'Source Sans Pro' => "'source sans pro', 'helvetica neue', helvetica, arial, sans-serif", 'Oswald' => "Oswald, 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif", 'Raleway' => "Raleway, 'Century Gothic', CenturyGothic, AppleGothic, sans-serif", 'Permanent Marker' => "'Permanent Marker', Tahoma, Verdana, Segoe, sans-serif", 'Pacifico' => "Pacifico, 'Arial Narrow', Arial, sans-serif", ]; public static $customFonts = [ 'Arvo', 'Lato', 'Lora', 'Merriweather', 'Merriweather Sans', 'Noticia Text', 'Open Sans', 'Playfair Display', 'Roboto', 'Source Sans Pro', 'Oswald', 'Raleway', 'Permanent Marker', 'Pacifico', ]; public static $defaultLineHeight = 1.6; public static $headingMarginMultiplier = 0.3; public static $paddingWidth = 20; public static function getBlockStyles($element, $ignoreSpecificStyles = false) { if (!isset($element['styles']['block'])) { return; } return self::getStyles($element['styles'], 'block', $ignoreSpecificStyles); } public static function getStyles($data, $type, $ignoreSpecificStyles = false) { $styles = array_map(function($attribute, $style) use ($ignoreSpecificStyles) { if (!$ignoreSpecificStyles || !in_array($attribute, $ignoreSpecificStyles)) { $style = StylesHelper::applyFontFamily($attribute, $style); return StylesHelper::translateCSSAttribute($attribute) . ': ' . $style . ';'; } }, array_keys($data[$type]), $data[$type]); return implode('', $styles); } public static function translateCSSAttribute($attribute) { return (array_key_exists($attribute, self::$cssAttributes)) ? self::$cssAttributes[$attribute] : $attribute; } public static function setStyle(array $style, string $selector): string { $css = $selector . '{' . PHP_EOL; $style = self::applyHeadingMargin($style, $selector); $style = self::applyLineHeight($style, $selector); foreach ($style as $attribute => $individualStyle) { $individualStyle = self::applyFontFamily($attribute, $individualStyle); $css .= self::translateCSSAttribute($attribute) . ':' . $individualStyle . ';' . PHP_EOL; } $css .= '}' . PHP_EOL; return $css; } public static function applyTextAlignment($block) { if (is_array($block)) { $textAlignment = isset($block['styles']['block']['textAlign']) ? strtolower($block['styles']['block']['textAlign']) : ''; if (preg_match('/center|right|justify/i', (string)$textAlignment)) { return $block; } $block['styles']['block']['textAlign'] = 'left'; return $block; } return (preg_match('/text-align.*?[center|justify|right]/i', (string)$block)) ? $block : $block . 'text-align:left;'; } /** * Join styles and makes sure they are separated by ; */ public static function joinStyles(?string $styles1, ?string $styles2): string { if ($styles1 === null) $styles1 = ''; if ($styles2 === null) $styles2 = ''; $style = trim($styles1); if ( (strlen($style) > 0) && (substr($style, -1) !== ';') ) $style .= ';'; $style .= $styles2; return $style; } public static function applyFontFamily($attribute, $style) { if ($attribute !== 'fontFamily') return $style; return (isset(self::$font[$style])) ? self::$font[$style] : self::$font['Arial']; } public static function applyHeadingMargin(array $style, string $selector): array { if (!preg_match('/h[1-4]/i', $selector)) return $style; $fontSize = (int)$style['fontSize']; $style['margin'] = sprintf('0 0 %spx 0', self::$headingMarginMultiplier * $fontSize); return $style; } public static function applyLineHeight(array $style, string $selector): array { if (!preg_match('/mailpoet_paragraph|h[1-4]/i', $selector)) return $style; $lineHeight = isset($style['lineHeight']) ? (float)$style['lineHeight'] : self::$defaultLineHeight; $fontSize = (int)$style['fontSize']; $msoLineHeight = round($lineHeight * $fontSize); if ($msoLineHeight % 2 === 1) { $msoLineHeight++; } $msoFontSize = $fontSize; if ($msoFontSize % 2 === 1) { $msoFontSize++; } $style['msoLineHeightAlt'] = sprintf('%spx', $msoLineHeight); $style = ['msoFontSize' => sprintf('%spx', $msoFontSize)] + $style; $style['lineHeight'] = sprintf('%spx', $lineHeight * $fontSize); return $style; } private static function getCustomFontsNames($styles) { $fontNames = []; foreach ($styles as $style) { if (isset($style['fontFamily']) && in_array($style['fontFamily'], self::$customFonts)) { $fontNames[$style['fontFamily']] = true; } } return array_keys($fontNames); } public static function getCustomFontsLinks($styles) { $links = []; foreach (self::getCustomFontsNames($styles) as $name) { $links[] = urlencode($name) . ':400,400i,700,700i'; } if (!count($links)) { return ''; } // see https://stackoverflow.com/a/48214207 return ''; } } PostProcess/index.php000064400000000006150732301320010640 0ustar00parseStr($template); $template = $DOM->query('body'); // url is a temporary data tag that will be further replaced with // the proper track API URL during sending $url = Links::DATA_TAG_OPEN; $openTrackingImage = sprintf( '', $url ); $template->html($template->html() . $openTrackingImage); return $DOM->__toString(); } public static function addTrackingImage() { WPFunctions::get()->addFilter(Renderer::FILTER_POST_PROCESS, function ($template) { return OpenTracking::process($template); }); return true; } }