Add entity lifecycle listener for emoji sanitisation

[MAILPOET-3196]
This commit is contained in:
Rostislav Wolny
2020-10-06 11:47:23 +02:00
committed by Veljko V
parent 7449d3df3e
commit 96f2bfaa20
8 changed files with 115 additions and 4 deletions

View File

@@ -124,6 +124,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoetVendor\Doctrine\ORM\EntityManager::class)
->setFactory([new Reference(\MailPoet\Doctrine\EntityManagerFactory::class), 'createEntityManager'])
->setPublic(true);
$container->autowire(\MailPoet\Doctrine\EventListeners\EmojiEncodingListener::class)->setPublic(true);
$container->autowire(\MailPoet\Doctrine\EventListeners\TimestampListener::class);
$container->autowire(\MailPoet\Doctrine\EventListeners\ValidationListener::class);
$container->autowire(\MailPoet\Doctrine\Validator\ValidatorFactory::class);

View File

@@ -2,6 +2,7 @@
namespace MailPoet\Doctrine;
use MailPoet\Doctrine\EventListeners\EmojiEncodingListener;
use MailPoet\Doctrine\EventListeners\TimestampListener;
use MailPoet\Doctrine\EventListeners\ValidationListener;
use MailPoet\Tracy\DoctrinePanel\DoctrinePanel;
@@ -25,16 +26,21 @@ class EntityManagerFactory {
/** @var ValidationListener */
private $validationListener;
/** @var EmojiEncodingListener */
private $emojiEncodingListener;
public function __construct(
Connection $connection,
Configuration $configuration,
TimestampListener $timestampListener,
ValidationListener $validationListener
ValidationListener $validationListener,
EmojiEncodingListener $emojiEncodingListener
) {
$this->connection = $connection;
$this->configuration = $configuration;
$this->timestampListener = $timestampListener;
$this->validationListener = $validationListener;
$this->emojiEncodingListener = $emojiEncodingListener;
}
public function createEntityManager() {
@@ -56,5 +62,10 @@ class EntityManagerFactory {
[Events::onFlush],
$this->validationListener
);
$entityManager->getEventManager()->addEventListener(
[Events::prePersist, Events::preUpdate],
$this->emojiEncodingListener
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace MailPoet\Doctrine\EventListeners;
use MailPoet\Entities\FormEntity;
use MailPoet\WP\Emoji;
use MailPoetVendor\Doctrine\ORM\Event\LifecycleEventArgs;
class EmojiEncodingListener {
/** @var Emoji */
private $emoji;
public function __construct(Emoji $emoji) {
$this->emoji = $emoji;
}
public function prePersist(LifecycleEventArgs $eventArgs) {
$this->sanitizeEmojiBeforeSaving($eventArgs);
}
public function preUpdate(LifecycleEventArgs $eventArgs) {
$this->sanitizeEmojiBeforeSaving($eventArgs);
}
private function sanitizeEmojiBeforeSaving(LifecycleEventArgs $eventArgs) {
$entity = $eventArgs->getEntity();
if ($entity instanceof FormEntity) {
$body = $entity->getBody();
if ($body !== null) {
$entity->setBody($this->emoji->sanitizeEmojisInFormBody($body));
}
}
}
}

View File

@@ -29,6 +29,12 @@ class Emoji {
return $this->decodeEntities($newsletterRenderedBody);
}
public function sanitizeEmojisInFormBody(array $body): array {
$bodyJson = json_encode($body, JSON_UNESCAPED_UNICODE);
$fixedJson = $this->encodeForUTF8Column(MP_FORMS_TABLE, 'body', $bodyJson);
return json_decode($fixedJson, true);
}
private function encodeRenderedBodyForUTF8Column($value) {
return $this->encodeForUTF8Column(
MP_SENDING_QUEUES_TABLE,

View File

@@ -0,0 +1,50 @@
<?php
namespace MailPoet\Test\Doctrine\EventListeners;
use MailPoet\Doctrine\EventListeners\EmojiEncodingListener;
use MailPoet\Entities\FormEntity;
use MailPoet\WP\Emoji;
use MailPoetVendor\Doctrine\ORM\Events;
class EmojiEncodingListenerTest extends \MailPoetTest {
public function testItSanitizeFormEntityOnPersistAndUpdate() {
$form = new FormEntity('Form' );
$form->setBody(['body']);
$emojiMock = $this->createMock(Emoji::class);
$emojiMock->expects($this->exactly(2))
->method('sanitizeEmojisInFormBody')
->willReturn(['sanitizedBody']);
$emojiEncodingListenerWithMockedEmoji = new EmojiEncodingListener($emojiMock);
$originalListener = $this->diContainer->get(EmojiEncodingListener::class);
$this->replaceListeners($originalListener, $emojiEncodingListenerWithMockedEmoji);
$this->entityManager->persist($form);
$this->entityManager->flush($form);
expect($form->getBody())->equals(['sanitizedBody']);
$form->setBody(['updatedBody']);
$this->entityManager->flush($form);
expect($form->getBody())->equals(['sanitizedBody']);
$this->replaceListeners($emojiEncodingListenerWithMockedEmoji, $originalListener);
}
/**
* We have to replace event listeners since EventManager
* is shared for all entity managers using same DB connection
*/
private function replaceListeners($original, $replacement) {
$this->entityManager->getEventManager()->removeEventListener(
[Events::prePersist, Events::preUpdate],
$original
);
$this->entityManager->getEventManager()->addEventListener(
[Events::prePersist, Events::preUpdate],
$replacement
);
}
public function _after() {
parent::_after();
$this->truncateEntity(FormEntity::class);
}
}

View File

@@ -5,9 +5,11 @@ namespace MailPoet\Test\Doctrine\EventListeners;
use MailPoet\Doctrine\Annotations\AnnotationReaderProvider;
use MailPoet\Doctrine\ConfigurationFactory;
use MailPoet\Doctrine\EntityManagerFactory;
use MailPoet\Doctrine\EventListeners\EmojiEncodingListener;
use MailPoet\Doctrine\EventListeners\TimestampListener;
use MailPoet\Doctrine\EventListeners\ValidationListener;
use MailPoet\Doctrine\Validator\ValidatorFactory;
use MailPoet\WP\Emoji;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Carbon\Carbon;
use MailPoetVendor\Doctrine\Common\Cache\ArrayCache;
@@ -91,7 +93,8 @@ class TimestampListenerTest extends \MailPoetTest {
$validatorFactory = new ValidatorFactory($annotationReaderProvider);
$timestampListener = new TimestampListener($this->wp);
$validationListener = new ValidationListener($validatorFactory->createValidator());
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener);
$emojiEncodingListener = new EmojiEncodingListener(new Emoji($this->wp));
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener, $emojiEncodingListener);
return $entityManagerFactory->createEntityManager();
}
}

View File

@@ -5,10 +5,12 @@ namespace MailPoet\Test\Doctrine\EventListeners;
use MailPoet\Doctrine\Annotations\AnnotationReaderProvider;
use MailPoet\Doctrine\ConfigurationFactory;
use MailPoet\Doctrine\EntityManagerFactory;
use MailPoet\Doctrine\EventListeners\EmojiEncodingListener;
use MailPoet\Doctrine\EventListeners\TimestampListener;
use MailPoet\Doctrine\EventListeners\ValidationListener;
use MailPoet\Doctrine\Validator\ValidationException;
use MailPoet\Doctrine\Validator\ValidatorFactory;
use MailPoet\WP\Emoji;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Doctrine\Common\Cache\ArrayCache;
@@ -75,7 +77,8 @@ class ValidationTest extends \MailPoetTest {
$validatorFactory = new ValidatorFactory($annotationReaderProvider);
$timestampListener = new TimestampListener($this->wp);
$validationListener = new ValidationListener($validatorFactory->createValidator());
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener);
$emojiEncodingListener = new EmojiEncodingListener(new Emoji($this->wp));
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener, $emojiEncodingListener);
return $entityManagerFactory->createEntityManager();
}
}

