- Refactors and renames code

- Adds Queue menu option and displays status
This commit is contained in:
MrCasual
2015-11-25 21:07:52 -05:00
parent 4208b148b4
commit 436faea591
11 changed files with 264 additions and 95 deletions

56
assets/js/src/queue.jsx Normal file
View File

@@ -0,0 +1,56 @@
define(
[
'react',
'react-dom',
'mailpoet',
'classnames'
],
function (
React,
ReactDOM,
MailPoet,
classNames
) {
var QueueDaemonControl = React.createClass({
getInitialState: function () {
return (queueDaemon) ? {status: queueDaemon.status} : null;
},
getDaemonData: function () {
MailPoet.Ajax.post({
endpoint: 'queue',
action: 'getQueueStatus'
}).done(function (response) {
this.setState({status: response.status});
}.bind(this));
},
componentDidMount: function () {
this.getDaemonData;
setInterval(this.getDaemonData, 2000);
},
render: function () {
if (!this.state) {
return (
<div className="QueueControl">
If you're seeing this message, queue daemon has not even been created!
</div>
)
}
return (
<div>
Queue is currently <b>{this.state.status}</b>
</div>
);
}
});
let container = document.getElementById('queue_container');
if (container) {
ReactDOM.render(
<QueueDaemonControl />,
container
)
}
}
);

View File

