Merge pull request #564 from mailpoet/cron_update

Cron update
This commit is contained in:
Tautvidas Sipavičius
2016-08-08 16:59:27 +03:00
committed by GitHub
23 changed files with 366 additions and 309 deletions

View File

@ -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;
}
}
});

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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(),

View File

@ -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,

View File

@ -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
View 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');
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View 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();
}
}

View 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();
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -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
View 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'));
}
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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(
@ -130,7 +126,7 @@ class MailChimp {
}
function processError($error) {
switch ($error) {
switch($error) {
case 'API':
$errorMessage = __('Invalid API Key.');
break;

View File

@ -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 %>

View File

@ -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].")