diff --git a/assets/js/src/queue.jsx b/assets/js/src/cron.jsx similarity index 86% rename from assets/js/src/queue.jsx rename to assets/js/src/cron.jsx index f083859a16..498bdc50e2 100644 --- a/assets/js/src/queue.jsx +++ b/assets/js/src/cron.jsx @@ -2,22 +2,20 @@ define( [ 'react', 'react-dom', - 'mailpoet', - 'classnames' + 'mailpoet' ], function ( React, ReactDOM, - MailPoet, - classNames + MailPoet ) { - var QueueControl = React.createClass({ + var CronControl = React.createClass({ getInitialState: function () { - return (queueDaemon) ? queueDaemon : null; + return (cronDaemon) ? cronDaemon : null; }, getDaemonData: function () { MailPoet.Ajax.post({ - endpoint: 'queue', + endpoint: 'cron', action: 'getDaemonStatus' }).done(function (response) { jQuery('.button-primary').removeClass('disabled'); @@ -37,7 +35,7 @@ define( controlDaemon: function (action) { jQuery('.button-primary').addClass('disabled'); MailPoet.Ajax.post({ - endpoint: 'queue', + endpoint: 'cron', action: 'controlDaemon', data: {'action': action} }).done(function (response) { @@ -60,7 +58,7 @@ define( return (
- Queue daemon is running. + Cron daemon is running.

It was started @@ -89,11 +87,11 @@ define( } } }); - let container = document.getElementById('queue_container'); + let container = document.getElementById('cron_container'); if (container) { ReactDOM.render( - , - document.getElementById('queue_status') + , + document.getElementById('cron_status') ) } } diff --git a/assets/js/src/newsletters/send.jsx b/assets/js/src/newsletters/send.jsx index afe8fd3548..d4476481f7 100644 --- a/assets/js/src/newsletters/send.jsx +++ b/assets/js/src/newsletters/send.jsx @@ -94,7 +94,7 @@ define( ], handleSend: function() { MailPoet.Ajax.post({ - endpoint: 'queue', + endpoint: 'sendingQueue', action: 'addQueue', data: { newsletter_id: this.props.params.id, diff --git a/lib/Config/Env.php b/lib/Config/Env.php index 9559b020b1..ca2f3430be 100644 --- a/lib/Config/Env.php +++ b/lib/Config/Env.php @@ -77,11 +77,16 @@ class Env { static function isPluginActivated() { $activatesPlugins = get_option('active_plugins'); - $isActivated = + $isActivated = ( in_array( sprintf('%s/%s.php', basename(self::$path), self::$plugin_name), $activatesPlugins - ); + ) || + in_array( + sprintf('%s/%s.php', explode('/', plugin_basename(__FILE__))[0], self::$plugin_name), + $activatesPlugins + ) + ); return ($isActivated) ? true : false; } } \ No newline at end of file diff --git a/lib/Config/Initializer.php b/lib/Config/Initializer.php index c631989660..f6e3b19bf7 100644 --- a/lib/Config/Initializer.php +++ b/lib/Config/Initializer.php @@ -2,7 +2,7 @@ namespace MailPoet\Config; use MailPoet\Models; -use MailPoet\Queue\Supervisor; +use MailPoet\Cron\Supervisor; use MailPoet\Router; if(!defined('ABSPATH')) exit; @@ -55,7 +55,7 @@ class Initializer { $subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field'; $newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields'; $newsletter_option = Env::$db_prefix . 'newsletter_option'; - $queues = Env::$db_prefix . 'queues'; + $sending_queue = Env::$db_prefix . 'sending_queue'; $newsletter_statistics = Env::$db_prefix . 'newsletter_statistics'; define('MP_SUBSCRIBERS_TABLE', $subscribers); @@ -72,7 +72,7 @@ class Initializer { define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field); define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields); define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option); - define('MP_QUEUES_TABLE', $queues); + define('MP_SENDING_QUEUE_TABLE', $sending_queue); define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics); } diff --git a/lib/Config/Menu.php b/lib/Config/Menu.php index 2bc4c42abe..b20a0696b8 100644 --- a/lib/Config/Menu.php +++ b/lib/Config/Menu.php @@ -172,13 +172,13 @@ class Menu { add_submenu_page( 'mailpoet', - __('Queue'), - __('Queue'), + __('Cron'), + __('Cron'), 'manage_options', - 'mailpoet-queue', + 'mailpoet-cron', array( $this, - 'queue' + 'cron' ) ); } @@ -374,9 +374,9 @@ class Menu { echo $this->renderer->render('form/editor.html', $data); } - function queue() { - $daemon = new \MailPoet\Queue\BootStrapMenu(); + function cron() { + $daemon = new \MailPoet\Cron\BootStrapMenu(); $data['daemon'] = json_encode($daemon->bootstrap()); - echo $this->renderer->render('queue.html', $data); + echo $this->renderer->render('cron.html', $data); } } \ No newline at end of file diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php index 9998a267ab..82ada56b1b 100644 --- a/lib/Config/Migrator.php +++ b/lib/Config/Migrator.php @@ -21,7 +21,7 @@ class Migrator { 'subscriber_custom_field', 'newsletter_option_fields', 'newsletter_option', - 'queues', + 'sending_queue', 'newsletter_statistics', 'forms' ); @@ -205,12 +205,12 @@ class Migrator { return $this->sqlify(__FUNCTION__, $attributes); } - function queues() { + function sending_queue() { $attributes = array( 'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'newsletter_id mediumint(9) NOT NULL,', 'subscribers longtext,', - 'status varchar(12) NOT NULL,', + 'status varchar(12) NULL DEFAULT NULL,', 'priority mediumint(9) NOT NULL DEFAULT 0,', 'count_total mediumint(9) NOT NULL DEFAULT 0,', 'count_processed mediumint(9) NOT NULL DEFAULT 0,', diff --git a/lib/Config/PublicAPI.php b/lib/Config/PublicAPI.php index f8ebe0223a..b36a699ab0 100644 --- a/lib/Config/PublicAPI.php +++ b/lib/Config/PublicAPI.php @@ -1,7 +1,7 @@ daemon = Setting::where('name', 'daemon') + $this->daemon = Setting::where('name', 'cron_daemon') ->findOne(); } function bootStrap() { - $queues = Queue::findMany(); return ($this->daemon) ? array_merge( array( @@ -21,13 +19,15 @@ class BootStrapMenu { 'Y-m-d H:i:s', $this->daemon->created_at, 'UTC' - )->diffForHumans(), + ) + ->diffForHumans(), 'timeSinceUpdate' => Carbon::createFromFormat( 'Y-m-d H:i:s', $this->daemon->updated_at, 'UTC' - )->diffForHumans() + ) + ->diffForHumans() ), json_decode($this->daemon->value, true) ) : diff --git a/lib/Queue/Daemon.php b/lib/Cron/Daemon.php similarity index 83% rename from lib/Queue/Daemon.php rename to lib/Cron/Daemon.php index 1012cf1f0a..5c009892d6 100644 --- a/lib/Queue/Daemon.php +++ b/lib/Cron/Daemon.php @@ -1,5 +1,5 @@ daemonData; if(!$daemon) { $daemon = Setting::create(); - $daemon->name = 'daemon'; - $daemon->value = json_encode( - array( - 'status' => 'stopped', - )); + $daemon->name = 'cron_daemon'; + $daemonData = array( + 'status' => null, + 'counter' => 0 + ); + $daemon->value = json_encode($daemonData); $daemon->save(); } if($daemonData['status'] !== 'started') { - $_SESSION['daemon'] = 'started'; - $daemonData = array( - 'status' => 'started', - 'token' => $this->refreshedToken, - 'counter' => $daemonData['status'] === 'paused' ? - $daemonData['counter'] : - 0 - ); - $_SESSION['daemon'] = array('result' => true); + $_SESSION['cron_daemon'] = 'started'; + $daemonData['status'] = 'started'; + $daemonData['token'] = $this->refreshedToken; + $_SESSION['cron_daemon'] = array('result' => true); $this->manageSession('end'); $daemon->value = json_encode($daemonData); $daemon->save(); $this->callSelf(); } else { - $_SESSION['daemon'] = array( + $_SESSION['cron_daemon'] = array( 'result' => false, 'error' => 'already started' ); @@ -67,7 +63,7 @@ class Daemon { $this->abortWithError('invalid token'); } - $worker = new Worker(); + $worker = new Worker($this->timer); $worker->process(); $elapsedTime = microtime(true) - $this->timer; if($elapsedTime < 30) { @@ -84,7 +80,7 @@ class Daemon { } function getDaemon() { - $daemon = Setting::where('name', 'daemon') + $daemon = Setting::where('name', 'cron_daemon') ->findOne(); return array( ($daemon) ? $daemon : null, diff --git a/lib/Queue/Supervisor.php b/lib/Cron/Supervisor.php similarity index 63% rename from lib/Queue/Supervisor.php rename to lib/Cron/Supervisor.php index cdd13cd1ab..eae5e2b263 100644 --- a/lib/Queue/Supervisor.php +++ b/lib/Cron/Supervisor.php @@ -1,5 +1,5 @@ daemon) { return $this->startDaemon(); - } else { - if(!$this->forceStart && ( - $this->daemonData['status'] === 'paused' || - $this->daemonData['status'] === 'stopped' - ) - ) { - return; - } - $currentTime = Carbon::now('UTC'); - $lastUpdateTime = Carbon::createFromFormat( - 'Y-m-d H:i:s', - $this->daemon->updated_at, 'UTC' - ); - $timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime); - if(!$this->forceStart && $timeSinceLastStart < 50) return; - if( - ($this->forceStart && $this->daemonData['status'] === 'paused') || - !$this->forceStart - ) { - $this->daemonData['status'] = 'paused'; - } - $this->daemon->value = json_encode($this->daemonData); - $this->daemon->save(); - return $this->startDaemon(); } + if(!$this->forceStart && $this->daemonData['status'] === 'stopped') { + return; + } + $currentTime = Carbon::now('UTC'); + $lastUpdateTime = Carbon::createFromFormat( + 'Y-m-d H:i:s', + $this->daemon->updated_at, 'UTC' + ); + $timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime); + if($timeSinceLastStart < 40) return; + $this->daemonData['status'] = null; + $this->daemon->value = json_encode($this->daemonData); + $this->daemon->save(); + return $this->startDaemon(); } function startDaemon() { if(!session_id()) session_start(); $sessionId = session_id(); session_write_close(); - $_SESSION['daemon'] = null; + $_SESSION['cron_daemon'] = null; $payload = json_encode(array('session' => $sessionId)); self::getRemoteUrl( '/?mailpoet-api§ion=queue&action=start&payload=' . urlencode($payload) ); session_start(); - $daemonStatus = $_SESSION['daemon']; + $daemonStatus = $_SESSION['cron_daemon']; unset($_SESSION['daemon']); session_write_close(); return $daemonStatus; } function getDaemon() { - $daemon = Setting::where('name', 'daemon') + $daemon = Setting::where('name', 'cron_daemon') ->findOne(); $daemonData = ($daemon) ? json_decode($daemon->value, true) : false; return array( diff --git a/lib/Cron/Worker.php b/lib/Cron/Worker.php new file mode 100644 index 0000000000..a6e4c9e0d9 --- /dev/null +++ b/lib/Cron/Worker.php @@ -0,0 +1,102 @@ +timer = ($timer) ? $timer : microtime(true); + } + + function process() { + $queues = + SendingQueue::orderByDesc('priority') + ->whereNull('deleted_at') + ->whereNull('status') + ->findResultSet(); + // TODO: usee Mailer's method to get the mailer from DB + $mailer = new Mailer(); + $mailer->mailer['method'] = 'MailPoet'; + $mailer->mailer['class'] = 'MailPoet\\Mailer\\MailPoet'; + $mailer->mailer['api_key'] = Setting::getValue('api_key', 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU'); + foreach($queues as $queue) { + if($this->checkExecutionTimer()) break; + $newsletter = Newsletter::findOne($queue->newsletter_id); + if(!$newsletter) { + continue; + }; + $newsletter = $newsletter->asArray(); + // TODO: render newsletter + $newsletter = array( + 'subject' => $newsletter['subject'], + 'id' => $newsletter['id'], + 'body' => array( + 'html' => 'rendering not yet implemented', + 'text' => 'rendering not yet implemented' + ) + ); + $subscribers = json_decode($queue->subscribers, true); + $subscribersToProcess = $subscribers['to_process']; + if(!isset($subscribers['failed'])) $subscribers['failed'] = array(); + if(!isset($subscribers['processed'])) $subscribers['processed'] = array(); + foreach(array_chunk($subscribersToProcess, 200) as $subscriberIds) { + if($this->checkExecutionTimer()) break; + $dbSubscribers = Subscriber::whereIn('id', $subscriberIds) + ->findArray(); + foreach($dbSubscribers as $i => $dbSubscriber) { + if($this->checkExecutionTimer()) break; + // TODO: replace shortcodes in the newsletter + $result = $mailer->send( + $newsletter, + sprintf( + "%s %s <%s>", + $dbSubscriber['first_name'], + $dbSubscriber['last_name'], + $dbSubscriber['email'] + ) + ); + $newsletterStatistics = NewsletterStatistics::create(); + $newsletterStatistics->subscriber_id = $dbSubscriber['id']; + $newsletterStatistics->newsletter_id = $newsletter['id']; + $newsletterStatistics->queue_id = $queue->id; + $newsletterStatistics->save(); + if($result) { + $subscribers['processed'][] = $dbSubscriber['id']; + } else { + $subscribers['failed'][] = $dbSubscriber['id']; + } + $subscribers['to_process'] = array_values( + array_diff( + $subscribers['to_process'], + array_merge($subscribers['processed'], $subscribers['failed']) + ) + ); + $queue->count_processed = + count($subscribers['processed']) + count($subscribers['failed']); + $queue->count_to_process = count($subscribers['to_process']); + $queue->count_failed = count($subscribers['failed']); + $queue->count_total = + $queue->count_processed + $queue->count_to_process; + if(!$queue->count_to_process) { + $queue->processed_at = date('Y-m-d H:i:s'); + $queue->status = 'completed'; + } + $queue->subscribers = json_encode($subscribers); + $queue->save(); + } + } + } + } + + function checkExecutionTimer() { + $elapsedTime = microtime(true) - $this->timer; + return ($elapsedTime >= 28) ? true : false; + } +} \ No newline at end of file diff --git a/lib/Models/Queue.php b/lib/Models/SendingQueue.php similarity index 59% rename from lib/Models/Queue.php rename to lib/Models/SendingQueue.php index 3bbafa9d4b..d1c9762132 100644 --- a/lib/Models/Queue.php +++ b/lib/Models/SendingQueue.php @@ -3,8 +3,8 @@ namespace MailPoet\Models; if(!defined('ABSPATH')) exit; -class Queue extends Model { - public static $_table = MP_QUEUES_TABLE; +class SendingQueue extends Model { + public static $_table = MP_SENDING_QUEUE_TABLE; function __construct() { parent::__construct(); diff --git a/lib/Queue/Worker.php b/lib/Queue/Worker.php deleted file mode 100644 index 7c53b064ce..0000000000 --- a/lib/Queue/Worker.php +++ /dev/null @@ -1,66 +0,0 @@ -timer = $timer; - $this->timer = microtime(true); - } - - function process() { - $queues = - Queue::orderByDesc('priority') - ->whereNull('deleted_at') - ->whereNotIn('status', array( - 'paused', - 'completed' - )) - ->findResultSet(); - foreach($queues as $queue) { - $newsletter = Newsletter::findOne($queue->newsletter_id); - if(!$newsletter) { - continue; - }; - $newsletter = $newsletter->asArray(); - $subscribers = json_decode($queue->subscribers, true); - if(!isset($subscribers['failed'])) $subscribers['failed'] = array(); - if(!isset($subscribers['processed'])) $subscribers['processed'] = array(); - $subscribersToProcess = $subscribers['to_process']; - foreach($subscribersToProcess as $subscriber) { - $elapsedTime = microtime(true) - $this->timer; - if($elapsedTime >= 28) break; - // TODO: hook up to mailer - sleep(1); - $newsletterStatistics = NewsletterStatistics::create(); - $newsletterStatistics->subscriber_id = $subscriber; - $newsletterStatistics->newsletter_id = $newsletter['id']; - $newsletterStatistics->queue_id = $queue->id; - $newsletterStatistics->save(); - $subscribers['processed'][] = $subscriber; - $subscribers['to_process'] = array_values( - array_diff( - $subscribers['to_process'], - $subscribers['processed'] - ) - ); - $queue->count_processed = count($subscribers['processed']); - $queue->count_to_process = count($subscribers['to_process']); - $queue->count_failed = count($subscribers['failed']); - $queue->count_total = - $queue->count_processed + $queue->count_to_process + $queue->count_failed; - if(!$queue->count_to_process) { - $queue->processed_at = date('Y-m-d H:i:s'); - $queue->status = 'completed'; - } - $queue->subscribers = json_encode($subscribers); - $queue->save(); - } - } - } -} \ No newline at end of file diff --git a/lib/Router/Cron.php b/lib/Router/Cron.php new file mode 100644 index 0000000000..142442b18e --- /dev/null +++ b/lib/Router/Cron.php @@ -0,0 +1,194 @@ + $supervisor->checkDaemon() ? + true : + false + ) + ); + break; + case 'stop': + $status = 'stopped'; + break; + default: + $status = 'paused'; + break; + } + $daemon = new Daemon(); + if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') { + $result = false; + } else { + $daemon->daemonData['status'] = $status; + $daemon->daemon->value = json_encode($daemon->daemonData); + $result = $daemon->daemon->save(); + } + wp_send_json( + array( + 'result' => $result + ) + ); + } + + function getDaemonStatus() { + $daemon = new \MailPoet\Cron\BootStrapMenu(); + wp_send_json($daemon->bootStrap()); + } + + function addQueue($data) { + $queue = SendingQueue::where('newsletter_id', $data['newsletter_id']) + ->whereNull('status') + ->findArray(); + + !d($queue); + exit; + $queue = SendingQueue::create(); + + $queue->newsletter_id = $data['newsletter_id']; + + + $subscriber_ids = array(); + $segments = Segment::whereIn('id', $data['segments']) + ->findMany(); + foreach($segments as $segment) { + $subscriber_ids = array_merge($subscriber_ids, Helpers::arrayColumn( + $segment->subscribers() + ->findArray(), + 'id' + )); + } + + $subscriber_ids = array_unique($subscriber_ids); + $queue->subscribers = json_encode( + array( + 'to_process' => $subscriber_ids + ) + ); + + $queue->count_total = $queue->count_to_process = count($subscriber_ids); + $queue->save(); + wp_send_json( + !$queue->save() ? + array( + 'result' => false, + 'error' => 'Queue could not be created.' + ) : + array( + 'result' => true, + 'data' => array($queue->id) + ) + ); + } + + function addQueues($data) { + $result = array_map(function ($queueData) { + $queue = SendingQueue::create(); + $queue->newsletter_id = $queueData['newsletter_id']; + $queue->subscribers = json_encode( + array( + 'to_process' => $queueData['subscribers'] + ) + ); + $queue->count_total = $queue->count_to_process = count($queueData['subscribers']); + $queue->save(); + return array( + 'newsletter_id' => $queue->newsletter_id, + 'queue_id' => $queue->id + ); + }, $data); + $result = Helpers::arrayColumn($result, 'queue_id', 'newsletter_id'); + wp_send_json( + count($data) != count($result) ? + array( + 'result' => false, + 'error' => __('Some queues could not be created.'), + 'data' => $result + ) : + array( + 'result' => true, + 'data' => $result + ) + ); + } + + function deleteQueue($data) { + $queue = SendingQueue::whereNull('deleted_at') + ->findOne($data['queue_id']); + if(!$queue) { + wp_send_json( + array( + 'result' => false, + 'error' => __('Queue not found.') + ) + ); + } + $queue->deleted_at = 'Y-m-d H:i:s'; + $queue->save(); + wp_send_json(array('result' => true)); + } + + function deleteQueues($data) { + $queues = SendingQueue::whereNull('deleted_at') + ->whereIn('id', $data['queue_ids']) + ->findResultSet(); + if(!$queues->count()) { + wp_send_json( + array( + 'result' => false, + 'error' => __('Queues not found.') + ) + ); + } + foreach($queues as $queue) { + $queue->deleted_at = 'Y-m-d H:i:s'; + $queue->save(); + } + wp_send_json(array('result' => true)); + } + + function getQueueStatus($data) { + $queue = SendingQueue::whereNull('deleted_at') + ->findOne($data['queue_id']) + ->asArray(); + wp_send_json( + !$queue ? + array( + 'result' => false, + 'error' => __('Queue not found.') + ) : + array( + 'result' => true, + 'data' => $queue + ) + ); + } + + function getQueuesStatus($data) { + $queues = SendingQueue::whereNull('deleted_at') + ->whereIn('id', $data['queue_ids']) + ->findArray(); + wp_send_json( + !$queues ? + array( + 'result' => false, + 'error' => __('Queue not found.') + ) : + array( + 'result' => true, + 'data' => $queues + ) + ); + } +} \ No newline at end of file diff --git a/lib/Router/Mailer.php b/lib/Router/Mailer.php index 19932d2401..b32b225fec 100644 --- a/lib/Router/Mailer.php +++ b/lib/Router/Mailer.php @@ -1,12 +1,15 @@ fromName = $this->getSetting('from_name'); - $this->fromEmail = $this->getSetting('from_address'); + list($this->fromName, $this->fromEmail) = $this->getSetting('sender'); $this->mailer = $this->getSetting('mailer'); $this->from = sprintf('%s <%s>', $this->fromName, $this->fromEmail); } @@ -14,61 +17,61 @@ class Mailer { function send($newsletter, $subscriber) { $subscriber = $this->transformSubscriber($subscriber); $mailer = $this->buildMailer(); - return wp_send_json($mailer->send($newsletter, $subscriber)); + return $mailer->send($newsletter, $subscriber); } function buildMailer() { - switch ($this->mailer['name']) { - case 'AmazonSES': - $mailer = new $this->mailer['class']( - $this->mailer['region'], - $this->mailer['access_key'], - $this->mailer['secret_key'], - $this->from - ); - break; - case 'ElasticEmail': - $mailer = new $this->mailer['class']( - $this->mailer['api_key'], - $this->fromEmail, $this->fromName - ); - break; - case 'MailGun': - $mailer = new $this->mailer['class']( - $this->mailer['domain'], - $this->mailer['api_key'], - $this->from - ); - break; - case 'MailPoet': - $mailer = new $this->mailer['class']( - $this->mailer['api_key'], - $this->fromEmail, - $this->fromName - ); - break; - case 'Mandrill': - $mailer = new $this->mailer['class']( - $this->mailer['api_key'], - $this->fromEmail, $this->fromName - ); - break; - case 'SendGrid': - $mailer = new $this->mailer['class']( - $this->mailer['api_key'], - $this->fromEmail, - $this->fromName - ); - break; - case 'SMTP': - $mailer = new $this->mailer['class']( - $this->mailer['host'], - $this->mailer['port'], - $this->mailer['authentication'], - $this->mailer['encryption'], - $this->fromEmail, - $this->fromName); - break; + switch($this->mailer['method']) { + case 'AmazonSES': + $mailer = new $this->mailer['class']( + $this->mailer['region'], + $this->mailer['access_key'], + $this->mailer['secret_key'], + $this->from + ); + break; + case 'ElasticEmail': + $mailer = new $this->mailer['class']( + $this->mailer['api_key'], + $this->fromEmail, $this->fromName + ); + break; + case 'MailGun': + $mailer = new $this->mailer['class']( + $this->mailer['domain'], + $this->mailer['api_key'], + $this->from + ); + break; + case 'MailPoet': + $mailer = new $this->mailer['class']( + $this->mailer['api_key'], + $this->fromEmail, + $this->fromName + ); + break; + case 'Mandrill': + $mailer = new $this->mailer['class']( + $this->mailer['api_key'], + $this->fromEmail, $this->fromName + ); + break; + case 'SendGrid': + $mailer = new $this->mailer['class']( + $this->mailer['api_key'], + $this->fromEmail, + $this->fromName + ); + break; + case 'SMTP': + $mailer = new $this->mailer['class']( + $this->mailer['host'], + $this->mailer['port'], + $this->mailer['authentication'], + $this->mailer['encryption'], + $this->fromEmail, + $this->fromName); + break; } return $mailer; } @@ -84,65 +87,72 @@ class Mailer { } function getSetting($setting) { - if($setting === 'mailer') { - $mailers = array( - array( - 'name' => 'AmazonSES', - 'type' => 'API', - 'access_key' => 'AKIAJM6Y5HMGXBLDNSRA', - 'secret_key' => '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', - 'region' => 'us-east-1' - ), - array( - 'name' => 'ElasticEmail', - 'type' => 'API', - 'api_key' => '997f1f7f-41de-4d7f-a8cb-86c8481370fa' - ), - array( - 'name' => 'MailGun', - 'type' => 'API', - 'api_key' => 'key-6cf5g5qjzenk-7nodj44gdt8phe6vam2', - 'domain' => 'mrcasual.com' - ), - array( - 'name' => 'MailPoet', - 'api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU' - ), - array( - 'name' => 'Mandrill', - 'type' => 'API', - 'api_key' => '692ys1B7REEoZN7R-dYwNA' - ), - array( - 'name' => 'SendGrid', - 'type' => 'API', - 'api_key' => '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' - ), - array( - 'name' => 'SMTP', - 'host' => 'email-smtp.us-west-2.amazonaws.com', - 'port' => 587, - 'authentication' => array( - 'login' => 'AKIAIGPBLH6JWG5VCBQQ', - 'password' => 'AudVHXHaYkvr54veCzqiqOxDiMMyfQW3/V6F1tYzGXY3' - ), - 'encryption' => 'tls' - ), - array( - 'name' => 'WPMail' - ) - ); - $mailer = $mailers[array_rand($mailers)]; - $mailer['class'] = 'MailPoet\\Mailer\\' . - ((isset($mailer['type'])) ? - $mailer['type'] . '\\' . $mailer['name'] : - $mailer['name'] - ); - return $mailer; + switch($setting) { + case 'mailer': + // TODO: remove + /* $mailers = array( + array( + 'method' => 'AmazonSES', + 'type' => 'API', + 'access_key' => 'AKIAJM6Y5HMGXBLDNSRA', + 'secret_key' => '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', + 'region' => 'us-east-1' + ), + array( + 'method' => 'ElasticEmail', + 'type' => 'API', + 'api_key' => '997f1f7f-41de-4d7f-a8cb-86c8481370fa' + ), + array( + 'method' => 'MailGun', + 'type' => 'API', + 'api_key' => 'key-6cf5g5qjzenk-7nodj44gdt8phe6vam2', + 'domain' => 'mrcasual.com' + ), + array( + 'method' => 'MailPoet', + 'api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU' + ), + array( + 'method' => 'Mandrill', + 'type' => 'API', + 'api_key' => '692ys1B7REEoZN7R-dYwNA' + ), + array( + 'method' => 'SendGrid', + 'type' => 'API', + 'api_key' => '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' + ), + array( + 'method' => 'SMTP', + 'host' => 'email-smtp.us-west-2.amazonaws.com', + 'port' => 587, + 'authentication' => array( + 'login' => 'AKIAIGPBLH6JWG5VCBQQ', + 'password' => 'AudVHXHaYkvr54veCzqiqOxDiMMyfQW3/V6F1tYzGXY3' + ), + 'encryption' => 'tls' + ), + array( + 'method' => 'WPMail' + ) + );*/ + $mailer = Setting::getValue('mta', null); + if(!$mailer) throw new \Exception('Mailing method has not been configured.'); + $mailer['class'] = 'MailPoet\\Mailer\\' . + ((isset($mailer['type'])) ? + $mailer['type'] . '\\' . $mailer['method'] : + $mailer['method'] + ); + return $mailer; + break;; + case 'sender': + $sender = Setting::getValue($setting, null); + return array($sender['name'], $sender['address']); + break; + default: + return Setting::getValue($setting, null); + break; } - if($setting === 'from_name') return 'Sender'; - if($setting === 'from_address') return 'staff@mailpoet.com'; - return Setting::where('name', $setting) - ->findOne()->value; } } \ No newline at end of file diff --git a/lib/Router/Newsletters.php b/lib/Router/Newsletters.php index 62f2d35efe..7cce1e469d 100644 --- a/lib/Router/Newsletters.php +++ b/lib/Router/Newsletters.php @@ -12,7 +12,7 @@ use MailPoet\Models\NewsletterSegment; use MailPoet\Models\NewsletterOptionField; use MailPoet\Models\NewsletterOption; use MailPoet\Newsletter\Renderer\Renderer; -use MailPoet\Models\Queue as SendingQueue; +use MailPoet\Models\SendingQueue; if(!defined('ABSPATH')) exit; @@ -231,6 +231,7 @@ class Newsletters { // get queue $queue = SendingQueue::where('newsletter_id', $item['id']) + ->orderByDesc('updated_at') ->findOne(); $item['queue'] = ($queue !== false) ? $queue->asArray() : null; } diff --git a/lib/Router/Queue.php b/lib/Router/Queue.php index 9c2112c367..4a2ae14b3b 100644 --- a/lib/Router/Queue.php +++ b/lib/Router/Queue.php @@ -1,10 +1,8 @@ bootStrap()); } function addQueue($data) { + $queue = SendingQueue::where('newsletter_id', $data['newsletter_id']) + ->whereNull('status') + ->findArray(); + + !d($queue); + exit; $queue = SendingQueue::create(); $queue->newsletter_id = $data['newsletter_id']; + $subscriber_ids = array(); - $segments = Segment::whereIn('id', $data['segments'])->findMany(); + $segments = Segment::whereIn('id', $data['segments']) + ->findMany(); foreach($segments as $segment) { $subscriber_ids = array_merge($subscriber_ids, Helpers::arrayColumn( - $segment->subscribers()->findArray(), + $segment->subscribers() + ->findArray(), 'id' )); } diff --git a/lib/Router/SendingQueue.php b/lib/Router/SendingQueue.php new file mode 100644 index 0000000000..2cb67e0c89 --- /dev/null +++ b/lib/Router/SendingQueue.php @@ -0,0 +1,167 @@ +whereNull('status') + ->findArray(); + if(count($queue)) { + wp_send_json( + array( + 'result' => false, + 'errors' => array(__('Send operation is already in progress.')) + ) + ); + } + $queue = \MailPoet\Models\SendingQueue::create(); + $queue->newsletter_id = $data['newsletter_id']; + $subscriber_ids = array(); + $segments = Segment::whereIn('id', $data['segments']) + ->findMany(); + + foreach($segments as $segment) { + $subscriber_ids = array_merge($subscriber_ids, Helpers::arrayColumn( + $segment->subscribers() + ->findArray(), + 'id' + )); + } + $subscriber_ids = array_unique($subscriber_ids); + $queue->subscribers = json_encode( + array( + 'to_process' => $subscriber_ids + ) + ); + $queue->count_total = $queue->count_to_process = count($subscriber_ids); + $queue->save(); + wp_send_json( + !$queue->save() ? + array( + 'result' => false, + 'errors' => array(__('Queue could not be created.')) + ) : + array( + 'result' => true, + 'data' => array($queue->id) + ) + ); + } + + function addQueues($data) { + $newsletterIds = Helpers::arrayColumn($data, 'newsletter_id'); + $queues = \MailPoet\Models\SendingQueue::whereIn('newsletter_id', $newsletterIds) + ->whereNull('status') + ->findArray(); + if(count($queues)) { + wp_send_json( + array( + 'result' => false, + 'errors' => array(__('Send operation is already in progress.')) + ) + ); + } + $result = array_map(function ($queueData) { + $queue = \MailPoet\Models\SendingQueue::create(); + $queue->newsletter_id = $queueData['newsletter_id']; + $queue->subscribers = json_encode( + array( + 'to_process' => $queueData['subscribers'] + ) + ); + $queue->count_total = $queue->count_to_process = count($queueData['subscribers']); + $queue->save(); + return array( + 'newsletter_id' => $queue->newsletter_id, + 'queue_id' => $queue->id + ); + }, $data); + $result = Helpers::arrayColumn($result, 'queue_id', 'newsletter_id'); + wp_send_json( + count($data) != count($result) ? + array( + 'result' => false, + 'errors' => array(__('Some queues could not be created.')), + 'data' => $result + ) : + array( + 'result' => true, + 'data' => $result + ) + ); + } + + function deleteQueue($data) { + $queue = \MailPoet\Models\SendingQueue::whereNull('deleted_at') + ->findOne($data['queue_id']); + if(!$queue) { + wp_send_json( + array( + 'result' => false, + 'errors' => array(__('Queue not found.')) + ) + ); + } + $queue->deleted_at = 'Y-m-d H:i:s'; + $queue->save(); + wp_send_json(array('result' => true)); + } + + function deleteQueues($data) { + $queues = \MailPoet\Models\SendingQueue::whereNull('deleted_at') + ->whereIn('id', $data['queue_ids']) + ->findResultSet(); + if(!$queues->count()) { + wp_send_json( + array( + 'result' => false, + 'errors' => array(__('Queues not found.')) + ) + ); + } + foreach($queues as $queue) { + $queue->deleted_at = 'Y-m-d H:i:s'; + $queue->save(); + } + wp_send_json(array('result' => true)); + } + + function getQueueStatus($data) { + $queue = \MailPoet\Models\SendingQueue::whereNull('deleted_at') + ->findOne($data['queue_id']) + ->asArray(); + wp_send_json( + !$queue ? + array( + 'result' => false, + 'errors' => array(__('Queue not found.')) + ) : + array( + 'result' => true, + 'data' => $queue + ) + ); + } + + function getQueuesStatus($data) { + $queues = \MailPoet\Models\SendingQueue::whereNull('deleted_at') + ->whereIn('id', $data['queue_ids']) + ->findArray(); + wp_send_json( + !$queues ? + array( + 'result' => false, + 'errors' => array(__('Queue not found.')) + ) : + array( + 'result' => true, + 'data' => $queues + ) + ); + } +} \ No newline at end of file diff --git a/lib/Subscribers/ImportExport/Import/Import.php b/lib/Subscribers/ImportExport/Import/Import.php index 70e57dfb74..438eda2a80 100644 --- a/lib/Subscribers/ImportExport/Import/Import.php +++ b/lib/Subscribers/ImportExport/Import/Import.php @@ -63,7 +63,7 @@ class Import { ); } } - } catch (\PDOException $e) { + } catch(\PDOException $e) { return array( 'result' => false, 'error' => $e->getMessage() @@ -145,7 +145,7 @@ class Import { ); if(!$existingTrashedRecords) return; $existingTrashedRecords = Helpers::flattenArray($existingTrashedRecords); - foreach (array_chunk($existingTrashedRecords, 200) as $subscriberIds) { + foreach(array_chunk($existingTrashedRecords, 200) as $subscriberIds) { Subscriber::whereIn('id', $subscriberIds) ->deleteMany(); SubscriberSegment::whereIn('subscriber_id', $subscriberIds) @@ -247,7 +247,7 @@ class Import { }, $subscriberFields); }, range(0, $subscribersCount)); $currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime; - foreach (array_chunk($subscribers, 200) as $data) { + foreach(array_chunk($subscribers, 100) as $data) { if($action == 'create') { Subscriber::createMultiple( $subscriberFields, @@ -310,7 +310,7 @@ class Import { ); }, $count, $subscribersData[$column]); }, $subscriberCustomFields)[0]; - foreach (array_chunk($subscribers, 200) as $data) { + foreach(array_chunk($subscribers, 200) as $data) { if($action === 'create') { SubscriberCustomField::createMultiple( $data @@ -325,7 +325,7 @@ class Import { } function addSubscribersToSegments($subscribers, $segments) { - foreach (array_chunk($subscribers, 200) as $data) { + foreach(array_chunk($subscribers, 200) as $data) { SubscriberSegment::createMultiple($segments, $data); } } diff --git a/tests/unit/Mailer/API/AmazonSESCest.php b/tests/unit/Mailer/API/AmazonSESCest.php index bce41944a0..219ac64917 100644 --- a/tests/unit/Mailer/API/AmazonSESCest.php +++ b/tests/unit/Mailer/API/AmazonSESCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\API\AmazonSES; class AmazonSESCest { function _before() { $this->settings = array( - 'name' => 'AmazonSES', + 'method' => 'AmazonSES', 'type' => 'API', 'access_key' => 'AKIAJM6Y5HMGXBLDNSRA', 'secret_key' => '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', diff --git a/tests/unit/Mailer/API/ElasticEmailCest.php b/tests/unit/Mailer/API/ElasticEmailCest.php index 112df03fce..047a12f306 100644 --- a/tests/unit/Mailer/API/ElasticEmailCest.php +++ b/tests/unit/Mailer/API/ElasticEmailCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\API\ElasticEmail; class ElasticEmailCest { function _before() { $this->settings = array( - 'name' => 'ElasticEmail', + 'method' => 'ElasticEmail', 'type' => 'API', 'api_key' => '997f1f7f-41de-4d7f-a8cb-86c8481370fa' ); diff --git a/tests/unit/Mailer/API/MailGunCest.php b/tests/unit/Mailer/API/MailGunCest.php index 85df8e45d1..6e3a5a8e68 100644 --- a/tests/unit/Mailer/API/MailGunCest.php +++ b/tests/unit/Mailer/API/MailGunCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\API\MailGun; class MailGunCest { function _before() { $this->settings = array( - 'name' => 'MailGun', + 'method' => 'MailGun', 'type' => 'API', 'api_key' => 'key-6cf5g5qjzenk-7nodj44gdt8phe6vam2', 'domain' => 'mrcasual.com' diff --git a/tests/unit/Mailer/API/MandrillCest.php b/tests/unit/Mailer/API/MandrillCest.php index be3998d153..38775ca2d5 100644 --- a/tests/unit/Mailer/API/MandrillCest.php +++ b/tests/unit/Mailer/API/MandrillCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\API\Mandrill; class MandrillCest { function _before() { $this->settings = array( - 'name' => 'Mandrill', + 'method' => 'Mandrill', 'type' => 'API', 'api_key' => '692ys1B7REEoZN7R-dYwNA' ); diff --git a/tests/unit/Mailer/API/SendGridCest.php b/tests/unit/Mailer/API/SendGridCest.php index 720dc8d632..77bb636ff0 100644 --- a/tests/unit/Mailer/API/SendGridCest.php +++ b/tests/unit/Mailer/API/SendGridCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\API\SendGrid; class SendGridCest { function _before() { $this->settings = array( - 'name' => 'SendGrid', + 'method' => 'SendGrid', 'type' => 'API', 'api_key' => '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' ); diff --git a/tests/unit/Mailer/MailPoetCest.php b/tests/unit/Mailer/MailPoetCest.php index 999e387050..24eb6e0bea 100644 --- a/tests/unit/Mailer/MailPoetCest.php +++ b/tests/unit/Mailer/MailPoetCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\MailPoet; class MailPoetCest { function _before() { $this->settings = array( - 'name' => 'MailPoet', + 'method' => 'MailPoet', 'api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU' ); $this->fromEmail = 'staff@mailpoet.com'; diff --git a/tests/unit/Mailer/SMTPCest.php b/tests/unit/Mailer/SMTPCest.php index 7fd61532ad..9d3d4e492a 100644 --- a/tests/unit/Mailer/SMTPCest.php +++ b/tests/unit/Mailer/SMTPCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\SMTP; class SMTPCest { function _before() { $this->settings = array( - 'name' => 'SMTP', + 'method' => 'SMTP', 'host' => 'email-smtp.us-west-2.amazonaws.com', 'port' => 587, 'authentication' => array( diff --git a/tests/unit/Mailer/WPMailCest.php b/tests/unit/Mailer/WPMailCest.php index 68310a52a0..c8cfdbf686 100644 --- a/tests/unit/Mailer/WPMailCest.php +++ b/tests/unit/Mailer/WPMailCest.php @@ -5,7 +5,7 @@ use MailPoet\Mailer\WPMail; class WPMailCest { function _before() { $this->settings = array( - 'name' => 'WPMail' + 'method' => 'WPMail' ); $this->fromEmail = 'staff@mailpoet.com'; $this->fromName = 'Sender'; diff --git a/tests/unit/Router/MailerCest.php b/tests/unit/Router/MailerCest.php index 2073b0e762..55914ce061 100644 --- a/tests/unit/Router/MailerCest.php +++ b/tests/unit/Router/MailerCest.php @@ -43,8 +43,8 @@ class MailerCest { $mailer = $this->router->buildMailer(); $class = 'Mailpoet\\Mailer\\' . ((isset($this->router->mailer['type'])) ? - $this->router->mailer['type'] . '\\' . $this->router->mailer['name'] : - $this->router->mailer['name'] + $this->router->mailer['type'] . '\\' . $this->router->mailer['method'] : + $this->router->mailer['method'] ); expect($mailer instanceof $class)->true(); expect(method_exists($mailer, 'send'))->true(); @@ -52,7 +52,7 @@ class MailerCest { function itCanSend() { $newsletter = array( - 'subject' => 'testing Mailer router with ' . $this->router->mailer['name'], + 'subject' => 'testing Mailer router with ' . $this->router->mailer['method'], 'body' => array( 'html' => 'HTML body', 'text' => 'TEXT body' diff --git a/views/cron.html b/views/cron.html new file mode 100644 index 0000000000..a4548c1318 --- /dev/null +++ b/views/cron.html @@ -0,0 +1,10 @@ +<% extends 'layout.html' %> + +<% block content %> +
+
+
+ +<% endblock %> diff --git a/views/queue.html b/views/queue.html deleted file mode 100644 index 3a837d77c1..0000000000 --- a/views/queue.html +++ /dev/null @@ -1,13 +0,0 @@ -<% extends 'layout.html' %> - -<% block content %> -
-
-
-
- - -<% endblock %> diff --git a/webpack.config.js b/webpack.config.js index 7734b29453..86be7a7b05 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -103,7 +103,7 @@ config.push(_.extend({}, baseConfig, { 'subscribers/importExport/import.js', 'subscribers/importExport/export.js', 'helpscout', - 'queue.jsx' + 'cron.jsx' ], form_editor: [ 'form_editor/form_editor.js',