init
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\AbandonedCart;
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce as WooCommerceEmail;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
|
||||
class AbandonedCartContent {
|
||||
/** @var AutomatedLatestContentBlock */
|
||||
private $ALCBlock;
|
||||
|
||||
public function __construct(
|
||||
AutomatedLatestContentBlock $ALCBlock
|
||||
) {
|
||||
$this->ALCBlock = $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
|
||||
|| $groupOption->getValue() !== WooCommerceEmail::SLUG
|
||||
|| !$eventOption
|
||||
|| $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);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterPostEntity;
|
||||
use MailPoet\Newsletter\AutomatedLatestContent;
|
||||
use MailPoet\Newsletter\BlockPostQuery;
|
||||
use MailPoet\Newsletter\NewsletterPostsRepository;
|
||||
|
||||
class AutomatedLatestContentBlock {
|
||||
/**
|
||||
* Cache for rendered posts in newsletter.
|
||||
* Used to prevent duplicate post in case a newsletter contains 2 ALC blocks
|
||||
* @var array
|
||||
*/
|
||||
public $renderedPostsInNewsletter;
|
||||
|
||||
/** @var AutomatedLatestContent */
|
||||
private $ALC;
|
||||
|
||||
/** @var NewsletterPostsRepository */
|
||||
private $newsletterPostsRepository;
|
||||
|
||||
public function __construct(
|
||||
NewsletterPostsRepository $newsletterPostsRepository,
|
||||
AutomatedLatestContent $ALC
|
||||
) {
|
||||
$this->renderedPostsInNewsletter = [];
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Button {
|
||||
public function render($element, $columnBaseWidth) {
|
||||
$originalWidth = $this->getOriginalWidth($element, $columnBaseWidth);
|
||||
$element['styles']['block']['width'] = $this->calculateWidth($element, $columnBaseWidth);
|
||||
$styles = 'display:block;text-decoration:none;text-align:center;' . StylesHelper::getBlockStyles($element, $exclude = ['textAlign']);
|
||||
$styles = EHelper::escapeHtmlStyleAttr($styles);
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_padded_vertical mailpoet_padded_side" valign="top">
|
||||
<div>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;">
|
||||
<tr>
|
||||
<td class="mailpoet_button-container" style="text-align:' . $element['styles']['block']['textAlign'] . ';"><!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
|
||||
href="' . EHelper::escapeHtmlLinkAttr($element['url']) . '"
|
||||
style="height:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['lineHeight']) . ';
|
||||
width:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['width']) . ';
|
||||
v-text-anchor:middle;"
|
||||
arcsize="' . round((int)$element['styles']['block']['borderRadius'] / ((int)$element['styles']['block']['lineHeight'] ?: 1) * 100) . '%"
|
||||
strokeweight="' . EHelper::escapeHtmlAttr($element['styles']['block']['borderWidth']) . '"
|
||||
strokecolor="' . EHelper::escapeHtmlAttr($element['styles']['block']['borderColor']) . '"
|
||||
fillcolor="' . EHelper::escapeHtmlAttr($element['styles']['block']['backgroundColor']) . '">
|
||||
<w:anchorlock/>
|
||||
<center style="color:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontColor']) . ';
|
||||
font-family:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontFamily']) . ';
|
||||
font-size:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontSize']) . ';
|
||||
font-weight:bold;">' . EHelper::escapeHtmlText($element['text']) . '
|
||||
</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-- -->
|
||||
<table
|
||||
border="0"
|
||||
cellspacing="0"
|
||||
cellpadding="0"
|
||||
role="presentation"
|
||||
style="display:inline-block;border-collapse:separate;mso-table-lspace:0;mso-table-rspace:0;width:' . EHelper::escapeHtmlStyleAttr($originalWidth) . '"
|
||||
width="' . EHelper::escapeHtmlStyleAttr($originalWidth) . '"
|
||||
>
|
||||
<tr>
|
||||
<td class="mailpoet_table_button"
|
||||
valign="middle"
|
||||
role="presentation"
|
||||
style="mso-table-lspace: 0;mso-table-rspace: 0;' . $styles . '"
|
||||
>
|
||||
<a class="mailpoet_button" style="
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
line-height: ' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['lineHeight']) . ';
|
||||
color: ' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontColor']) . ';
|
||||
" href="' . EHelper::escapeHtmlLinkAttr($element['url']) . '" target="_blank">' . EHelper::escapeHtmlText($element['text']) . '</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
|
||||
public function getOriginalWidth($element, $columnBaseWidth): string {
|
||||
$columnWidth = $columnBaseWidth - (StylesHelper::$paddingWidth * 2);
|
||||
$originalWidth = (int)$element['styles']['block']['width'];
|
||||
$originalWidth = ($originalWidth > $columnWidth) ?
|
||||
$columnWidth :
|
||||
$originalWidth;
|
||||
|
||||
return $originalWidth . 'px';
|
||||
}
|
||||
|
||||
public function calculateWidth($element, $columnBaseWidth): string {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\NewsletterProcessingException;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
|
||||
class Coupon {
|
||||
const TYPE = 'coupon';
|
||||
|
||||
const CODE_PLACEHOLDER = 'XXXX-XXXXXXX-XXXX';
|
||||
|
||||
/*** @var Helper */
|
||||
private $helper;
|
||||
|
||||
public function __construct(
|
||||
Helper $helper
|
||||
) {
|
||||
$this->helper = $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 = '
|
||||
<tr>
|
||||
<td class="mailpoet_padded_vertical mailpoet_padded_side" valign="top">
|
||||
<div>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;">
|
||||
<tr>
|
||||
<td class="mailpoet_coupon-container" style="text-align:' . $element['styles']['block']['textAlign'] . ';"><!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
|
||||
style="height:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['lineHeight']) . ';
|
||||
width:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['width']) . ';
|
||||
v-text-anchor:middle;"
|
||||
arcsize="' . round((int)$element['styles']['block']['borderRadius'] / ((int)$element['styles']['block']['lineHeight'] ?: 1) * 100) . '%"
|
||||
strokeweight="' . EHelper::escapeHtmlAttr($element['styles']['block']['borderWidth']) . '"
|
||||
strokecolor="' . EHelper::escapeHtmlAttr($element['styles']['block']['borderColor']) . '"
|
||||
fillcolor="' . EHelper::escapeHtmlAttr($element['styles']['block']['backgroundColor']) . '">
|
||||
<w:anchorlock/>
|
||||
<center style="color:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontColor']) . ';
|
||||
font-family:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontFamily']) . ';
|
||||
font-size:' . EHelper::escapeHtmlStyleAttr($element['styles']['block']['fontSize']) . ';
|
||||
font-weight:bold;">' . EHelper::escapeHtmlText($couponCode) . '
|
||||
</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-- -->
|
||||
<div class="mailpoet_coupon" style="' . $styles . '">' . EHelper::escapeHtmlText($couponCode) . '</div>
|
||||
<!--<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>';
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Divider {
|
||||
public function render($element) {
|
||||
$backgroundColor = $element['styles']['block']['backgroundColor'];
|
||||
$dividerCellStyle = "border-top-width: {$element['styles']['block']['borderWidth']};";
|
||||
$dividerCellStyle .= "border-top-style: {$element['styles']['block']['borderStyle']};";
|
||||
$dividerCellStyle .= "border-top-color: {$element['styles']['block']['borderColor']};";
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_divider" valign="top" ' .
|
||||
(($element['styles']['block']['backgroundColor'] !== 'transparent') ?
|
||||
'bgColor="' . EHelper::escapeHtmlAttr($backgroundColor) . '" style="background-color:' . EHelper::escapeHtmlStyleAttr($backgroundColor) . ';' :
|
||||
'style="'
|
||||
) .
|
||||
sprintf(
|
||||
'padding: %s %spx %s %spx;',
|
||||
EHelper::escapeHtmlStyleAttr($element['styles']['block']['padding']),
|
||||
StylesHelper::$paddingWidth,
|
||||
EHelper::escapeHtmlStyleAttr($element['styles']['block']['padding']),
|
||||
StylesHelper::$paddingWidth
|
||||
) . '">
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0"
|
||||
style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;">
|
||||
<tr>
|
||||
<td class="mailpoet_divider-cell" style="' . EHelper::escapeHtmlStyleAttr($dividerCellStyle) . '">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\Util\pQuery\pQuery;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\CSS;
|
||||
|
||||
class Footer {
|
||||
private NewsletterHtmlSanitizer $htmlSanitizer;
|
||||
private WPFunctions $wp;
|
||||
|
||||
public function __construct(
|
||||
NewsletterHtmlSanitizer $htmlSanitizer,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->htmlSanitizer = $htmlSanitizer;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function render($element) {
|
||||
$element['text'] = preg_replace('/\n/', '<br />', $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="' . $this->wp->escAttr($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 = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_footer" ' . $backgroundColor . ' style="' . $style . '">
|
||||
' . $this->htmlSanitizer->sanitize($DOM->__toString()) . '
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\Util\pQuery\pQuery;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\CSS;
|
||||
|
||||
class Header {
|
||||
private NewsletterHtmlSanitizer $htmlSanitizer;
|
||||
private WPFunctions $wp;
|
||||
|
||||
public function __construct(
|
||||
NewsletterHtmlSanitizer $htmlSanitizer,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->htmlSanitizer = $htmlSanitizer;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function render($element) {
|
||||
$element['text'] = preg_replace('/\n/', '<br />', $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="' . $this->wp->escAttr($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 = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_header" ' . $backgroundColor . ' style="' . $style . '">
|
||||
' . $this->htmlSanitizer->sanitize($DOM->__toString()) . '
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Image {
|
||||
public function render($element, $columnBaseWidth) {
|
||||
if (empty($element['src'])) {
|
||||
return '';
|
||||
}
|
||||
if (substr($element['src'], 0, 1) == '/' && substr($element['src'], 1, 1) != '/') {
|
||||
$element['src'] = WPFunctions::get()->getOption('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 = '
|
||||
<img src="' . EHelper::escapeHtmlLinkAttr($element['src']) . '" width="' . EHelper::escapeHtmlAttr($element['width']) . '" alt="' . EHelper::escapeHtmlAttr($element['alt']) . '"' . $style . '/>
|
||||
';
|
||||
if (!empty($element['link'])) {
|
||||
$imageTemplate = '<a href="' . EHelper::escapeHtmlLinkAttr($element['link']) . '">' . trim($imageTemplate) . '</a>';
|
||||
}
|
||||
$align = 'center';
|
||||
if (!empty($element['styles']['block']['textAlign']) && in_array($element['styles']['block']['textAlign'], ['left', 'right'])) {
|
||||
$align = $element['styles']['block']['textAlign'];
|
||||
}
|
||||
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_image ' . (($element['fullWidth'] === false) ? 'mailpoet_padded_vertical mailpoet_padded_side' : '') . '" align="' . EHelper::escapeHtmlAttr($align) . '" valign="top">
|
||||
' . trim($imageTemplate) . '
|
||||
</td>
|
||||
</tr>';
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Placeholder {
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function render($element): string {
|
||||
$placeholder = $element['placeholder'];
|
||||
$class = $element['class'] ?? '';
|
||||
$style = $element['style'] ?? '';
|
||||
return '
|
||||
<tr>
|
||||
<td class="' . $this->wp->escAttr($class) . '" style="' . $this->wp->escAttr($style) . '">
|
||||
' . $this->wp->escHtml($placeholder) . '
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\Renderer\Columns\ColumnsHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Renderer {
|
||||
/** @var AutomatedLatestContentBlock */
|
||||
private $ALC;
|
||||
|
||||
/** @var Button */
|
||||
private $button;
|
||||
|
||||
/** @var Divider */
|
||||
private $divider;
|
||||
|
||||
/** @var Footer */
|
||||
private $footer;
|
||||
|
||||
/** @var Header */
|
||||
private $header;
|
||||
|
||||
/** @var Image */
|
||||
private $image;
|
||||
|
||||
/** @var Social */
|
||||
private $social;
|
||||
|
||||
/** @var Spacer */
|
||||
private $spacer;
|
||||
|
||||
/** @var Text */
|
||||
private $text;
|
||||
|
||||
/** @var Placeholder */
|
||||
private $placeholder;
|
||||
|
||||
/** @var Coupon */
|
||||
private $coupon;
|
||||
|
||||
public function __construct(
|
||||
AutomatedLatestContentBlock $ALC,
|
||||
Button $button,
|
||||
Divider $divider,
|
||||
Footer $footer,
|
||||
Header $header,
|
||||
Image $image,
|
||||
Social $social,
|
||||
Spacer $spacer,
|
||||
Text $text,
|
||||
Placeholder $placeholder,
|
||||
Coupon $coupon
|
||||
) {
|
||||
$this->ALC = $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 "<!-- Skipped unsupported block type: {$block['type']} -->";
|
||||
}
|
||||
|
||||
public function processAutomatedLatestContent(NewsletterEntity $newsletter, $args, $columnBaseWidth) {
|
||||
$transformedPosts = [
|
||||
'blocks' => $this->ALC->render($newsletter, $args),
|
||||
];
|
||||
$transformedPosts = StylesHelper::applyTextAlignment($transformedPosts);
|
||||
return $this->renderBlocksInColumn($newsletter, $transformedPosts, $columnBaseWidth);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
|
||||
class Social {
|
||||
public function render($element) {
|
||||
$iconsBlock = '';
|
||||
if (is_array($element['icons'])) {
|
||||
foreach ($element['icons'] as $index => $icon) {
|
||||
if (empty($icon['image'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$style = 'width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;';
|
||||
$iconsBlock .= '<a href="' . EHelper::escapeHtmlLinkAttr($icon['link']) . '" style="text-decoration:none!important;"
|
||||
><img
|
||||
src="' . EHelper::escapeHtmlLinkAttr($icon['image']) . '"
|
||||
width="' . (int)$icon['width'] . '"
|
||||
height="' . (int)$icon['height'] . '"
|
||||
style="' . EHelper::escapeHtmlStyleAttr($style) . '"
|
||||
alt="' . EHelper::escapeHtmlAttr($icon['iconType']) . '"
|
||||
></a> ';
|
||||
}
|
||||
}
|
||||
$alignment = isset($element['styles']['block']['textAlign']) ? $element['styles']['block']['textAlign'] : 'center';
|
||||
if (!empty($iconsBlock)) {
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_padded_side mailpoet_padded_vertical" valign="top" align="' . EHelper::escapeHtmlAttr($alignment) . '">
|
||||
' . $iconsBlock . '
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
|
||||
class Spacer {
|
||||
public function render($element) {
|
||||
$height = (int)$element['styles']['block']['height'];
|
||||
$backgroundColor = EHelper::escapeHtmlAttr($element['styles']['block']['backgroundColor']);
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_spacer" ' .
|
||||
(($backgroundColor !== 'transparent') ? 'bgcolor="' . $backgroundColor . '" ' : '') .
|
||||
'height="' . $height . '" valign="top"></td>
|
||||
</tr>';
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Editor\PostContentManager;
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\Util\pQuery\pQuery;
|
||||
|
||||
class Text {
|
||||
public function render($element) {
|
||||
$html = $element['text'];
|
||||
// replace with spaces
|
||||
$html = str_replace(' ', ' ', $html);
|
||||
$html = str_replace('\xc2\xa0', ' ', $html);
|
||||
$html = $this->convertBlockquotesToTables($html);
|
||||
$html = $this->convertParagraphsToTables($html);
|
||||
$html = $this->styleLists($html);
|
||||
$html = $this->styleHeadings($html);
|
||||
$html = $this->removeLastLineBreak($html);
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_text mailpoet_padded_vertical mailpoet_padded_side" valign="top" style="word-break:break-word;word-wrap:break-word;">
|
||||
' . $html . '
|
||||
</td>
|
||||
</tr>';
|
||||
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[] = '<br />';
|
||||
$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('
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="2" bgcolor="#565656"></td>
|
||||
<td width="10"></td>
|
||||
<td valign="top">
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
|
||||
<tr>
|
||||
<td class="mailpoet_blockquote">
|
||||
' . implode('', $contents) . '
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>');
|
||||
$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())) ?
|
||||
'<br /><br />' :
|
||||
'';
|
||||
// if this element is followed by a list, add single line break
|
||||
$lineBreaks = ($nextElement && preg_match('/<li/i', $nextElement->getOuterText())) ?
|
||||
'<br />' :
|
||||
$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())) ?
|
||||
'<br /><br />' :
|
||||
$lineBreaks;
|
||||
}
|
||||
$paragraph->html('
|
||||
<tr>
|
||||
<td class="mailpoet_paragraph" style="word-break:break-word;word-wrap:break-word;' . EHelper::escapeHtmlStyleAttr($style) . '">
|
||||
' . $contents . $lineBreaks . '
|
||||
</td>
|
||||
</tr>');
|
||||
}
|
||||
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('/(^)?(<br[^>]*?\/?>)+$/i', '', $html);
|
||||
}
|
||||
|
||||
public function insertLineBreak($element) {
|
||||
$element->parent->insertChild(
|
||||
[
|
||||
'tag_name' => 'br',
|
||||
'self_close' => true,
|
||||
'attributes' => [],
|
||||
],
|
||||
$element->index() + 1
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user