Compare commits
5 Commits
update-plu
...
5.5.2
Author | SHA1 | Date | |
---|---|---|---|
40e7266890 | |||
8ec779a9e0 | |||
e77b5bf309 | |||
f704b497e0 | |||
9dd35849a0 |
@ -1,5 +1,9 @@
|
||||
== Changelog ==
|
||||
|
||||
= 5.5.2 - 2024-12-24 =
|
||||
|
||||
- Improved: minor changes and fixes.
|
||||
|
||||
= 5.5.1 - 2024-12-17 =
|
||||
|
||||
- Improved: added option to protect WordPress and WooCommerce registration forms with a captcha.
|
||||
|
@ -2,12 +2,25 @@
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
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']);
|
||||
@ -32,7 +45,7 @@ class Footer {
|
||||
}
|
||||
$backgroundColor = $element['styles']['block']['backgroundColor'];
|
||||
$backgroundColor = ($backgroundColor !== 'transparent') ?
|
||||
'bgcolor="' . $backgroundColor . '"' :
|
||||
'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');
|
||||
@ -40,7 +53,7 @@ class Footer {
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_footer" ' . $backgroundColor . ' style="' . $style . '">
|
||||
' . str_replace('&', '&', $DOM->html()) . '
|
||||
' . $this->htmlSanitizer->sanitize($DOM->__toString()) . '
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
|
@ -2,12 +2,25 @@
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
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']);
|
||||
@ -32,7 +45,7 @@ class Header {
|
||||
}
|
||||
$backgroundColor = $element['styles']['block']['backgroundColor'];
|
||||
$backgroundColor = ($backgroundColor !== 'transparent') ?
|
||||
'bgcolor="' . $backgroundColor . '"' :
|
||||
'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');
|
||||
@ -40,7 +53,7 @@ class Header {
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_header" ' . $backgroundColor . ' style="' . $style . '">
|
||||
' . str_replace('&', '&', $DOM->html()) . '
|
||||
' . $this->htmlSanitizer->sanitize($DOM->__toString()) . '
|
||||
</td>
|
||||
</tr>';
|
||||
return $template;
|
||||
|
@ -11,7 +11,7 @@ class OpenTracking {
|
||||
public static function process($template) {
|
||||
$DOM = new pQuery();
|
||||
$DOM = $DOM->parseStr($template);
|
||||
$template = $DOM->query('body');
|
||||
$template = $DOM->select('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;
|
||||
@ -19,7 +19,7 @@ class OpenTracking {
|
||||
'<img alt="" class="" src="%s"/>',
|
||||
$url
|
||||
);
|
||||
$template->html($template->html() . $openTrackingImage);
|
||||
self::appendToDomNodes($template, $openTrackingImage);
|
||||
return $DOM->__toString();
|
||||
}
|
||||
|
||||
@ -29,4 +29,16 @@ class OpenTracking {
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function appendToDomNodes($template, $openTrackingImage): void {
|
||||
// Preserve backward compatibility with pQuery::html()
|
||||
// by processing an array of DomNodes
|
||||
if (!empty($template)) {
|
||||
$template = is_array($template) ? $template : [$template];
|
||||
array_map(
|
||||
fn($item) => $item->html($item->toString(true, true, 1) . $openTrackingImage),
|
||||
$template,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Plugin Name: MailPoet
|
||||
* Version: 5.5.1
|
||||
* Version: 5.5.2
|
||||
* Plugin URI: https://www.mailpoet.com
|
||||
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
|
||||
* Author: MailPoet
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
$mailpoetPlugin = [
|
||||
'version' => '5.5.1',
|
||||
'version' => '5.5.2',
|
||||
'filename' => __FILE__,
|
||||
'path' => dirname(__FILE__),
|
||||
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',
|
||||
|
@ -3,7 +3,7 @@ Contributors: mailpoet, woocommerce, automattic
|
||||
Tags: email marketing, post notification, woocommerce emails, email automation, newsletter
|
||||
Requires at least: 6.6
|
||||
Tested up to: 6.7
|
||||
Stable tag: 5.5.1
|
||||
Stable tag: 5.5.2
|
||||
Requires PHP: 7.4
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
@ -228,9 +228,7 @@ Check our [Knowledge Base](https://kb.mailpoet.com) or contact us through our [s
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 5.5.1 - 2024-12-17 =
|
||||
* Improved: added option to protect WordPress and WooCommerce registration forms with a captcha.
|
||||
* Changed: removed 3rd party Beamer integration;
|
||||
* Fixed: error when trying to use mailpoet_settings database table before it is available.
|
||||
= 5.5.2 - 2024-12-24 =
|
||||
* Improved: minor changes and fixes.
|
||||
|
||||
[See the changelog for all versions.](https://github.com/mailpoet/mailpoet/blob/trunk/mailpoet/CHANGELOG.md)
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class FooterAndHeaderTest extends \MailPoetTest {
|
||||
private Footer $footerRenderer;
|
||||
private Header $headerRenderer;
|
||||
|
||||
private $block = [
|
||||
'text' => '<p>Hello</p>',
|
||||
'styles' => [
|
||||
'text' => [
|
||||
'fontSize' => 16,
|
||||
],
|
||||
'block' => [
|
||||
'backgroundColor' => '#ffffff',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
$this->footerRenderer = new Footer(
|
||||
$this->diContainer->get(NewsletterHtmlSanitizer::class),
|
||||
$this->diContainer->get(WPFunctions::class)
|
||||
);
|
||||
$this->headerRenderer = new Header(
|
||||
$this->diContainer->get(NewsletterHtmlSanitizer::class),
|
||||
$this->diContainer->get(WPFunctions::class)
|
||||
);
|
||||
}
|
||||
|
||||
public function testHeaderAndFooterSanitizeText(): void {
|
||||
$this->checkItSanitizesText($this->footerRenderer);
|
||||
$this->checkItSanitizesText($this->headerRenderer);
|
||||
}
|
||||
|
||||
public function testHeaderAndFooterSanitizeStyles(): void {
|
||||
$this->checkItSanitizesStyles($this->footerRenderer);
|
||||
$this->checkItSanitizesStyles($this->headerRenderer);
|
||||
}
|
||||
|
||||
public function checkItSanitizesText($renderer): void {
|
||||
$block = $this->block;
|
||||
// It removes tags that are not allowed
|
||||
$block['text'] = '<p><img src=x onerror="alert(1)"><script>alert(2);</script></p>';
|
||||
$result = $renderer->render($block);
|
||||
$this->assertStringNotContainsString('<img', $result);
|
||||
$this->assertStringNotContainsString('alert(1)', $result);
|
||||
$this->assertStringNotContainsString('<script>', $result);
|
||||
// Html entities should remain encoded
|
||||
$block['text'] = '<p><script>alert(1)</script></p>';
|
||||
$result = $renderer->render($block);
|
||||
$this->assertStringContainsString('<script>alert(1)</script>', $result);
|
||||
}
|
||||
|
||||
private function checkItSanitizesStyles($renderer): void {
|
||||
$block = $this->block;
|
||||
$block['styles']['block']['backgroundColor'] = '"> <script>alert(1);</script>';
|
||||
$result = $renderer->render($block);
|
||||
$this->assertStringNotContainsString('<script>', $result);
|
||||
$this->assertStringContainsString('<script>', $result);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
use MailPoet\Newsletter\Links\Links;
|
||||
use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking;
|
||||
|
||||
class OpenTrackingTest extends \MailPoetTest {
|
||||
public function testItAddsTrackingImage(): void {
|
||||
$template = '<html><body><p>text</p></body></html>';
|
||||
$result = OpenTracking::process($template);
|
||||
$this->assertStringContainsString('<img', $result);
|
||||
$this->assertStringContainsString('src="' . Links::DATA_TAG_OPEN . '"', $result);
|
||||
}
|
||||
|
||||
public function testItReturnsOriginalInputIfBodyTagIsMissing(): void {
|
||||
$template = '<html><p>text</p></html>';
|
||||
$result = OpenTracking::process($template);
|
||||
$this->assertEquals($template, $result);
|
||||
}
|
||||
|
||||
public function testItPreservesHTMLEntities(): void {
|
||||
$template = '<html><body><p>text</p><img src="x" onerror="alert(1)"></body></html>';
|
||||
$result = OpenTracking::process($template);
|
||||
$this->assertStringContainsString('<img src="x" onerror="alert(1)">', $result);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ use Codeception\Util\Fixtures;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Features\FeaturesController;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Renderer\Blocks\Button;
|
||||
use MailPoet\Newsletter\Renderer\Blocks\Divider;
|
||||
@ -268,7 +269,11 @@ class RendererTest extends \MailPoetTest {
|
||||
public function testItRendersHeader() {
|
||||
$newsletter = (array)$this->newsletter->getBody();
|
||||
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][0];
|
||||
$DOM = $this->dOMParser->parseStr((new Header)->render($template));
|
||||
$headerRenderer = new Header(
|
||||
$this->diContainer->get(NewsletterHtmlSanitizer::class),
|
||||
$this->diContainer->get(WPFunctions::class)
|
||||
);
|
||||
$DOM = $this->dOMParser->parseStr($headerRenderer->render($template));
|
||||
// element should be properly nested, and styles should be applied
|
||||
verify($DOM('tr > td.mailpoet_header', 0)->html())->notEmpty();
|
||||
verify($DOM('tr > td > a', 0)->html())->notEmpty();
|
||||
@ -582,7 +587,11 @@ class RendererTest extends \MailPoetTest {
|
||||
public function testItRendersFooter() {
|
||||
$newsletter = (array)$this->newsletter->getBody();
|
||||
$template = $newsletter['content']['blocks'][3]['blocks'][0]['blocks'][0];
|
||||
$DOM = $this->dOMParser->parseStr((new Footer)->render($template));
|
||||
$footerRenderer = new Footer(
|
||||
$this->diContainer->get(NewsletterHtmlSanitizer::class),
|
||||
$this->diContainer->get(WPFunctions::class)
|
||||
);
|
||||
$DOM = $this->dOMParser->parseStr($footerRenderer->render($template));
|
||||
// element should be properly nested, and styles should be applied
|
||||
verify($DOM('tr > td.mailpoet_footer', 0)->html())->notEmpty();
|
||||
verify($DOM('tr > td > a', 0)->html())->notEmpty();
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
class FooterTest extends \MailPoetUnitTest {
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class FooterTest extends \MailPoetUnitTest {
|
||||
private Footer $renderer;
|
||||
private $block = [
|
||||
'type' => 'footer',
|
||||
'text' => '<p>Footer text. <a href="http://example.com">link</a></p>',
|
||||
@ -24,8 +27,21 @@ class FooterTest extends \MailPoetUnitTest {
|
||||
],
|
||||
];
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
$wpMock = $this->createMock(WPFunctions::class);
|
||||
$wpMock->method('escAttr')->willReturnCallback(function($attr) {
|
||||
return $attr;
|
||||
});
|
||||
$sanitizerMock = $this->createMock(NewsletterHtmlSanitizer::class);
|
||||
$sanitizerMock->method('sanitize')->willReturnCallback(function($html) {
|
||||
return $html;
|
||||
});
|
||||
$this->renderer = new Footer($sanitizerMock, $wpMock);
|
||||
}
|
||||
|
||||
public function testItRendersCorrectly() {
|
||||
$output = (new Footer)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
$expectedResult = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_footer" style="line-height: 19.2px;color: #222222;font-family: roboto, \'helvetica neue\', helvetica, arial, sans-serif;font-size: 12px;text-align: center;">
|
||||
@ -37,7 +53,7 @@ class FooterTest extends \MailPoetUnitTest {
|
||||
|
||||
public function testItRendersWithBackgroundColor() {
|
||||
$this->block['styles']['block']['backgroundColor'] = '#f0f0f0';
|
||||
$output = (new Footer)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
$expectedResult = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_footer" bgcolor="#f0f0f0" style="line-height: 19.2px;background-color: #f0f0f0;color: #222222;font-family: roboto, \'helvetica neue\', helvetica, arial, sans-serif;font-size: 12px;text-align: center;">
|
||||
@ -49,13 +65,13 @@ class FooterTest extends \MailPoetUnitTest {
|
||||
|
||||
public function testItPrefersInlinedCssForLinks() {
|
||||
$this->block['text'] = '<p>Footer text. <a href="http://example.com" style="color:#aaaaaa;">link</a></p>';
|
||||
$output = (new Footer)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
verify($output)->stringContainsString('<a href="http://example.com" style="color:#aaaaaa;text-decoration:none">link</a>');
|
||||
}
|
||||
|
||||
public function testItRaisesExceptionIfTextIsNotString() {
|
||||
$this->block['text'] = ['some', 'array'];
|
||||
$this->expectException('RuntimeException');
|
||||
(new Footer)->render($this->block);
|
||||
$this->renderer->render($this->block);
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
class HeaderTest extends \MailPoetUnitTest {
|
||||
use MailPoet\Newsletter\NewsletterHtmlSanitizer;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class HeaderTest extends \MailPoetUnitTest {
|
||||
private Header $renderer;
|
||||
private $block = [
|
||||
'type' => 'header',
|
||||
'text' => '<a href="[link:newsletter_view_in_browser_url]">View this in your browser.</a>',
|
||||
@ -24,8 +27,21 @@ class HeaderTest extends \MailPoetUnitTest {
|
||||
],
|
||||
];
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
$wpMock = $this->createMock(WPFunctions::class);
|
||||
$wpMock->method('escAttr')->willReturnCallback(function($attr) {
|
||||
return $attr;
|
||||
});
|
||||
$sanitizerMock = $this->createMock(NewsletterHtmlSanitizer::class);
|
||||
$sanitizerMock->method('sanitize')->willReturnCallback(function($html) {
|
||||
return $html;
|
||||
});
|
||||
$this->renderer = new Header($sanitizerMock, $wpMock);
|
||||
}
|
||||
|
||||
public function testItRendersCorrectly() {
|
||||
$output = (new Header)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
$expectedResult = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_header" style="line-height: 19.2px;color: #222222;font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;font-size: 12px;text-align: left;">
|
||||
@ -37,7 +53,7 @@ class HeaderTest extends \MailPoetUnitTest {
|
||||
|
||||
public function testItRendersBackgroundColorCorrectly() {
|
||||
$this->block['styles']['block']['backgroundColor'] = '#f0f0f0';
|
||||
$output = (new Header)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
$expectedResult = '
|
||||
<tr>
|
||||
<td class="mailpoet_header_footer_padded mailpoet_header" bgcolor="#f0f0f0" style="line-height: 19.2px;background-color: #f0f0f0;color: #222222;font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;font-size: 12px;text-align: left;">
|
||||
@ -49,13 +65,13 @@ class HeaderTest extends \MailPoetUnitTest {
|
||||
|
||||
public function testItPrefersInlinedCssForLinks() {
|
||||
$this->block['text'] = '<p>Header text. <a href="http://example.com" style="color:#aaaaaa;">link</a></p>';
|
||||
$output = (new Footer)->render($this->block);
|
||||
$output = $this->renderer->render($this->block);
|
||||
verify($output)->stringContainsString('<a href="http://example.com" style="color:#aaaaaa;text-decoration:underline">link</a>');
|
||||
}
|
||||
|
||||
public function testItRaisesExceptionIfTextIsNotString() {
|
||||
$this->block['text'] = ['some', 'array'];
|
||||
$this->expectException('RuntimeException');
|
||||
(new Header)->render($this->block);
|
||||
$this->renderer->render($this->block);
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ $console = new \Codeception\Lib\Console\Output([]);
|
||||
abstract class MailPoetUnitTest extends \Codeception\TestCase\Test {
|
||||
protected $runTestInSeparateProcess = false;
|
||||
protected $preserveGlobalState = false;
|
||||
protected $tester = null;
|
||||
}
|
||||
|
||||
include '_fixtures.php';
|
||||
|
Reference in New Issue
Block a user