@@ -8,7 +8,9 @@
"tburry/pquery": "*", "tburry/pquery": "*",
"j4mie/paris": "1.5.4", "j4mie/paris": "1.5.4",
"swiftmailer/swiftmailer": "^5.4", "swiftmailer/swiftmailer": "^5.4",
"phpseclib/phpseclib": "*" "phpseclib/phpseclib": "*",
"mtdowling/cron-expression": "^1.0",
"nesbot/carbon": "^1.21"
}, },
"require-dev": { "require-dev": {
"codeception/codeception": "*", "codeception/codeception": "*",

View File

@@ -137,7 +137,7 @@ class Initializer {
function runQueueSupervisor() { function runQueueSupervisor() {
try { try {
$supervisor = new Supervisor(); $supervisor = new Supervisor();
$supervisor->checkQueue(); $supervisor->checkDaemon();
} catch (\Exception $e) { } catch (\Exception $e) {
} }
} }

View File

@@ -2,16 +2,17 @@
namespace MailPoet\Config; namespace MailPoet\Config;
use MailPoet\Subscribers\ImportExport\BootStrapMenu; use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use \MailPoet\Models\Segment; use MailPoet\Form\Block;
use \MailPoet\Models\Setting; use MailPoet\Form\Renderer as FormRenderer;
use \MailPoet\Models\Form; use MailPoet\Models\Form;
use \MailPoet\Form\Block; use MailPoet\Models\Segment;
use \MailPoet\Form\Renderer as FormRenderer; use MailPoet\Models\Setting;
use \MailPoet\Settings\Hosts; use MailPoet\Settings\Charsets;
use \MailPoet\Settings\Pages; use MailPoet\Settings\Hosts;
use \MailPoet\Settings\Charsets; use MailPoet\Settings\Pages;
use \MailPoet\Util\Permissions; use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use \MailPoet\Util\DKIM; use MailPoet\Util\DKIM;
use MailPoet\Util\Permissions;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@@ -24,7 +25,10 @@ class Menu {
function init() { function init() {
add_action( add_action(
'admin_menu', 'admin_menu',
array($this, 'setup') array(
$this,
'setup'
)
); );
} }
@@ -34,7 +38,10 @@ class Menu {
'MailPoet', 'MailPoet',
'manage_options', 'manage_options',
'mailpoet', 'mailpoet',
array($this, 'home'), array(
$this,
'home'
),
$this->assets_url . '/img/menu_icon.png', $this->assets_url . '/img/menu_icon.png',
30 30
); );
@@ -44,7 +51,10 @@ class Menu {
__('Newsletters'), __('Newsletters'),
'manage_options', 'manage_options',
'mailpoet-newsletters', 'mailpoet-newsletters',
array($this, 'newsletters') array(
$this,
'newsletters'
)
); );
add_submenu_page( add_submenu_page(
'mailpoet', 'mailpoet',
@@ -52,7 +62,10 @@ class Menu {
__('Forms'), __('Forms'),
'manage_options', 'manage_options',
'mailpoet-forms', 'mailpoet-forms',
array($this, 'forms') array(
$this,
'forms'
)
); );
add_submenu_page( add_submenu_page(
'mailpoet', 'mailpoet',
@@ -60,7 +73,10 @@ class Menu {
__('Subscribers'), __('Subscribers'),
'manage_options', 'manage_options',
'mailpoet-subscribers', 'mailpoet-subscribers',
array($this, 'subscribers') array(
$this,
'subscribers'
)
); );
add_submenu_page( add_submenu_page(
'mailpoet', 'mailpoet',
@@ -68,7 +84,10 @@ class Menu {
__('Segments'), __('Segments'),
'manage_options', 'manage_options',
'mailpoet-segments', 'mailpoet-segments',
array($this, 'segments') array(
$this,
'segments'
)
); );
add_submenu_page( add_submenu_page(
'mailpoet', 'mailpoet',
@@ -76,7 +95,10 @@ class Menu {
__('Settings'), __('Settings'),
'manage_options', 'manage_options',
'mailpoet-settings', 'mailpoet-settings',
array($this, 'settings') array(
$this,
'settings'
)
); );
add_submenu_page( add_submenu_page(
null, null,
@@ -84,7 +106,10 @@ class Menu {
__('Import'), __('Import'),
'manage_options', 'manage_options',
'mailpoet-import', 'mailpoet-import',
array($this, 'import') array(
$this,
'import'
)
); );
add_submenu_page( add_submenu_page(
null, null,
@@ -92,7 +117,10 @@ class Menu {
__('Export'), __('Export'),
'manage_options', 'manage_options',
'mailpoet-export', 'mailpoet-export',
array($this, 'export') array(
$this,
'export'
)
); );
add_submenu_page( add_submenu_page(
@@ -101,7 +129,10 @@ class Menu {
__('Welcome'), __('Welcome'),
'manage_options', 'manage_options',
'mailpoet-welcome', 'mailpoet-welcome',
array($this, 'welcome') array(
$this,
'welcome'
)
); );
add_submenu_page( add_submenu_page(
@@ -110,7 +141,10 @@ class Menu {
__('Update'), __('Update'),
'manage_options', 'manage_options',
'mailpoet-update', 'mailpoet-update',
array($this, 'update') array(
$this,
'update'
)
); );
add_submenu_page( add_submenu_page(
@@ -119,7 +153,10 @@ class Menu {
__('Form editor'), __('Form editor'),
'manage_options', 'manage_options',
'mailpoet-form-editor', 'mailpoet-form-editor',
array($this, 'formEditor') array(
$this,
'formEditor'
)
); );
add_submenu_page( add_submenu_page(
@@ -128,7 +165,22 @@ class Menu {
__('Newsletter editor'), __('Newsletter editor'),
'manage_options', 'manage_options',
'mailpoet-newsletter-editor', 'mailpoet-newsletter-editor',
array($this, 'newletterEditor') array(
$this,
'newletterEditor'
)
);
add_submenu_page(
'mailpoet',
__('Queue'),
__('Queue'),
'manage_options',
'mailpoet-queue',
array(
$this,
'queue'
)
); );
} }
@@ -142,8 +194,8 @@ class Menu {
$current_url = home_url(add_query_arg($wp->query_string, $wp->request)); $current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url = $redirect_url =
(!empty($_GET['mailpoet_redirect'])) (!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect']) ? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer(); : wp_get_referer();
if( if(
$redirect_url === $current_url $redirect_url === $current_url
@@ -166,8 +218,8 @@ class Menu {
$current_url = home_url(add_query_arg($wp->query_string, $wp->request)); $current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url = $redirect_url =
(!empty($_GET['mailpoet_redirect'])) (!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect']) ? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer(); : wp_get_referer();
if( if(
$redirect_url === $current_url $redirect_url === $current_url
@@ -206,7 +258,8 @@ class Menu {
$data = array( $data = array(
'settings' => $settings, 'settings' => $settings,
'segments' => Segment::getPublic()->findArray(), 'segments' => Segment::getPublished()
->findArray(),
'pages' => Pages::getAll(), 'pages' => Pages::getAll(),
'flags' => $this->_getFlags(), 'flags' => $this->_getFlags(),
'charsets' => Charsets::getAll(), 'charsets' => Charsets::getAll(),
@@ -234,11 +287,14 @@ class Menu {
// check if users can register // check if users can register
$flags['registration_enabled'] = $flags['registration_enabled'] =
!(in_array($registration, array('none', 'blog'))); !(in_array($registration, array(
'none',
'blog'
)));
} else { } else {
// check if users can register // check if users can register
$flags['registration_enabled'] = $flags['registration_enabled'] =
(bool)get_option('users_can_register', false); (bool) get_option('users_can_register', false);
} }
return $flags; return $flags;
@@ -272,7 +328,7 @@ class Menu {
$data['segments'] = Segment::findArray(); $data['segments'] = Segment::findArray();
$settings = Setting::findArray(); $settings = Setting::findArray();
$data['settings'] = array(); $data['settings'] = array();
foreach($settings as $setting) { foreach ($settings as $setting) {
$data['settings'][$setting['name']] = $setting['value']; $data['settings'][$setting['name']] = $setting['value'];
} }
$data['roles'] = $wp_roles->get_names(); $data['roles'] = $wp_roles->get_names();
@@ -300,7 +356,7 @@ class Menu {
} }
function formEditor() { function formEditor() {
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0); $id = (isset($_GET['id']) ? (int) $_GET['id'] : 0);
$form = Form::findOne($id); $form = Form::findOne($id);
if($form !== false) { if($form !== false) {
$form = $form->asArray(); $form = $form->asArray();
@@ -309,7 +365,8 @@ class Menu {
$data = array( $data = array(
'form' => $form, 'form' => $form,
'pages' => Pages::getAll(), 'pages' => Pages::getAll(),
'segments' => Segment::getPublic()->findArray(), 'segments' => Segment::getPublished()
->findArray(),
'styles' => FormRenderer::getStyles($form), 'styles' => FormRenderer::getStyles($form),
'date_types' => Block\Date::getDateTypes(), 'date_types' => Block\Date::getDateTypes(),
'date_formats' => Block\Date::getDateFormats() 'date_formats' => Block\Date::getDateFormats()
@@ -317,4 +374,10 @@ class Menu {
echo $this->renderer->render('form/editor.html', $data); echo $this->renderer->render('form/editor.html', $data);
} }
function queue() {
$daemon = new \MailPoet\Queue\BootStrapMenu();
$data['daemon'] = json_encode($daemon->bootstrap());
echo $this->renderer->render('queue.html', $data);
}
} }

View File

@@ -0,0 +1,24 @@
<?php
namespace MailPoet\Queue;
use MailPoet\Models\Setting;
class BootStrapMenu {
function __construct() {
$this->daemon = Setting::where('name', 'daemon')
->findOne()
->asArray();
}
function bootStrap() {
return ($this->daemon) ?
array_merge(
array(
'started_at' => $this->daemon['created_at'],
'updated_at' => $this->daemon['updated_at']
),
json_decode($this->daemon['value'], true)
) :
"false";
}
}

View File

@@ -12,7 +12,7 @@ class Daemon {
function __construct($payload = array()) { function __construct($payload = array()) {
set_time_limit(0); set_time_limit(0);
ignore_user_abort(); ignore_user_abort();
list ($this->queue, $this->queueData) = $this->getQueue(); list ($this->daemon, $this->daemonData) = $this->getDaemon();
$this->refreshedToken = $this->refreshToken(); $this->refreshedToken = $this->refreshToken();
$this->payload = $payload; $this->payload = $payload;
$this->timer = microtime(true); $this->timer = microtime(true);
@@ -23,30 +23,30 @@ class Daemon {
$this->abortWithError('missing session ID'); $this->abortWithError('missing session ID');
} }
$this->manageSession('start'); $this->manageSession('start');
$queue = $this->queue; $daemon = $this->daemon;
$queueData = $this->queueData; $daemonData = $this->daemonData;
if(!$queue) { if(!$daemon) {
$queue = Setting::create(); $daemon = Setting::create();
$queue->name = 'queue'; $daemon->name = 'daemon';
$queue->value = json_encode(array('status' => 'stopped')); $daemon->value = json_encode(array('status' => 'stopped'));
$queue->save(); $daemon->save();
} }
if($queueData['status'] !== 'started') { if($daemonData['status'] !== 'started') {
$_SESSION['queue'] = 'started'; $_SESSION['daemon'] = 'started';
$queueData = array( $daemonData = array(
'status' => 'started', 'status' => 'started',
'token' => $this->refreshedToken, 'token' => $this->refreshedToken,
'counter' => ($queueData['status'] === 'paused') ? 'counter' => ($daemonData['status'] === 'paused') ?
$queueData['counter'] : $daemonData['counter'] :
0 0
); );
$_SESSION['queue'] = array('result' => true); $_SESSION['daemon'] = array('result' => true);
$this->manageSession('end'); $this->manageSession('end');
$queue->value = json_encode($queueData); $daemon->value = json_encode($daemonData);
$queue->save(); $daemon->save();
$this->callSelf(); $this->callSelf();
} else { } else {
$_SESSION['queue'] = array( $_SESSION['daemon'] = array(
'result' => false, 'result' => false,
'error' => 'already started' 'error' => 'already started'
); );
@@ -55,33 +55,37 @@ class Daemon {
} }
function run() { function run() {
if(!$this->queue || $this->queueData['status'] !== 'started') { if(!$this->daemon || $this->daemonData['status'] !== 'started') {
$this->abortWithError('not running'); $this->abortWithError('not running');
} }
if(!isset($this->payload['token']) || if(!isset($this->payload['token']) ||
$this->payload['token'] !== $this->queueData['token'] $this->payload['token'] !== $this->daemonData['token']
) { ) {
$this->abortWithError('invalid token'); $this->abortWithError('invalid token');
} }
$worker = new Worker(); $worker = new Worker();
$worker->process(); $worker->process();
$elapsedTime = microtime(true) - $this->timer;
// after each execution, read queue in case it's status was modified if ($elapsedTime < 30) {
list($queue, $queueData) = $this->getQueue(); sleep(30 - $elapsedTime);
$queueData['counter']++; }
$queueData['token'] = $this->refreshedToken;
$queue->value = json_encode($queueData); // after each execution, read daemon in case it's status was modified
$queue->save(); list($daemon, $daemonData) = $this->getDaemon();
$daemonData['counter']++;
$daemonData['token'] = $this->refreshedToken;
$daemon->value = json_encode($daemonData);
$daemon->save();
$this->callSelf(); $this->callSelf();
} }
function getQueue() { function getDaemon() {
$queue = Setting::where('name', 'queue') $daemon = Setting::where('name', 'daemon')
->findOne(); ->findOne();
return array( return array(
($queue) ? $queue : null, ($daemon) ? $daemon : null,
($queue) ? json_decode($queue->value, true) : null ($daemon) ? json_decode($daemon->value, true) : null
); );
} }

View File

@@ -13,15 +13,16 @@ class Supervisor {
if(!Env::isPluginActivated()) { if(!Env::isPluginActivated()) {
throw new \Exception('Database has not been configured.'); throw new \Exception('Database has not been configured.');
} }
list ($this->queue, $this->queueData) = $this->getQueue(); list ($this->daemon, $this->daemonData) = $this->getDaemon();
} }
function checkQueue() { function checkDaemon() {
if(!$this->queue) { if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) return;
return $this->startQueue(); if(!$this->daemon) {
return $this->startDaemon();
} else { } else {
if(!$this->forceStart && ($this->queueData['status'] === 'paused' || if(!$this->forceStart && ($this->daemonData['status'] === 'paused' ||
$this->queueData['status'] === 'stopped' $this->daemonData['status'] === 'stopped'
) )
) { ) {
return; return;
@@ -29,40 +30,40 @@ class Supervisor {
$currentTime = Carbon::now('UTC'); $currentTime = Carbon::now('UTC');
$lastUpdateTime = Carbon::createFromFormat( $lastUpdateTime = Carbon::createFromFormat(
'Y-m-d H:i:s', 'Y-m-d H:i:s',
$this->queue->updated_at, 'UTC' $this->daemon->updated_at, 'UTC'
); );
$timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime); $timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime);
if($timeSinceLastStart < 5) return; if($timeSinceLastStart < 5) return;
$this->queueData['status'] = 'paused'; $this->daemonData['status'] = 'paused';
$this->queue->value = json_encode($this->queueData); $this->daemon->value = json_encode($this->daemonData);
$this->queue->save(); $this->daemon->save();
return $this->startQueue(); return $this->startDaemon();
} }
} }
function startQueue() { function startDaemon() {
if(!session_id()) session_start(); if(!session_id()) session_start();
$sessionId = session_id(); $sessionId = session_id();
session_write_close(); session_write_close();
$_SESSION['queue'] = null; $_SESSION['daemon'] = null;
$payload = json_encode(array('session' => $sessionId)); $payload = json_encode(array('session' => $sessionId));
self::getRemoteUrl( self::getRemoteUrl(
'/?mailpoet-api&section=queue&action=start&payload=' . urlencode($payload) '/?mailpoet-api&section=queue&action=start&payload=' . urlencode($payload)
); );
session_start(); session_start();
$queueStatus = $_SESSION['queue']; $daemonStatus = $_SESSION['daemon'];
unset($_SESSION['queue']); unset($_SESSION['daemon']);
session_write_close(); session_write_close();
return $queueStatus; return $daemonStatus;
} }
function getQueue() { function getDaemon() {
$queue = Setting::where('name', 'queue') $daemon = Setting::where('name', 'daemon')
->findOne(); ->findOne();
$queueData = ($queue) ? json_decode($queue->value, true) : false; $daemonData = ($daemon) ? json_decode($daemon->value, true) : false;
return array( return array(
$queue, $daemon,
$queueData $daemonData
); );
} }

View File

@@ -28,8 +28,9 @@ class Worker {
if(!isset($subscribers['processed'])) $subscribers['processed'] = array(); if(!isset($subscribers['processed'])) $subscribers['processed'] = array();
$subscribersToProcess = $subscribers['to_process']; $subscribersToProcess = $subscribers['to_process'];
foreach ($subscribersToProcess as $subscriber) { foreach ($subscribersToProcess as $subscriber) {
if(microtime(true) - $this->timer >= 28) break; $elapsedTime = microtime(true) - $this->timer;
// TODO: remove if($elapsedTime >= 28) break;
// TODO: hook up to mailer
sleep(1); sleep(1);
$newsletterStatistics = NewsletterStatistics::create(); $newsletterStatistics = NewsletterStatistics::create();
$newsletterStatistics->subscriber_id = $subscriber; $newsletterStatistics->subscriber_id = $subscriber;

View File

@@ -1,6 +1,7 @@
<?php <?php
namespace MailPoet\Router; namespace MailPoet\Router;
use MailPoet\Models\Setting;
use MailPoet\Queue\Daemon; use MailPoet\Queue\Daemon;
use MailPoet\Queue\Supervisor; use MailPoet\Queue\Supervisor;
@@ -11,7 +12,7 @@ class Queue {
$supervisor = new Supervisor(); $supervisor = new Supervisor();
wp_send_json( wp_send_json(
array( array(
'result' => ($supervisor->checkQueue($forceStart = true)) ? 'result' => ($supervisor->checkDaemon($forceStart = true)) ?
true : true :
false false
) )
@@ -27,13 +28,13 @@ class Queue {
$status = 'paused'; $status = 'paused';
break; break;
} }
$queue = new Daemon(); $daemon = new Daemon();
if(!$queue->queue || $queue->queueData['status'] !== 'started') { if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') {
$result = false; $result = false;
} else { } else {
$queue->queueData['status'] = $status; $daemon->daemonData['status'] = $status;
$queue->queue->value = json_encode($queue->queueData); $daemon->daemon->value = json_encode($daemon->daemonData);
$result = $queue->queue->save(); $result = $daemon->daemon->save();
} }
wp_send_json( wp_send_json(
array( array(
@@ -41,4 +42,10 @@ class Queue {
) )
); );
} }
function getQueueStatus() {
$daemon = new \MailPoet\Queue\BootStrapMenu();
wp_send_json($daemon->bootStrap());
}
} }

10
views/queue.html Normal file
View File

@@ -0,0 +1,10 @@
<% extends 'layout.html' %>
<% block content %>
<div id="queue_container"></div>
<script type="text/javascript">
</script>
<script>
var queueDaemon = <%= daemon|raw %>
</script>
<% endblock %>

View File

@@ -102,7 +102,8 @@ config.push(_.extend({}, baseConfig, {
'settings/tabs.js', 'settings/tabs.js',
'subscribers/importExport/import.js', 'subscribers/importExport/import.js',
'subscribers/importExport/export.js', 'subscribers/importExport/export.js',
'helpscout' 'helpscout',
'queue.jsx'
], ],
form_editor: [ form_editor: [
'form_editor/form_editor.js', 'form_editor/form_editor.js',