View File

@@ -6,10 +6,12 @@ use Exception;
use MailPoet\Doctrine\Annotations\AnnotationReaderProvider;
use MailPoet\Doctrine\ConfigurationFactory;
use MailPoet\Doctrine\EntityManagerFactory;
use MailPoet\Doctrine\EventListeners\EmojiEncodingListener;
use MailPoet\Doctrine\EventListeners\TimestampListener;
use MailPoet\Doctrine\EventListeners\ValidationListener;
use MailPoet\Doctrine\Validator\ValidatorFactory;
use MailPoet\Test\Doctrine\Types\JsonEntity;
use MailPoet\WP\Emoji;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Doctrine\Common\Cache\ArrayCache;
use RuntimeException;
@@ -183,7 +185,8 @@ class JsonTypesTest extends \MailPoetTest {
$validatorFactory = new ValidatorFactory($annotationReaderProvider);
$timestampListener = new TimestampListener($this->wp);
$validationListener = new ValidationListener($validatorFactory->createValidator());
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener);
$emojiEncodingListener = new EmojiEncodingListener(new Emoji($this->wp));
$entityManagerFactory = new EntityManagerFactory($this->connection, $configuration, $timestampListener, $validationListener, $emojiEncodingListener);
return $entityManagerFactory->createEntityManager();
}
}