Add blocks preprocessor

Currently, we need to wrap top-level non-column blocks into a single
column. This is done in the preprocessor.

[MAILPOET-5540]
This commit is contained in:
Rostislav Wolny
2023-08-31 17:57:04 +02:00
committed by Jan Lysý
parent 69a87e8146
commit 968ff6754e
5 changed files with 128 additions and 5 deletions

View File

@@ -319,6 +319,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\BodyRenderer::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\BlocksRenderer::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\BlocksRegistry::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Preprocessor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Integrations\Core\Initializer::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Integrations\MailPoet\EmailEditor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Integrations\MailPoet\EmailApiController::class)->setPublic(true);

View File

@@ -13,10 +13,7 @@ class BodyRenderer {
$this->blocksRenderer = $blocksRenderer;
}
public function renderBody(string $postContent): string {
$parser = new \WP_Block_Parser();
$parsedBlocks = $parser->parse($postContent);
// @todo We need to wrap top level blocks which are not in columns into a column
public function renderBody(array $parsedBlocks): string {
return $this->blocksRenderer->render($parsedBlocks);
}
}

View File

@@ -0,0 +1,52 @@
<?php declare(strict_types = 1);
namespace MailPoet\EmailEditor\Engine\Renderer;
class Preprocessor {
const SINGLE_COLUMN_TEMPLATE = [
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [[
'blockName' => 'core/column',
'attrs' => [],
'innerBlocks' => [],
]],
];
public function preprocess(array $parsedBlocks): array {
return $this->addTopLevelColumns($parsedBlocks);
}
/**
* In the editor we allow putting content blocks directly into the root level of the email.
* But for rendering purposes it is more convenient to have them wrapped in a single column.
* This method walks through the first level of blocks and wraps non column blocks into a single column.
*/
private function addTopLevelColumns(array $parsedBlocks): array {
$wrappedParsedBlocks = [];
$nonColumnsBlocksBuffer = [];
foreach ($parsedBlocks as $block) {
// The next block is columns so we can flush the buffer and add the columns block
if ($block['blockName'] === 'core/columns') {
if ($nonColumnsBlocksBuffer) {
$columnsBlock = self::SINGLE_COLUMN_TEMPLATE;
$columnsBlock['innerBlocks'][0]['innerBlocks'] = $nonColumnsBlocksBuffer;
$nonColumnsBlocksBuffer = [];
$wrappedParsedBlocks[] = $columnsBlock;
}
$wrappedParsedBlocks[] = $block;
continue;
}
// Non columns block so we add it to the buffer
$nonColumnsBlocksBuffer[] = $block;
}
// Flush the buffer if there are any blocks left
if ($nonColumnsBlocksBuffer) {
$columnsBlock = self::SINGLE_COLUMN_TEMPLATE;
$columnsBlock['innerBlocks'][0]['innerBlocks'] = $nonColumnsBlocksBuffer;
$wrappedParsedBlocks[] = $columnsBlock;
}
return $wrappedParsedBlocks;
}
}

View File

@@ -13,6 +13,9 @@ class Renderer {
/** @var BodyRenderer */
private $bodyRenderer;
/** @var Preprocessor */
private $preprocessor;
const TEMPLATE_FILE = 'template.html';
const STYLES_FILE = 'styles.css';
@@ -21,14 +24,21 @@ class Renderer {
*/
public function __construct(
\MailPoetVendor\CSS $cssInliner,
Preprocessor $preprocessor,
BodyRenderer $bodyRenderer
) {
$this->cssInliner = $cssInliner;
$this->preprocessor = $preprocessor;
$this->bodyRenderer = $bodyRenderer;
}
public function render(\WP_Post $post, string $subject, string $preHeader, string $language, $metaRobots = ''): array {
$renderedBody = $this->bodyRenderer->renderBody($post->post_content); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$parser = new \WP_Block_Parser();
$parsedBlocks = $parser->parse($post->post_content); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$parsedBlocks = $this->preprocessor->preprocess($parsedBlocks);
$renderedBody = $this->bodyRenderer->renderBody($parsedBlocks);
$styles = (string)file_get_contents(dirname(__FILE__) . '/' . self::STYLES_FILE);
$styles = apply_filters('mailpoet_email_renderer_styles', $styles, $post);

View File

@@ -0,0 +1,63 @@
<?php declare(strict_types = 1);
namespace MailPoet\EmailEditor\Engine\Renderer;
class PreprocessorTest extends \MailPoetUnitTest {
private $paragraphBlock = [
'blockName' => 'core/paragraph',
'attrs' => [],
'innerHTML' => 'Paragraph content',
];
private $columnsBlock = [
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [[
'blockName' => 'core/column',
'attrs' => [],
'innerBlocks' => [],
]],
];
/** @var Preprocessor */
private $preprocessor;
public function _before() {
parent::_before();
$this->preprocessor = new Preprocessor();
}
public function testItWrapsSingleTopLevelBlockIntoColumns() {
$parsedDocument = [$this->paragraphBlock];
$result = $this->preprocessor->preprocess($parsedDocument);
expect($result[0]['blockName'])->equals('core/columns');
expect($result[0]['innerBlocks'][0]['blockName'])->equals('core/column');
expect($result[0]['innerBlocks'][0]['innerBlocks'][0]['blockName'])->equals('core/paragraph');
expect($result[0]['innerBlocks'][0]['innerBlocks'][0]['innerHTML'])->equals('Paragraph content');
}
public function testItDoesntWrapColumns() {
$parsedDocumentWithMultipleColumns = [$this->columnsBlock, $this->columnsBlock];
$result = $this->preprocessor->preprocess($parsedDocumentWithMultipleColumns);
expect($result)->equals($parsedDocumentWithMultipleColumns);
}
public function testItWrapsTopLevelBlocksSpreadBetweenColumns() {
$parsedDocument = [$this->paragraphBlock, $this->columnsBlock, $this->paragraphBlock, $this->paragraphBlock];
// We expect to wrap top level paragraph blocks into columns so the result should three columns blocks
$result = $this->preprocessor->preprocess($parsedDocument);
expect($result)->count(3);
// First columns contain columns with one paragraph block
expect($result[0]['innerBlocks'][0]['blockName'])->equals('core/column');
expect($result[0]['innerBlocks'][0]['innerBlocks'][0]['blockName'])->equals('core/paragraph');
// Second columns remains empty
expect($result[1]['innerBlocks'][0]['blockName'])->equals('core/column');
expect($result[1]['innerBlocks'][0]['innerBlocks'])->isEmpty();
// Third columns contain columns with two paragraph blocks
expect($result[2]['innerBlocks'][0]['blockName'])->equals('core/column');
expect($result[2]['innerBlocks'][0]['innerBlocks'])->count(2);
expect($result[2]['innerBlocks'][0]['innerBlocks'][0]['blockName'])->equals('core/paragraph');
expect($result[2]['innerBlocks'][0]['innerBlocks'][1]['blockName'])->equals('core/paragraph');
}
}