diff --git a/assets/js/src/cron.jsx b/assets/js/src/cron.jsx index 0691489352..1f3dcb3183 100644 --- a/assets/js/src/cron.jsx +++ b/assets/js/src/cron.jsx @@ -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(
{MailPoet.I18n.t('loadingDaemonStatus')}
); - } switch(this.state.status) { - case 'started': + case 'loading': return(
- {MailPoet.I18n.t('cronDaemonIsRunning')} -
-
- {MailPoet.I18n.t('stop')} + {MailPoet.I18n.t('loadingDaemonStatus')}
); - break; - case 'starting': - case 'stopping': + case false: + return( +
+ {MailPoet.I18n.t('daemonNotRunning')} +
+ ); + default: return(
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
); - break; - case 'stopped': - return( -
- {MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)} -
-
- {MailPoet.I18n.t('start')} -
- ); - break; } } }); @@ -97,4 +66,4 @@ define( container ); } -}); +}); \ No newline at end of file diff --git a/lib/API/Endpoints/Cron.php b/lib/API/Endpoints/Cron.php index a2ee3941b4..700a8ce5ff 100644 --- a/lib/API/Endpoints/Cron.php +++ b/lib/API/Endpoints/Cron.php @@ -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); } } \ No newline at end of file diff --git a/lib/Config/Initializer.php b/lib/Config/Initializer.php index 77bcd7e5a9..01b47b9ba9 100644 --- a/lib/Config/Initializer.php +++ b/lib/Config/Initializer.php @@ -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() { diff --git a/lib/Config/Menu.php b/lib/Config/Menu.php index 88850296b1..9aef4eff9b 100644 --- a/lib/Config/Menu.php +++ b/lib/Config/Menu.php @@ -1,6 +1,7 @@ $settings, 'segments' => Segment::getPublic()->findArray(), + 'cron_trigger' => CronTrigger::getAvailableMethods(), 'pages' => Pages::getAll(), 'flags' => $flags, 'current_user' => wp_get_current_user(), @@ -433,4 +435,4 @@ class Menu { ? (int)$listing_per_page : Listing\Handler::DEFAULT_LIMIT_PER_PAGE; } -} +} \ No newline at end of file diff --git a/lib/Config/Populator.php b/lib/Config/Populator.php index 65ae2bd54a..9d7231337c 100644 --- a/lib/Config/Populator.php +++ b/lib/Config/Populator.php @@ -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, diff --git a/lib/Cron/CronHelper.php b/lib/Cron/CronHelper.php index d78d5b122c..91a6566e82 100644 --- a/lib/Cron/CronHelper.php +++ b/lib/Cron/CronHelper.php @@ -1,9 +1,9 @@ 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.')); diff --git a/lib/Cron/CronTrigger.php b/lib/Cron/CronTrigger.php new file mode 100644 index 0000000000..9d5f88aec8 --- /dev/null +++ b/lib/Cron/CronTrigger.php @@ -0,0 +1,41 @@ + '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'); + } +} \ No newline at end of file diff --git a/lib/Cron/Daemon.php b/lib/Cron/Daemon.php index 3cdcdd0696..aaeada8b45 100644 --- a/lib/Cron/Daemon.php +++ b/lib/Cron/Daemon.php @@ -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; } diff --git a/lib/Cron/Supervisor.php b/lib/Cron/Supervisor.php index 9490bd159a..92d51ec2f9 100644 --- a/lib/Cron/Supervisor.php +++ b/lib/Cron/Supervisor.php @@ -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); + $execution_timeout_exceeded = + (time() - (int)$daemon['updated_at']) > CronHelper::DAEMON_EXECUTION_TIMEOUT; + if($execution_timeout_exceeded) { + CronHelper::restartDaemon($this->token); + return $this->runDaemon(); } - // 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); - 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(); - if(!empty($status) || !$daemon) { - if(!$daemon) { - $message = __('Daemon failed to run.'); - } else { - list(, $message) = explode(':', $status[0]); - $message = base64_decode($message); - } - return $this->formatResultMessage( - false, - $message - ); - } - return $this->formatDaemonStatusMessage($daemon['status']); + return $daemon; } - 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; + function getDaemon() { + $daemon = CronHelper::getDaemon(); + if(!$daemon) { + CronHelper::createDaemon($this->token); + return $this->runDaemon(); } - return $formattedResult; + return $daemon; } -} +} \ No newline at end of file diff --git a/lib/Cron/Triggers/MailPoet.php b/lib/Cron/Triggers/MailPoet.php new file mode 100644 index 0000000000..ab9d8cf01a --- /dev/null +++ b/lib/Cron/Triggers/MailPoet.php @@ -0,0 +1,13 @@ +checkDaemon(); + } +} \ No newline at end of file diff --git a/lib/Cron/Triggers/WordPress.php b/lib/Cron/Triggers/WordPress.php new file mode 100644 index 0000000000..5885cfa9fc --- /dev/null +++ b/lib/Cron/Triggers/WordPress.php @@ -0,0 +1,31 @@ +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(); + } } \ No newline at end of file diff --git a/lib/Cron/Workers/SendingQueue/SendingQueue.php b/lib/Cron/Workers/SendingQueue/SendingQueue.php index 25fc18551a..55fdbc889a 100644 --- a/lib/Cron/Workers/SendingQueue/SendingQueue.php +++ b/lib/Cron/Workers/SendingQueue/SendingQueue.php @@ -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); + // enforce sending limit if there are still subscribers left to process + if($queue->count_to_process) { + MailerLog::enforceSendingLimit(); + } } - $queue = $this->updateQueue($queue); - if($subscribers_to_process_count) { - $this->mailer_task->checkSendingLimit(); - } - 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(); } diff --git a/lib/Cron/Workers/SendingQueue/Tasks/Mailer.php b/lib/Cron/Workers/SendingQueue/Tasks/Mailer.php index 9563ff9361..c2a105166d 100644 --- a/lib/Cron/Workers/SendingQueue/Tasks/Mailer.php +++ b/lib/Cron/Workers/SendingQueue/Tasks/Mailer.php @@ -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); - } - } -} +} \ No newline at end of file diff --git a/lib/Cron/Workers/SendingQueue/Tasks/Newsletter.php b/lib/Cron/Workers/SendingQueue/Tasks/Newsletter.php index 6944812bf5..7f34771315 100644 --- a/lib/Cron/Workers/SendingQueue/Tasks/Newsletter.php +++ b/lib/Cron/Workers/SendingQueue/Tasks/Newsletter.php @@ -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); + } + } +} \ No newline at end of file diff --git a/lib/Cron/Workers/SendingQueue/Tasks/Posts.php b/lib/Cron/Workers/SendingQueue/Tasks/Posts.php index a61f60aeb2..6cbd102c71 100644 --- a/lib/Cron/Workers/SendingQueue/Tasks/Posts.php +++ b/lib/Cron/Workers/SendingQueue/Tasks/Posts.php @@ -1,7 +1,7 @@ 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; } @@ -140,4 +157,4 @@ class Mailer { $subscriber = trim(preg_replace('!\s\s+!', ' ', $subscriber)); return $subscriber; } -} +} \ No newline at end of file diff --git a/lib/Mailer/MailerLog.php b/lib/Mailer/MailerLog.php new file mode 100644 index 0000000000..4e273fd524 --- /dev/null +++ b/lib/Mailer/MailerLog.php @@ -0,0 +1,60 @@ + 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')); + } + } +} \ No newline at end of file diff --git a/lib/Models/Setting.php b/lib/Models/Setting.php index e94f084149..bf19a03e2d 100644 --- a/lib/Models/Setting.php +++ b/lib/Models/Setting.php @@ -1,6 +1,8 @@ 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; + } +} \ No newline at end of file diff --git a/lib/Newsletter/Links/Links.php b/lib/Newsletter/Links/Links.php index 912f44aa8a..13ca2a7704 100644 --- a/lib/Newsletter/Links/Links.php +++ b/lib/Newsletter/Links/Links.php @@ -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; diff --git a/lib/Subscribers/ImportExport/Import/MailChimp.php b/lib/Subscribers/ImportExport/Import/MailChimp.php index bae4d9877c..1aae5c4377 100644 --- a/lib/Subscribers/ImportExport/Import/MailChimp.php +++ b/lib/Subscribers/ImportExport/Import/MailChimp.php @@ -66,44 +66,40 @@ class MailChimp { $connection = @fopen($url, 'r'); if(!$connection) { return $this->processError('connection'); - } else { - $i = 0; - $header = array(); - while(!feof($connection)) { - $buffer = fgets($connection, 4096); - if(trim($buffer) !== '') { - $obj = json_decode($buffer); - if($i === 0) { - $header = $obj; - if(is_object($header) && isset($header->error)) { - return $this->processError('lists'); - } - if(!isset($header_hash)) { - $header_hash = md5(implode(',', $header)); - } else { - if(md5(implode(',', $header) !== $header_hash)) { - return $this->processError('headers'); - } - } - } else { - $subscribers[] = $obj; - } - $i++; - } - - $bytes_fetched += strlen($buffer); - if($bytes_fetched > $this->max_post_size) { - return $this->processError('size'); - - } - } - fclose($connection); } + $i = 0; + $header = array(); + while(!feof($connection)) { + $buffer = fgets($connection, 4096); + if(trim($buffer) !== '') { + $obj = json_decode($buffer); + if($i === 0) { + $header = $obj; + if(is_object($header) && isset($header->error)) { + return $this->processError('lists'); + } + if(!isset($header_hash)) { + $header_hash = md5(implode(',', $header)); + } else { + if(md5(implode(',', $header) !== $header_hash)) { + return $this->processError('headers'); + } + } + } else { + $subscribers[] = $obj; + } + $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; diff --git a/views/cron.html b/views/cron.html index 22660da728..4b964c3248 100644 --- a/views/cron.html +++ b/views/cron.html @@ -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 %> diff --git a/views/settings/advanced.html b/views/settings/advanced.html index 6e1e555f11..35852c3efc 100644 --- a/views/settings/advanced.html +++ b/views/settings/advanced.html @@ -39,10 +39,9 @@