@ -21,8 +21,6 @@ define(
|
||||
action: 'getStatus'
|
||||
})
|
||||
.done(function(response) {
|
||||
jQuery('.button-primary')
|
||||
.removeClass('disabled');
|
||||
if(response.status !== undefined) {
|
||||
this.setState(response);
|
||||
} else {
|
||||
@ -36,55 +34,26 @@ define(
|
||||
setInterval(this.getCronData, 5000);
|
||||
}
|
||||
},
|
||||
controlCron: function(action) {
|
||||
if(jQuery('.button-primary').hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
jQuery('.button-primary')
|
||||
.addClass('disabled');
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'cron',
|
||||
action: action,
|
||||
})
|
||||
.done(function(response) {
|
||||
if(!response.result) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('daemonControlError'));
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
render: function() {
|
||||
if(this.state.status === 'loading') {
|
||||
return(<div>{MailPoet.I18n.t('loadingDaemonStatus')}</div>);
|
||||
}
|
||||
switch(this.state.status) {
|
||||
case 'started':
|
||||
case 'loading':
|
||||
return(
|
||||
<div>
|
||||
{MailPoet.I18n.t('cronDaemonIsRunning')}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>{MailPoet.I18n.t('stop')}</a>
|
||||
{MailPoet.I18n.t('loadingDaemonStatus')}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'starting':
|
||||
case 'stopping':
|
||||
case false:
|
||||
return(
|
||||
<div>
|
||||
{MailPoet.I18n.t('daemonNotRunning')}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return(
|
||||
<div>
|
||||
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'stopped':
|
||||
return(
|
||||
<div>
|
||||
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
|
||||
<br />
|
||||
<br />
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>{MailPoet.I18n.t('start')}</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2,35 +2,15 @@
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Cron\Supervisor;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Cron {
|
||||
function start() {
|
||||
$supervisor = new Supervisor($force_run = true);
|
||||
return $supervisor->checkDaemon();
|
||||
}
|
||||
|
||||
function stop() {
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!$daemon || $daemon['status'] !== 'started') {
|
||||
$result = false;
|
||||
} else {
|
||||
$daemon['status'] = 'stopping';
|
||||
$result = CronHelper::saveDaemon($daemon);
|
||||
}
|
||||
return array(
|
||||
'result' => $result
|
||||
);
|
||||
}
|
||||
|
||||
function getStatus() {
|
||||
$daemon = Setting::where('name', 'cron_daemon')
|
||||
->findOne();
|
||||
$daemon = Setting::getValue(CronHelper::DAEMON_SETTING);
|
||||
return ($daemon) ?
|
||||
unserialize($daemon->value) :
|
||||
false;
|
||||
$daemon :
|
||||
array('status' => false);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Models;
|
||||
use MailPoet\Cron\Supervisor;
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use MailPoet\Router;
|
||||
use MailPoet\API;
|
||||
use MailPoet\WP\Notice as WPNotice;
|
||||
@ -107,7 +107,7 @@ class Initializer {
|
||||
$this->setupShortcodes();
|
||||
$this->setupHooks();
|
||||
$this->setupImages();
|
||||
$this->runQueueSupervisor();
|
||||
$this->setupCronTrigger();
|
||||
|
||||
$this->plugin_initialized = true;
|
||||
} catch(\Exception $e) {
|
||||
@ -192,14 +192,9 @@ class Initializer {
|
||||
$router->init();
|
||||
}
|
||||
|
||||
function runQueueSupervisor() {
|
||||
if(php_sapi_name() === 'cli') return;
|
||||
try {
|
||||
$supervisor = new Supervisor();
|
||||
$supervisor->checkDaemon();
|
||||
} catch(\Exception $e) {
|
||||
// Prevent Daemon exceptions from breaking out and breaking UI
|
||||
}
|
||||
function setupCronTrigger() {
|
||||
$cron_trigger = new CronTrigger();
|
||||
$cron_trigger->init();
|
||||
}
|
||||
|
||||
function setupImages() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use MailPoet\Form\Block;
|
||||
use MailPoet\Form\Renderer as FormRenderer;
|
||||
use MailPoet\Models\CustomField;
|
||||
@ -255,6 +256,7 @@ class Menu {
|
||||
$data = array(
|
||||
'settings' => $settings,
|
||||
'segments' => Segment::getPublic()->findArray(),
|
||||
'cron_trigger' => CronTrigger::getAvailableMethods(),
|
||||
'pages' => Pages::getAll(),
|
||||
'flags' => $flags,
|
||||
'current_user' => wp_get_current_user(),
|
||||
|
@ -10,6 +10,7 @@ use MailPoet\Config\PopulatorData\Templates\PostNotificationsBlank1Column;
|
||||
use MailPoet\Config\PopulatorData\Templates\WelcomeBlank1Column;
|
||||
use MailPoet\Config\PopulatorData\Templates\WelcomeBlank12Column;
|
||||
use MailPoet\Config\PopulatorData\Templates\SimpleText;
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Segments\WP;
|
||||
use \MailPoet\Models\Setting;
|
||||
@ -74,6 +75,13 @@ class Populator {
|
||||
private function createDefaultSettings() {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
if(!Setting::getValue(CronTrigger::SETTING_NAME)) {
|
||||
// disable task scheduler (cron) be default
|
||||
Setting::setValue(CronTrigger::SETTING_NAME, array(
|
||||
'method' => CronTrigger::DEFAULT_METHOD
|
||||
));
|
||||
}
|
||||
|
||||
// default sender info based on current user
|
||||
$sender = array(
|
||||
'name' => $current_user->display_name,
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use MailPoet\Router\Front as FrontRouter;
|
||||
use MailPoet\Router\Endpoints\Queue as QueueEndpoint;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Router\Endpoints\Queue as QueueEndpoint;
|
||||
use MailPoet\Router\Front as FrontRouter;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -12,6 +12,7 @@ class CronHelper {
|
||||
const DAEMON_EXECUTION_LIMIT = 20;
|
||||
const DAEMON_EXECUTION_TIMEOUT = 35;
|
||||
const DAEMON_REQUEST_TIMEOUT = 2;
|
||||
const DAEMON_SETTING = 'cron_daemon';
|
||||
|
||||
static function createDaemon($token) {
|
||||
$daemon = array(
|
||||
@ -22,18 +23,32 @@ class CronHelper {
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
static function restartDaemon($token) {
|
||||
return self::createDaemon($token);
|
||||
}
|
||||
|
||||
static function getDaemon() {
|
||||
return Setting::getValue('cron_daemon');
|
||||
return Setting::getValue(self::DAEMON_SETTING);
|
||||
}
|
||||
|
||||
static function saveDaemon($daemon) {
|
||||
$daemon['updated_at'] = time();
|
||||
return Setting::setValue(
|
||||
'cron_daemon',
|
||||
self::DAEMON_SETTING,
|
||||
$daemon
|
||||
);
|
||||
}
|
||||
|
||||
static function stopDaemon() {
|
||||
$daemon = self::getDaemon();
|
||||
$daemon['status'] = Daemon::STATUS_STOPPED;
|
||||
return self::saveDaemon($daemon);
|
||||
}
|
||||
|
||||
static function deleteDaemon() {
|
||||
return Setting::deleteValue(self::DAEMON_SETTING);
|
||||
}
|
||||
|
||||
static function createToken() {
|
||||
return Security::generateRandomString();
|
||||
}
|
||||
@ -46,6 +61,8 @@ class CronHelper {
|
||||
$data
|
||||
);
|
||||
$args = array(
|
||||
'blocking' => false,
|
||||
'sslverify' => false,
|
||||
'timeout' => $timeout,
|
||||
'user-agent' => 'MailPoet (www.mailpoet.com) Cron'
|
||||
);
|
||||
@ -70,7 +87,7 @@ class CronHelper {
|
||||
throw new \Exception(__('Site URL is unreachable.'));
|
||||
}
|
||||
|
||||
static function checkExecutionTimer($timer) {
|
||||
static function enforceExecutionLimit($timer) {
|
||||
$elapsed_time = microtime(true) - $timer;
|
||||
if($elapsed_time >= self::DAEMON_EXECUTION_LIMIT) {
|
||||
throw new \Exception(__('Maximum execution time has been reached.'));
|
||||
|
41
lib/Cron/CronTrigger.php
Normal file
41
lib/Cron/CronTrigger.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class CronTrigger {
|
||||
public $current_method;
|
||||
public static $available_methods = array(
|
||||
'mailpoet' => 'MailPoet',
|
||||
'wordpress' => 'WordPress'
|
||||
);
|
||||
const DEFAULT_METHOD = 'WordPress';
|
||||
const SETTING_NAME = 'cron_trigger';
|
||||
|
||||
function __construct() {
|
||||
$this->current_method = self::getCurrentMethod();
|
||||
}
|
||||
|
||||
function init() {
|
||||
try {
|
||||
// configure cron trigger only outside of cli environment
|
||||
if(php_sapi_name() === 'cli') return;
|
||||
$trigger_class = __NAMESPACE__ . '\Triggers\\' . $this->current_method;
|
||||
return (class_exists($trigger_class)) ?
|
||||
$trigger_class::run() :
|
||||
false;
|
||||
} catch(\Exception $e) {
|
||||
// cron exceptions should not prevent the rest of the site from loading
|
||||
}
|
||||
}
|
||||
|
||||
static function getAvailableMethods() {
|
||||
return self::$available_methods;
|
||||
}
|
||||
|
||||
static function getCurrentMethod() {
|
||||
return Setting::getValue(self::SETTING_NAME . '.method');
|
||||
}
|
||||
}
|
@ -78,15 +78,16 @@ class Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
function abortWithError($message) {
|
||||
exit('[mailpoet_cron_error:' . base64_encode($message) . ']');
|
||||
}
|
||||
|
||||
function callSelf() {
|
||||
CronHelper::accessDaemon($this->token, self::REQUEST_TIMEOUT);
|
||||
$this->terminateRequest();
|
||||
}
|
||||
|
||||
function abortWithError($message) {
|
||||
status_header(404, $message);
|
||||
exit;
|
||||
}
|
||||
|
||||
function terminateRequest() {
|
||||
exit;
|
||||
}
|
||||
|
@ -6,87 +6,35 @@ if(!defined('ABSPATH')) exit;
|
||||
class Supervisor {
|
||||
public $daemon;
|
||||
public $token;
|
||||
public $force_run;
|
||||
|
||||
function __construct($force_run = false) {
|
||||
$this->daemon = CronHelper::getDaemon();
|
||||
function __construct() {
|
||||
$this->token = CronHelper::createToken();
|
||||
$this->force_run = $force_run;
|
||||
$this->daemon = $this->getDaemon();
|
||||
}
|
||||
|
||||
function checkDaemon() {
|
||||
$daemon = $this->daemon;
|
||||
if(!$daemon) {
|
||||
$daemon = CronHelper::createDaemon($this->token);
|
||||
return $this->runDaemon($daemon);
|
||||
}
|
||||
// if the daemon is stopped, return its status and do nothing
|
||||
if(!$this->force_run &&
|
||||
isset($daemon['status']) &&
|
||||
$daemon['status'] === Daemon::STATUS_STOPPED
|
||||
) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
$elapsed_time = time() - (int)$daemon['updated_at'];
|
||||
// if it's been less than 40 seconds since last execution and we're not
|
||||
// force-running the daemon, return its status and do nothing
|
||||
if($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT && !$this->force_run) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
} elseif($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT &&
|
||||
$this->force_run &&
|
||||
in_array($daemon['status'], array(
|
||||
Daemon::STATUS_STOPPING,
|
||||
Daemon::STATUS_STARTING
|
||||
))
|
||||
) {
|
||||
// if it's been less than 40 seconds since last execution, we are
|
||||
// force-running the daemon and it's either being started or stopped,
|
||||
// return its status and do nothing
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
// re-create (restart) daemon
|
||||
CronHelper::createDaemon($this->token);
|
||||
$execution_timeout_exceeded =
|
||||
(time() - (int)$daemon['updated_at']) > CronHelper::DAEMON_EXECUTION_TIMEOUT;
|
||||
if($execution_timeout_exceeded) {
|
||||
CronHelper::restartDaemon($this->token);
|
||||
return $this->runDaemon();
|
||||
}
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
function runDaemon() {
|
||||
$request = CronHelper::accessDaemon($this->token);
|
||||
preg_match('/\[(mailpoet_cron_error:.*?)\]/i', $request, $status);
|
||||
CronHelper::accessDaemon($this->token);
|
||||
$daemon = CronHelper::getDaemon();
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
function getDaemon() {
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!empty($status) || !$daemon) {
|
||||
if(!$daemon) {
|
||||
$message = __('Daemon failed to run.');
|
||||
} else {
|
||||
list(, $message) = explode(':', $status[0]);
|
||||
$message = base64_decode($message);
|
||||
CronHelper::createDaemon($this->token);
|
||||
return $this->runDaemon();
|
||||
}
|
||||
return $this->formatResultMessage(
|
||||
false,
|
||||
$message
|
||||
);
|
||||
}
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
|
||||
private function formatDaemonStatusMessage($status) {
|
||||
return $this->formatResultMessage(
|
||||
true,
|
||||
sprintf(
|
||||
__('Daemon is currently %s.'),
|
||||
__($status)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function formatResultMessage($result, $message) {
|
||||
$formattedResult = array(
|
||||
'result' => $result
|
||||
);
|
||||
if(!$result) {
|
||||
$formattedResult['errors'] = array($message);
|
||||
} else {
|
||||
$formattedResult['message'] = $message;
|
||||
}
|
||||
return $formattedResult;
|
||||
return $daemon;
|
||||
}
|
||||
}
|
13
lib/Cron/Triggers/MailPoet.php
Normal file
13
lib/Cron/Triggers/MailPoet.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Triggers;
|
||||
|
||||
use MailPoet\Cron\Supervisor;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class MailPoet {
|
||||
static function run() {
|
||||
$supervisor = new Supervisor();
|
||||
return $supervisor->checkDaemon();
|
||||
}
|
||||
}
|
31
lib/Cron/Triggers/WordPress.php
Normal file
31
lib/Cron/Triggers/WordPress.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Triggers;
|
||||
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Cron\Workers\Scheduler as SchedulerWorker;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class WordPress {
|
||||
static function run() {
|
||||
return (self::checkExecutionRequirements()) ?
|
||||
MailPoet::run() :
|
||||
self::cleanup();
|
||||
}
|
||||
|
||||
static function checkExecutionRequirements() {
|
||||
$scheduled_queues = SchedulerWorker::getScheduledQueues();
|
||||
$running_queues = SendingQueueWorker::getRunningQueues();
|
||||
$sending_limit_reached = MailerLog::isSendingLimitReached();
|
||||
return (($scheduled_queues || $running_queues) && !$sending_limit_reached);
|
||||
}
|
||||
|
||||
static function cleanup() {
|
||||
$cron_daemon = CronHelper::getDaemon();
|
||||
if($cron_daemon) {
|
||||
CronHelper::deleteDaemon();
|
||||
}
|
||||
}
|
||||
}
|
@ -20,13 +20,12 @@ class Scheduler {
|
||||
|
||||
function __construct($timer = false) {
|
||||
$this->timer = ($timer) ? $timer : microtime(true);
|
||||
CronHelper::checkExecutionTimer($this->timer);
|
||||
// abort if execution limit is reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
|
||||
function process() {
|
||||
$scheduled_queues = SendingQueue::where('status', 'scheduled')
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->findMany();
|
||||
$scheduled_queues = self::getScheduledQueues();
|
||||
if(!count($scheduled_queues)) return;
|
||||
foreach($scheduled_queues as $i => $queue) {
|
||||
$newsletter = Newsletter::filter('filterWithOptions')
|
||||
@ -40,7 +39,7 @@ class Scheduler {
|
||||
} elseif($newsletter->type === 'standard') {
|
||||
$this->processScheduledStandardNewsletter($newsletter, $queue);
|
||||
}
|
||||
CronHelper::checkExecutionTimer($this->timer);
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,4 +184,10 @@ class Scheduler {
|
||||
$notification_history :
|
||||
false;
|
||||
}
|
||||
|
||||
static function getScheduledQueues() {
|
||||
return SendingQueue::where('status', 'scheduled')
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->findMany();
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Mailer as MailerTask;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterTask;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Subscribers as SubscribersTask;
|
||||
use MailPoet\Models\Newsletter as NewsletterModel;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||
use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel;
|
||||
use MailPoet\Models\Subscriber as SubscriberModel;
|
||||
@ -23,11 +23,14 @@ class SendingQueue {
|
||||
$this->mailer_task = new MailerTask();
|
||||
$this->newsletter_task = new NewsletterTask();
|
||||
$this->timer = ($timer) ? $timer : microtime(true);
|
||||
// abort if execution or sending limit are reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
|
||||
function process() {
|
||||
$this->mailer_task->checkSendingLimit();
|
||||
foreach($this->getQueues() as $queue) {
|
||||
foreach(self::getRunningQueues() as $queue) {
|
||||
// abort if sending limit is reached
|
||||
MailerLog::enforceSendingLimit();
|
||||
// get and pre-process newsletter (render, replace shortcodes/links, etc.)
|
||||
$newsletter = $this->newsletter_task->getAndPreProcess($queue->asArray());
|
||||
if(!$newsletter) {
|
||||
@ -67,6 +70,11 @@ class SendingQueue {
|
||||
$newsletter,
|
||||
$found_subscribers
|
||||
);
|
||||
if($queue->status === SendingQueueModel::STATUS_COMPLETED) {
|
||||
$this->newsletter_task->markNewsletterAsSent($queue->newsletter_id);
|
||||
}
|
||||
// abort if execution limit is reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,6 +149,7 @@ class SendingQueue {
|
||||
$prepared_subscribers_ids,
|
||||
$queue->subscribers
|
||||
);
|
||||
$queue = $this->updateQueue($queue);
|
||||
} else {
|
||||
// update processed/to process list
|
||||
$queue->subscribers = SubscribersTask::updateProcessedList(
|
||||
@ -149,19 +158,18 @@ class SendingQueue {
|
||||
);
|
||||
// log statistics
|
||||
StatisticsNewslettersModel::createMultiple($statistics);
|
||||
// keep track of sent items
|
||||
$this->mailer_task->updateMailerLog();
|
||||
$subscribers_to_process_count = count($queue->subscribers['to_process']);
|
||||
}
|
||||
// update the sent count
|
||||
$this->mailer_task->updateSentCount();
|
||||
$queue = $this->updateQueue($queue);
|
||||
if($subscribers_to_process_count) {
|
||||
$this->mailer_task->checkSendingLimit();
|
||||
// enforce sending limit if there are still subscribers left to process
|
||||
if($queue->count_to_process) {
|
||||
MailerLog::enforceSendingLimit();
|
||||
}
|
||||
}
|
||||
CronHelper::checkExecutionTimer($this->timer);
|
||||
return $queue;
|
||||
}
|
||||
|
||||
function getQueues() {
|
||||
static function getRunningQueues() {
|
||||
return SendingQueueModel::orderByDesc('priority')
|
||||
->whereNull('deleted_at')
|
||||
->whereNull('status')
|
||||
@ -178,13 +186,6 @@ class SendingQueue {
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = current_time('mysql');
|
||||
$queue->status = SendingQueueModel::STATUS_COMPLETED;
|
||||
// if it's a standard or post notificaiton newsletter, update its status to sent
|
||||
$newsletter = NewsletterModel::findOne($queue->newsletter_id);
|
||||
if($newsletter->type === NewsletterModel::TYPE_STANDARD ||
|
||||
$newsletter->type === NewsletterModel::TYPE_NOTIFICATION_HISTORY
|
||||
) {
|
||||
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
|
||||
}
|
||||
}
|
||||
return $queue->save();
|
||||
}
|
||||
|
@ -2,18 +2,14 @@
|
||||
namespace MailPoet\Cron\Workers\SendingQueue\Tasks;
|
||||
|
||||
use MailPoet\Mailer\Mailer as MailerFactory;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Mailer {
|
||||
public $mta_config;
|
||||
public $mta_log;
|
||||
public $mailer;
|
||||
|
||||
function __construct() {
|
||||
$this->mta_config = $this->getMailerConfig();
|
||||
$this->mta_log = $this->getMailerLog();
|
||||
$this->mailer = $this->configureMailer();
|
||||
}
|
||||
|
||||
@ -40,33 +36,16 @@ class Mailer {
|
||||
return $this->mailer;
|
||||
}
|
||||
|
||||
function getMailerConfig() {
|
||||
$mta_config = Setting::getValue('mta');
|
||||
if(!$mta_config) {
|
||||
throw new \Exception(__('Mailer is not configured'));
|
||||
}
|
||||
return $mta_config;
|
||||
}
|
||||
|
||||
function getMailerLog() {
|
||||
$mta_log = Setting::getValue('mta_log');
|
||||
if(!$mta_log) {
|
||||
$mta_log = array(
|
||||
'sent' => 0,
|
||||
'started' => time()
|
||||
);
|
||||
Setting::setValue('mta_log', $mta_log);
|
||||
}
|
||||
return $mta_log;
|
||||
return MailerLog::getMailerLog();
|
||||
}
|
||||
|
||||
function updateMailerLog() {
|
||||
$this->mta_log['sent']++;
|
||||
Setting::setValue('mta_log', $this->mta_log);
|
||||
function updateSentCount() {
|
||||
return MailerLog::incrementSentCount();
|
||||
}
|
||||
|
||||
function getProcessingMethod() {
|
||||
return ($this->mta_config['method'] === 'MailPoet') ?
|
||||
return ($this->mailer->mailer_config['method'] === MailerFactory::METHOD_MAILPOET) ?
|
||||
'bulk' :
|
||||
'individual';
|
||||
}
|
||||
@ -81,23 +60,4 @@ class Mailer {
|
||||
$prepared_subscribers
|
||||
);
|
||||
}
|
||||
|
||||
function checkSendingLimit() {
|
||||
if($this->mta_config['method'] === 'MailPoet') return;
|
||||
$frequency_interval = (int)$this->mta_config['frequency']['interval'] * 60;
|
||||
$frequency_limit = (int)$this->mta_config['frequency']['emails'];
|
||||
$elapsed_time = time() - (int)$this->mta_log['started'];
|
||||
if($this->mta_log['sent'] === $frequency_limit &&
|
||||
$elapsed_time <= $frequency_interval
|
||||
) {
|
||||
throw new \Exception(__('Sending frequency limit has been reached'));
|
||||
}
|
||||
if($elapsed_time > $frequency_interval) {
|
||||
$this->mta_log = array(
|
||||
'sent' => 0,
|
||||
'started' => time()
|
||||
);
|
||||
Setting::setValue('mta_log', $this->mta_log);
|
||||
}
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@ class Newsletter {
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
function render($newsletter) {
|
||||
function render(array $newsletter) {
|
||||
$renderer = new Renderer($newsletter);
|
||||
$newsletter['rendered_body'] = $renderer->render();
|
||||
return $newsletter;
|
||||
@ -103,4 +103,12 @@ class Newsletter {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function markNewsletterAsSent($newsletter_id) {
|
||||
$newsletter = NewsletterModel::findOne($newsletter_id);
|
||||
// if it's a standard newsletter, update its status
|
||||
if($newsletter->type === NewsletterModel::TYPE_STANDARD) {
|
||||
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Workers\SendingQueue\Tasks;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\Newsletter as NewsletterModel;
|
||||
use MailPoet\Models\NewsletterPost;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -19,7 +19,7 @@ class Posts {
|
||||
if(!count($matched_posts_ids)) {
|
||||
return $newsletter;
|
||||
}
|
||||
$newsletter_id = ($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) ?
|
||||
$newsletter_id = ($newsletter['type'] === NewsletterModel::TYPE_NOTIFICATION_HISTORY) ?
|
||||
$newsletter['parent_id'] :
|
||||
$newsletter['id'];
|
||||
foreach($matched_posts_ids as $post_id) {
|
||||
|
@ -8,13 +8,22 @@ require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Mailer {
|
||||
public $mailer;
|
||||
public $mailer_config;
|
||||
public $sender;
|
||||
public $reply_to;
|
||||
public $mailer_instance;
|
||||
const MAILER_CONFIG = 'mta';
|
||||
const SENDING_LIMIT_INTERVAL_MULTIPLIER = 60;
|
||||
const METHOD_MAILPOET = 'MailPoet';
|
||||
const METHOD_MAILGUN = 'MailGun';
|
||||
const METHOD_ELASTICEMAIL = 'ElasticEmail';
|
||||
const METHOD_AMAZONSES = 'AmazonSES';
|
||||
const METHOD_SENDGRID = 'SendGrid';
|
||||
const METHOD_PHPMAIL = 'PHPMail';
|
||||
const METHOD_SMTP = 'SMTP';
|
||||
|
||||
function __construct($mailer = false, $sender = false, $reply_to = false) {
|
||||
$this->mailer = $this->getMailer($mailer);
|
||||
$this->mailer_config = self::getMailerConfig($mailer);
|
||||
$this->sender = $this->getSender($sender);
|
||||
$this->reply_to = $this->getReplyTo($reply_to);
|
||||
$this->mailer_instance = $this->buildMailer();
|
||||
@ -26,59 +35,59 @@ class Mailer {
|
||||
}
|
||||
|
||||
function buildMailer() {
|
||||
switch($this->mailer['method']) {
|
||||
case 'AmazonSES':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['region'],
|
||||
$this->mailer['access_key'],
|
||||
$this->mailer['secret_key'],
|
||||
switch($this->mailer_config['method']) {
|
||||
case self::METHOD_AMAZONSES:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['region'],
|
||||
$this->mailer_config['access_key'],
|
||||
$this->mailer_config['secret_key'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'ElasticEmail':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['api_key'],
|
||||
case self::METHOD_ELASTICEMAIL:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['api_key'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'MailGun':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['domain'],
|
||||
$this->mailer['api_key'],
|
||||
case self::METHOD_MAILGUN:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['domain'],
|
||||
$this->mailer_config['api_key'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'MailPoet':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['mailpoet_api_key'],
|
||||
case self::METHOD_MAILPOET:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['mailpoet_api_key'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'SendGrid':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['api_key'],
|
||||
case self::METHOD_SENDGRID:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['api_key'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'PHPMail':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
case self::METHOD_PHPMAIL:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
break;
|
||||
case 'SMTP':
|
||||
$mailer_instance = new $this->mailer['class'](
|
||||
$this->mailer['host'],
|
||||
$this->mailer['port'],
|
||||
$this->mailer['authentication'],
|
||||
$this->mailer['login'],
|
||||
$this->mailer['password'],
|
||||
$this->mailer['encryption'],
|
||||
case self::METHOD_SMTP:
|
||||
$mailer_instance = new $this->mailer_config['class'](
|
||||
$this->mailer_config['host'],
|
||||
$this->mailer_config['port'],
|
||||
$this->mailer_config['authentication'],
|
||||
$this->mailer_config['login'],
|
||||
$this->mailer_config['password'],
|
||||
$this->mailer_config['encryption'],
|
||||
$this->sender,
|
||||
$this->reply_to
|
||||
);
|
||||
@ -89,12 +98,20 @@ class Mailer {
|
||||
return $mailer_instance;
|
||||
}
|
||||
|
||||
function getMailer($mailer = false) {
|
||||
static function getMailerConfig($mailer = false) {
|
||||
if(!$mailer) {
|
||||
$mailer = Setting::getValue('mta');
|
||||
$mailer = Setting::getValue(self::MAILER_CONFIG);
|
||||
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured'));
|
||||
}
|
||||
if(empty($mailer['frequency'])) {
|
||||
$default_settings = Setting::getDefaults();
|
||||
$mailer['frequency'] = $default_settings['mta']['frequency'];
|
||||
}
|
||||
// add additional variables to the mailer object
|
||||
$mailer['class'] = 'MailPoet\\Mailer\\Methods\\' . $mailer['method'];
|
||||
$mailer['frequency_interval'] =
|
||||
(int)$mailer['frequency']['interval'] * self::SENDING_LIMIT_INTERVAL_MULTIPLIER;
|
||||
$mailer['frequency_limit'] = (int)$mailer['frequency']['emails'];
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
|
60
lib/Mailer/MailerLog.php
Normal file
60
lib/Mailer/MailerLog.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace MailPoet\Mailer;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class MailerLog {
|
||||
const SETTING_NAME = 'mta_log';
|
||||
|
||||
static function getMailerLog() {
|
||||
$mailer_log = Setting::getValue(self::SETTING_NAME);
|
||||
if(!$mailer_log) {
|
||||
$mailer_log = self::createMailerLog();
|
||||
}
|
||||
return $mailer_log;
|
||||
}
|
||||
|
||||
static function createMailerLog() {
|
||||
$mailer_log = array(
|
||||
'sent' => 0,
|
||||
'started' => time()
|
||||
);
|
||||
Setting::setValue(self::SETTING_NAME, $mailer_log);
|
||||
return $mailer_log;
|
||||
}
|
||||
|
||||
static function resetMailerLog() {
|
||||
return self::createMailerLog();
|
||||
}
|
||||
|
||||
static function updateMailerLog($mailer_log) {
|
||||
Setting::setValue(self::SETTING_NAME, $mailer_log);
|
||||
return $mailer_log;
|
||||
}
|
||||
|
||||
static function incrementSentCount($mailer_log = false) {
|
||||
$mailer_log = ($mailer_log) ? $mailer_log : self::getMailerLog();
|
||||
(int)$mailer_log['sent']++;
|
||||
return self::updateMailerLog($mailer_log);
|
||||
}
|
||||
|
||||
static function isSendingLimitReached() {
|
||||
$mailer_config = Mailer::getMailerConfig();
|
||||
$mailer_log = self::getMailerLog();
|
||||
$elapsed_time = time() - (int)$mailer_log['started'];
|
||||
if($mailer_log['sent'] === $mailer_config['frequency_limit']) {
|
||||
if($elapsed_time <= $mailer_config['frequency_interval']) return true;
|
||||
// reset mailer log if enough time has passed since the limit was reached
|
||||
self::resetMailerLog();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function enforceSendingLimit() {
|
||||
if(self::isSendingLimitReached()) {
|
||||
throw new \Exception(__('Sending frequency limit has been reached'));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Setting extends Model {
|
||||
@ -38,8 +40,8 @@ class Setting extends Model {
|
||||
'interval' => self::DEFAULT_SENDING_FREQUENCY_INTERVAL
|
||||
)
|
||||
),
|
||||
'task_scheduler' => array(
|
||||
'method' => 'WordPress'
|
||||
CronTrigger::SETTING_NAME => array(
|
||||
'method' => CronTrigger::DEFAULT_METHOD
|
||||
),
|
||||
'signup_confirmation' => array(
|
||||
'enabled' => true,
|
||||
@ -160,4 +162,9 @@ class Setting extends Model {
|
||||
|
||||
return $setting->save();
|
||||
}
|
||||
|
||||
public static function deleteValue($value) {
|
||||
$value = self::where('name', $value)->findOne();
|
||||
return ($value) ? $value->delete() : false;
|
||||
}
|
||||
}
|
@ -130,7 +130,7 @@ class Links {
|
||||
|
||||
static function save(array $links, $newsletter_id, $queue_id) {
|
||||
foreach($links as $link) {
|
||||
if(empty($link['hash'] || empty($link['url']))) continue;
|
||||
if(empty($link['hash']) || empty($link['url'])) continue;
|
||||
$newsletter_link = NewsletterLink::create();
|
||||
$newsletter_link->newsletter_id = $newsletter_id;
|
||||
$newsletter_link->queue_id = $queue_id;
|
||||
|
@ -66,7 +66,7 @@ class MailChimp {
|
||||
$connection = @fopen($url, 'r');
|
||||
if(!$connection) {
|
||||
return $this->processError('connection');
|
||||
} else {
|
||||
}
|
||||
$i = 0;
|
||||
$header = array();
|
||||
while(!feof($connection)) {
|
||||
@ -90,20 +90,16 @@ class MailChimp {
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
$bytes_fetched += strlen($buffer);
|
||||
if($bytes_fetched > $this->max_post_size) {
|
||||
return $this->processError('size');
|
||||
|
||||
}
|
||||
}
|
||||
fclose($connection);
|
||||
}
|
||||
}
|
||||
|
||||
if(!count($subscribers)) {
|
||||
return $this->processError('subscribers');
|
||||
|
||||
}
|
||||
|
||||
return array(
|
||||
|
@ -6,11 +6,10 @@
|
||||
|
||||
<% block translations %>
|
||||
<%= localize({
|
||||
'daemonNotRunning': __('Daemon is not running'),
|
||||
'daemonControlError': __('Cron daemon error'),
|
||||
'loadingDaemonStatus': __('Loading daemon status...'),
|
||||
'cronDaemonIsRunning': __('Cron daemon is running.'),
|
||||
'stop': __('Stop'),
|
||||
'start': __('Start'),
|
||||
'cronDaemonIsRunning': __('Cron daemon is running'),
|
||||
'cronDaemonState': __('Cron is %$1s')
|
||||
}) %>
|
||||
<% endblock %>
|
||||
|
@ -39,10 +39,9 @@
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="task_scheduler[method]"
|
||||
value="WordPress"
|
||||
<% if not(settings.task_scheduler.method)
|
||||
or (settings.task_scheduler.method == 'WordPress') %>
|
||||
name="cron_trigger[method]"
|
||||
value="<%= cron_trigger.wordpress %>"
|
||||
<% if (settings.cron_trigger.method == cron_trigger.wordpress) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __('Visitors to your website (recommended)') %>
|
||||
@ -52,9 +51,9 @@
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="task_scheduler[method]"
|
||||
value="MailPoet"
|
||||
<% if (settings.task_scheduler.method == 'MailPoet') %>
|
||||
name="cron_trigger[method]"
|
||||
value="<%= cron_trigger.mailpoet %>"
|
||||
<% if (settings.cron_trigger.method == cron_trigger.mailpoet) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __("MailPoet's own script. Doesn't work with [link]these hosts[/link].")
|
||||
|
Reference in New Issue
Block a user