Sending Progress

- improved progress bar styles (with completed status)
- add pause/resume buttons
- fixed method case in settings.mta for MailPoet & SMTP Providers
- fixed parsley dependency
- added validation on from name & address on step 3
This commit is contained in:
Jonathan Labreuille
2015-12-03 18:26:36 +01:00
committed by MrCasual
parent 9b011c0281
commit 4a2bbe3f88
15 changed files with 245 additions and 65 deletions

View File

@ -5,17 +5,25 @@
width: 100%
margin: 0
border-radius: 5px
position: relative
.mailpoet_progress span
.mailpoet_progress_label
position: absolute
width: 100%
text-align: center
display: inline-block
margin: 2px 0 0 0
.mailpoet_progress_bar
position: absolute
display: inline-block
height: 100%
border-radius: 3px
box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset
.blue span
background-color: #34c2e3
background-image: linear-gradient(top, #34c2e3, darken(#34c2e3, 20%))
.orange span
background-color: #fecf23
background-image: linear-gradient(top, #fecf23, #fd9215)
.mailpoet_progress_complete
.mailpoet_progress_bar
background-color: #fecf23
background-image: linear-gradient(top, #fecf23, #fd9215)

View File

@ -20,7 +20,9 @@ function(
value={ this.props.item[this.props.field.name] }
placeholder={ this.props.field.placeholder }
defaultValue={ this.props.field.defaultValue }
onChange={ this.props.onValueChange } />
onChange={ this.props.onValueChange }
{...this.props.field.validation}
/>
);
}
});

View File

