Add emoji support to newsletter body [MAILPOET-1009]
This commit is contained in:
@ -48,6 +48,11 @@ class Menu {
|
|||||||
if(self::isOnMailPoetAdminPage()) {
|
if(self::isOnMailPoetAdminPage()) {
|
||||||
do_action('mailpoet_conflict_resolver_styles');
|
do_action('mailpoet_conflict_resolver_styles');
|
||||||
do_action('mailpoet_conflict_resolver_scripts');
|
do_action('mailpoet_conflict_resolver_scripts');
|
||||||
|
|
||||||
|
if($_REQUEST['page'] === 'mailpoet-newsletter-editor') {
|
||||||
|
// Disable WP emojis to not interfere with the newsletter editor emoji handling
|
||||||
|
$this->disableWPEmojis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$main_page_slug = 'mailpoet-newsletters';
|
$main_page_slug = 'mailpoet-newsletters';
|
||||||
@ -278,6 +283,11 @@ class Menu {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function disableWPEmojis() {
|
||||||
|
remove_action('admin_print_scripts', 'print_emoji_detection_script');
|
||||||
|
remove_action('admin_print_styles', 'print_emoji_styles');
|
||||||
|
}
|
||||||
|
|
||||||
function welcome() {
|
function welcome() {
|
||||||
if((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
|
if((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use Carbon\Carbon;
|
|||||||
use MailPoet\Newsletter\Renderer\Renderer;
|
use MailPoet\Newsletter\Renderer\Renderer;
|
||||||
use MailPoet\Util\Helpers;
|
use MailPoet\Util\Helpers;
|
||||||
use MailPoet\Util\Security;
|
use MailPoet\Util\Security;
|
||||||
|
use MailPoet\WP\Emoji;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
@ -794,6 +795,10 @@ class Newsletter extends Model {
|
|||||||
$newsletter = self::findOne((int)$data['id']);
|
$newsletter = self::findOne((int)$data['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!empty($data['body'])) {
|
||||||
|
$data['body'] = Emoji::encodeForUTF8Column(self::$_table, 'body', $data['body']);
|
||||||
|
}
|
||||||
|
|
||||||
if($newsletter === false) {
|
if($newsletter === false) {
|
||||||
$newsletter = self::create();
|
$newsletter = self::create();
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Models;
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
use MailPoet\WP\Emoji;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
class SendingQueue extends Model {
|
class SendingQueue extends Model {
|
||||||
@ -55,7 +57,10 @@ class SendingQueue extends Model {
|
|||||||
$this->set('subscribers', serialize($this->subscribers));
|
$this->set('subscribers', serialize($this->subscribers));
|
||||||
}
|
}
|
||||||
if(!is_serialized($this->newsletter_rendered_body) && !is_null($this->newsletter_rendered_body)) {
|
if(!is_serialized($this->newsletter_rendered_body) && !is_null($this->newsletter_rendered_body)) {
|
||||||
$this->set('newsletter_rendered_body', serialize($this->newsletter_rendered_body));
|
$this->set(
|
||||||
|
'newsletter_rendered_body',
|
||||||
|
serialize($this->encodeEmojisInBody($this->newsletter_rendered_body))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// set the default priority to medium
|
// set the default priority to medium
|
||||||
if(!$this->priority) {
|
if(!$this->priority) {
|
||||||
@ -81,12 +86,34 @@ class SendingQueue extends Model {
|
|||||||
function getNewsletterRenderedBody($type = false) {
|
function getNewsletterRenderedBody($type = false) {
|
||||||
$rendered_newsletter = (!is_serialized($this->newsletter_rendered_body)) ?
|
$rendered_newsletter = (!is_serialized($this->newsletter_rendered_body)) ?
|
||||||
$this->newsletter_rendered_body :
|
$this->newsletter_rendered_body :
|
||||||
unserialize($this->newsletter_rendered_body);
|
$this->decodeEmojisInBody(unserialize($this->newsletter_rendered_body));
|
||||||
return ($type && !empty($rendered_newsletter[$type])) ?
|
return ($type && !empty($rendered_newsletter[$type])) ?
|
||||||
$rendered_newsletter[$type] :
|
$rendered_newsletter[$type] :
|
||||||
$rendered_newsletter;
|
$rendered_newsletter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeEmojisInBody($newsletter_rendered_body) {
|
||||||
|
if(is_array($newsletter_rendered_body)) {
|
||||||
|
foreach($newsletter_rendered_body as $key => $value) {
|
||||||
|
$newsletter_rendered_body[$key] = Emoji::encodeForUTF8Column(
|
||||||
|
self::$_table,
|
||||||
|
'newsletter_rendered_body',
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $newsletter_rendered_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeEmojisInBody($newsletter_rendered_body) {
|
||||||
|
if(is_array($newsletter_rendered_body)) {
|
||||||
|
foreach($newsletter_rendered_body as $key => $value) {
|
||||||
|
$newsletter_rendered_body[$key] = Emoji::decodeEntities($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $newsletter_rendered_body;
|
||||||
|
}
|
||||||
|
|
||||||
function isSubscriberProcessed($subscriber_id) {
|
function isSubscriberProcessed($subscriber_id) {
|
||||||
$subscribers = $this->getSubscribers();
|
$subscribers = $this->getSubscribers();
|
||||||
return in_array($subscriber_id, $subscribers['processed']);
|
return in_array($subscriber_id, $subscribers['processed']);
|
||||||
|
32
lib/WP/Emoji.php
Normal file
32
lib/WP/Emoji.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\WP;
|
||||||
|
|
||||||
|
class Emoji {
|
||||||
|
static function encodeForUTF8Column($table, $field, $value) {
|
||||||
|
global $wpdb;
|
||||||
|
$charset = $wpdb->get_col_charset($table, $field);
|
||||||
|
if($charset === 'utf8') {
|
||||||
|
$value = wp_encode_emoji($value);
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function decodeEntities($content) {
|
||||||
|
// Based on wp_staticize_emoji()
|
||||||
|
|
||||||
|
// Loosely match the Emoji Unicode range.
|
||||||
|
$regex = '/(&#x[2-3][0-9a-f]{3};|[1-6][0-9a-f]{2};)/';
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
if(preg_match_all($regex, $content, $matches)) {
|
||||||
|
if(!empty($matches[1])) {
|
||||||
|
foreach($matches[1] as $emoji) {
|
||||||
|
$entity = html_entity_decode($emoji, ENT_HTML5, 'UTF-8');
|
||||||
|
$content = str_replace($emoji, $entity, $content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
42
tests/unit/Models/SendingQueueTest.php
Normal file
42
tests/unit/Models/SendingQueueTest.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Test\Models;
|
||||||
|
|
||||||
|
use AspectMock\Test as Mock;
|
||||||
|
use MailPoet\Models\SendingQueue;
|
||||||
|
|
||||||
|
class SendingQueueTest extends \MailPoetTest {
|
||||||
|
function _before() {
|
||||||
|
$this->queue = SendingQueue::create();
|
||||||
|
$this->queue->save();
|
||||||
|
|
||||||
|
$this->rendered_body = array(
|
||||||
|
'html' => 'some html',
|
||||||
|
'text' => 'some text'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanEncodeEmojisInBody() {
|
||||||
|
$mock = Mock::double('MailPoet\WP\Emoji', [
|
||||||
|
'encodeForUTF8Column' => function($params) {
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
$this->queue->encodeEmojisInBody($this->rendered_body);
|
||||||
|
$mock->verifyInvokedMultipleTimes('encodeForUTF8Column', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanDecodeEmojisInBody() {
|
||||||
|
$mock = Mock::double('MailPoet\WP\Emoji', [
|
||||||
|
'decodeEntities' => function($params) {
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
$this->queue->decodeEmojisInBody($this->rendered_body);
|
||||||
|
$mock->verifyInvokedMultipleTimes('decodeEntities', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _after() {
|
||||||
|
Mock::clean();
|
||||||
|
\ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
|
||||||
|
}
|
||||||
|
}
|
51
tests/unit/WP/EmojiTest.php
Normal file
51
tests/unit/WP/EmojiTest.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Test\WP;
|
||||||
|
|
||||||
|
use MailPoet\Config\Env;
|
||||||
|
use MailPoet\WP\Emoji;
|
||||||
|
|
||||||
|
class EmojiTest extends \MailPoetTest {
|
||||||
|
function _before() {
|
||||||
|
$this->data_encoded = "Emojis: 😃😵💪, not emojis: .Ž";
|
||||||
|
$this->data_decoded = "Emojis: 😃😵💪, not emojis: .Ž";
|
||||||
|
|
||||||
|
$this->column = 'dummycol';
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanEncodeForUTF8Column() {
|
||||||
|
$table = Env::$db_prefix . 'dummytable_utf8';
|
||||||
|
$this->createTable($table, 'utf8');
|
||||||
|
|
||||||
|
$result = Emoji::encodeForUTF8Column($table, $this->column, $this->data_decoded);
|
||||||
|
expect($result)->equals($this->data_encoded);
|
||||||
|
|
||||||
|
$this->dropTable($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItDoesNotEncodeForUTF8MB4Column() {
|
||||||
|
$table = Env::$db_prefix . 'dummytable_utf8mb4';
|
||||||
|
$this->createTable($table, 'utf8mb4');
|
||||||
|
|
||||||
|
$result = Emoji::encodeForUTF8Column($table, $this->column, $this->data_decoded);
|
||||||
|
expect($result)->equals($this->data_decoded);
|
||||||
|
|
||||||
|
$this->dropTable($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testItCanDecodeEntities() {
|
||||||
|
$result = Emoji::decodeEntities($this->data_encoded);
|
||||||
|
expect($result)->equals($this->data_decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTable($table, $charset) {
|
||||||
|
\ORM::raw_execute(
|
||||||
|
'CREATE TABLE IF NOT EXISTS ' . $table
|
||||||
|
. ' (' . $this->column . ' TEXT) '
|
||||||
|
. 'DEFAULT CHARSET=' . $charset . ';'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dropTable($table) {
|
||||||
|
\ORM::raw_execute('DROP TABLE IF EXISTS ' . $table);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user