@ -397,6 +397,12 @@ define(
if(this.isMounted()) {
const params = this.props.params || {}
this.initWithParams(params)
if(this.props.auto_refresh) {
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
this.getItems();
}.bind(this));
}
}
},
componentWillReceiveProps: function(nextProps) {

View File

@ -123,23 +123,89 @@ define(
];
var NewsletterList = React.createClass({
pauseSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'pause',
data: item.id
}).done(function() {
jQuery('#resume_'+item.id).show();
jQuery('#pause_'+item.id).hide();
});
},
resumeSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'resume',
data: item.id
}).done(function() {
jQuery('#pause_'+item.id).show();
jQuery('#resume_'+item.id).hide();
});
},
renderStatus: function(item) {
if(item.queue === null) {
return (
<span>Not sent yet.</span>
);
} else {
var progressClasses = classNames(
'mailpoet_progress',
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
);
// calculate percentage done
var percentage = Math.round(
(item.queue.count_processed * 100) / (item.queue.count_total)
);
var label = false;
if(item.queue.status === 'completed') {
label = (
<span>
Sent to {
item.queue.count_processed - item.queue.count_failed
} out of { item.queue.count_total }.
</span>
);
} else {
label = (
<span>
{ item.queue.count_processed } / { item.queue.count_total }
&nbsp;&nbsp;
<a
id={ 'resume_'+item.id }
className="button"
style={{ display: (item.queue.status === 'paused') ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.resumeSending.bind(null, item) }
>Resume</a>
<a
id={ 'pause_'+item.id }
className="button mailpoet_pause"
style={{ display: (item.queue.status === null) ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.pauseSending.bind(null, item) }
>Pause</a>
</span>
);
}
return (
<div>
<div className="mailpoet_progress blue">
<span style={ { width: percentage + "%"} }></span>
<div className={ progressClasses }>
<span
className="mailpoet_progress_bar"
style={ { width: percentage + "%"} }
></span>
<span className="mailpoet_progress_label">
{ percentage + "%" }
</span>
</div>
{ item.queue.count_processed } / { item.queue.count_total }
<p style={{ textAlign:'center' }}>
{ label }
</p>
</div>
);
}
@ -194,7 +260,8 @@ define(
columns={columns}
bulk_actions={ bulk_actions }
item_actions={ item_actions }
messages={ messages } />
messages={ messages }
auto_refresh={ true } />
</div>
);
}

View File

@ -48,14 +48,21 @@ define(
name: 'from_name',
type: 'text',
placeholder: 'John Doe',
defaultValue: settings.from_name
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
validation: {
'data-parsley-required': true
}
},
{
name: 'from_email',
type: 'text',
placeholder: 'john.doe@email.com',
defaultValue: settings.from_address
},
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
validation: {
'data-parsley-required': true,
'data-parsley-type': 'email'
}
}
]
},
{
@ -93,33 +100,40 @@ define(
Router.History
],
handleSend: function() {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'addQueue',
data: {
newsletter_id: this.props.params.id,
segments: jQuery('#mailpoet_segments').val()
}
}).done(function(response) {
if(response === true) {
//this.history.pushState(null, '/');
if(jQuery('#mailpoet_newsletter').parsley().validate() === true) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'add',
data: {
newsletter_id: this.props.params.id,
segments: jQuery('#mailpoet_segments').val()
}
}).done(function(response) {
if(response.result === true) {
this.history.pushState(null, '/');
MailPoet.Notice.success(
'The newsletter has been sent!'
);
} else {
if(response.errors) {
MailPoet.Notice.error(
response.errors.join("<br />")
MailPoet.Notice.success(
'The newsletter is being sent...'
);
} else {
MailPoet.Notice.error(
'An error occurred while trying to send. '+
'<a href="?page=mailpoet-settings">Check your settings.</a>'
);
if(response.errors) {
MailPoet.Notice.error(
response.errors.join("<br />")
);
} else {
MailPoet.Notice.error(
'An error occurred while trying to send. '+
'<a href="?page=mailpoet-settings">Check your settings.</a>'
);
}
}
}
}.bind(this));
}.bind(this));
}
},
componentDidMount: function() {
if(this.isMounted()) {
jQuery('#mailpoet_newsletter').parsley();
}
},
render: function() {
return (

View File

@ -36,7 +36,7 @@ define(
} else {
// hide DKIM option when using MailPoet's API
jQuery('#mailpoet_mta_dkim')[
(method === 'mailpoet')
(method === 'MailPoet')
? 'hide'
: 'show'
]();

View File

@ -320,20 +320,27 @@ class Menu {
}
function newsletters() {
add_filter('heartbeat_received', array($this, 'getQueueStatus'), 10, 3);
global $wp_roles;
$data = array();
$data['segments'] = Segment::findArray();
$settings = Setting::findArray();
$data['settings'] = array();
foreach ($settings as $setting) {
$data['settings'][$setting['name']] = $setting['value'];
}
$data['settings'] = Setting::getAll();
$data['roles'] = $wp_roles->get_names();
echo $this->renderer->render('newsletters.html', $data);
}
function getQueueStatus($response, $data, $screen_id) {
if(isset($data['mailpoet'])) {
$response['mailpoet'] = array(
'hello' => 'world'
);
}
return $response;
}
function newletterEditor() {
$data = array();
wp_enqueue_media();
@ -364,7 +371,7 @@ class Menu {
$data = array(
'form' => $form,
'pages' => Pages::getAll(),
'segments' => Segment::getPublished()
'segments' => Segment::getPublic()
->findArray(),
'styles' => FormRenderer::getStyles($form),
'date_types' => Block\Date::getDateTypes(),

View File

@ -43,6 +43,12 @@ class Newsletter extends Model {
)->select_expr(MP_NEWSLETTER_OPTION_TABLE.'.value');
}
function getQueue() {
return SendingQueue::where('newsletter_id', $this->id)
->orderByDesc('updated_at')
->findOne();
}
static function search($orm, $search = '') {
return $orm->where_like('subject', '%' . $search . '%');
}

View File

@ -9,4 +9,27 @@ class SendingQueue extends Model {
function __construct() {
parent::__construct();
}
function pause() {
if($this->count_processed === $this->count_total) {
return false;
} else {
$this->set('status', 'paused');
return $this->save();
}
}
function resume() {
if($this->count_processed === $this->count_total) {
return $this->complete();
} else {
$this->set_expr('status', 'NULL');
return $this->save();
}
}
function complete() {
$this->set('status', 'completed');
return $this->save();
}
}

View File

@ -92,6 +92,8 @@ class Queue {
);
}
function addQueues($data) {
$result = array_map(function ($queueData) {
$queue = SendingQueue::create();

View File

@ -1,13 +1,14 @@
<?php
namespace MailPoet\Router;
use MailPoet\Models\Newsletter;
use MailPoet\Models\Segment;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class SendingQueue {
function addQueue($data) {
function add($data) {
$queue = \MailPoet\Models\SendingQueue::where('newsletter_id', $data['newsletter_id'])
->whereNull('status')
->findArray();
@ -18,6 +19,7 @@ class SendingQueue {
'errors' => array(__('Send operation is already in progress.'))
)
);
exit;
}
$queue = \MailPoet\Models\SendingQueue::create();
$queue->newsletter_id = $data['newsletter_id'];
@ -39,18 +41,60 @@ class SendingQueue {
)
);
$queue->count_total = $queue->count_to_process = count($subscriber_ids);
$queue->save();
wp_send_json(
!$queue->save() ?
$result = $queue->save();
if($result === false) {
$errors = array(__('Queue could not be created.'));
if(!empty($queue->getValidationErrors())) {
$errors = array_merge($errors, $queue->getValidationErrors());
}
wp_send_json(
array(
'result' => false,
'errors' => array(__('Queue could not be created.'))
) :
'errors' => $errors
)
);
} else {
wp_send_json(
array(
'result' => true,
'data' => array($queue->id)
)
);
);
}
}
function pause($newsletter_id) {
$newsletter = Newsletter::findOne($newsletter_id);
$result = false;
if($newsletter !== false) {
$queue = $newsletter->getQueue();
if($queue !== false && $queue->id() > 0) {
$result = $queue->pause();
}
}
wp_send_json(array(
'result' => $result
));
}
function resume($newsletter_id) {
$newsletter = Newsletter::findOne($newsletter_id);
$result = false;
if($newsletter !== false) {
$queue = $newsletter->getQueue();
if($queue !== false && $queue->id() > 0) {
$result = $queue->resume();
}
}
wp_send_json(array(
'result' => $result
));
}
function addQueues($data) {

View File

@ -3,25 +3,25 @@ namespace MailPoet\Settings;
class Hosts {
private static $_smtp = array(
'amazon' => array(
'AmazonSES' => array(
'name' => 'Amazon SES',
'api' => false,
'emails' => 100,
'interval' => 5
),
'elasticemail' => array(
'ElasticEmail' => array(
'name' => 'ElasticEmail',
'api' => true,
'emails' => 100,
'interval' => 5
),
'mailgun' => array(
'MailGun' => array(
'name' => 'MailGun',
'api' => false,
'emails' => 100,
'interval' => 5
),
'sendgrid' => array(
'SendGrid' => array(
'name' => 'SendGrid',
'api' => true,
'emails' => 100,

View File

@ -24,7 +24,7 @@
"moment": "^2.10.3",
"napa": "^1.2.0",
"papaparse": "4.1.1",
"parsley": "^0.1.0",
"parsleyjs": "^2.1.2",
"react": "^0.14.1",
"react-checkbox-group": "0.2.2",
"react-dom": "^0.14.1",

View File

@ -17,6 +17,7 @@
name="mta[method]"
value="<%= settings.mta.method %>"
/>
<!-- mta: sending frequency -->
<input
type="hidden"
@ -51,8 +52,8 @@
<!-- smtp: available sending methods -->
<ul class="mailpoet_sending_methods clearfix">
<li
data-method="mailpoet"
<% if(settings.mta.method == 'mailpoet') %>class="mailpoet_active"<% endif %>
data-method="MailPoet"
<% if(settings.mta.method == 'MailPoet') %>class="mailpoet_active"<% endif %>
>
<h3>
<img
@ -76,7 +77,7 @@
<div class="mailpoet_actions">
<a
class="button-secondary"
href="#mta/mailpoet"><%= __('Configure') %></a>
href="#mta/MailPoet"><%= __('Configure') %></a>
</div>
</li>
<li
@ -125,7 +126,7 @@
<!-- Sending Method: MailPoet -->
<div
class="mailpoet_sending_method"
data-method="mailpoet"
data-method="MailPoet"
style="display:none;"
>
<h3><%= __('Open a free account with MailPoet, and get:') %></h3>
@ -165,8 +166,8 @@
type="text"
id="mailpoet_api_key"
size="40"
name="api_key"
value="<%= settings.api_key %>"
name="mta[api_key]"
value="<%= settings.mta.api_key %>"
/>
</td>
</tr>
@ -666,7 +667,6 @@
)
? $('.mailpoet_sending_method:visible').data('method')
: $('#mta_method').val();
alert(
'Sending a test email to: '+recipient+
' using sending method: '+mta_method

View File

@ -77,7 +77,7 @@ baseConfig = {
{
include: /html2canvas.js$/,
loader: 'expose-loader?html2canvas',
},
}
]
}
};
@ -92,7 +92,8 @@ config.push(_.extend({}, baseConfig, {
'ajax',
'modal',
'notice',
'jquery.serialize_object'
'jquery.serialize_object',
'parsleyjs'
],
admin: [
'subscribers/subscribers.jsx',