Merge pull request #190 from mailpoet/batch_actions_round_1

listing: bulk actions
This commit is contained in:
Marco
2015-10-22 17:39:12 +02:00
13 changed files with 450 additions and 75 deletions

View File

@@ -19,7 +19,7 @@ a:focus
// select 2 // select 2
.select2-container .select2-container
width: 25em width: 25em !important
@media screen and (max-width: 782px) @media screen and (max-width: 782px)
.select2-container .select2-container

View File

@@ -45,6 +45,10 @@ function(
data.action = this.state.action; data.action = this.state.action;
if(action['onSuccess'] !== undefined) {
data.onSuccess = action.onSuccess;
}
if(data.action) { if(data.action) {
this.props.onBulkAction(selected_ids, data); this.props.onBulkAction(selected_ids, data);
} }

View File

@@ -5,8 +5,10 @@ define(['react', 'classnames'], function(React, classNames) {
return this.props.onSelectGroup(group); return this.props.onSelectGroup(group);
}, },
render: function() { render: function() {
var count = this.props.groups.length;
var groups = this.props.groups.map(function(group, index) { var groups = this.props.groups.map(function(group, index) {
if(group.name === 'trash' && group.count === 0) {
return false;
}
var classes = classNames( var classes = classNames(
{ 'current' : (group.name === this.props.group) } { 'current' : (group.name === this.props.group) }
@@ -14,12 +16,13 @@ define(['react', 'classnames'], function(React, classNames) {
return ( return (
<li key={index}> <li key={index}>
{(index > 0) ? ' |' : ''}
<a <a
href="javascript:;" href="javascript:;"
className={classes} className={classes}
onClick={this.handleSelect.bind(this, group.name)} > onClick={this.handleSelect.bind(this, group.name)} >
{group.label} <span className="count">({ group.count })</span> {group.label} <span className="count">({ group.count })</span>
</a>{(index < (count - 1)) ? ' | ' : ''} </a>
</li> </li>
); );
}.bind(this)); }.bind(this));

View File

@@ -43,8 +43,11 @@ define(
return !e.target.checked; return !e.target.checked;
}, },
handleDeleteItem: function(id) { handleRestoreItem: function(id) {
this.props.onDeleteItem(id); this.props.onRestoreItem(id);
},
handleDeleteItem: function(id, confirm = false) {
this.props.onDeleteItem(id, confirm);
}, },
handleToggleItem: function(id) { handleToggleItem: function(id) {
this.setState({ toggled: !this.state.toggled }); this.setState({ toggled: !this.state.toggled });
@@ -89,6 +92,40 @@ define(
); );
} }
if(this.props.group === 'trash') {
var actions = (
<div>
<div className="row-actions">
<span>
<a
href="javascript:;"
onClick={ this.handleRestoreItem.bind(
null,
this.props.item.id
)}
>Restore</a>
</span>
{ ' | ' }
<span className="delete">
<a
className="submitdelete"
href="javascript:;"
onClick={ this.handleDeleteItem.bind(
null,
this.props.item.id,
true
)}
>Delete permanently</a>
</span>
</div>
<button
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
className="toggle-row" type="button">
<span className="screen-reader-text">Show more details</span>
</button>
</div>
);
} else {
var actions = ( var actions = (
<div> <div>
<div className="row-actions"> <div className="row-actions">
@@ -97,7 +134,11 @@ define(
<span className="trash"> <span className="trash">
<a <a
href="javascript:;" href="javascript:;"
onClick={ this.handleDeleteItem.bind(null, this.props.item.id) }> onClick={ this.handleDeleteItem.bind(
null,
this.props.item.id,
false
) }>
Trash Trash
</a> </a>
</span> </span>
@@ -109,6 +150,7 @@ define(
</button> </button>
</div> </div>
); );
}
var row_classes = classNames({ 'is-expanded': !this.state.toggled }) var row_classes = classNames({ 'is-expanded': !this.state.toggled })
@@ -190,9 +232,11 @@ define(
onSelectItem={ this.props.onSelectItem } onSelectItem={ this.props.onSelectItem }
onRenderItem={ this.props.onRenderItem } onRenderItem={ this.props.onRenderItem }
onDeleteItem={ this.props.onDeleteItem } onDeleteItem={ this.props.onDeleteItem }
onRestoreItem={ this.props.onRestoreItem }
selection={ this.props.selection } selection={ this.props.selection }
is_selectable={ this.props.is_selectable } is_selectable={ this.props.is_selectable }
item_actions={ this.props.item_actions } item_actions={ this.props.item_actions }
group={ this.props.group }
key={ 'item-' + item.id } key={ 'item-' + item.id }
item={ item } /> item={ item } />
); );
@@ -254,7 +298,27 @@ define(
} }
}.bind(this)); }.bind(this));
}, },
handleDeleteItem: function(id) { handleRestoreItem: function(id) {
this.setState({
loading: true,
page: 1
});
MailPoet.Ajax.post({
endpoint: this.props.endpoint,
action: 'restore',
data: id
}).done(function(response) {
if(
this.props.messages !== undefined
&& this.props.messages['onRestore'] !== undefined
) {
this.props.messages.onRestore(response);
}
this.getItems();
}.bind(this));
},
handleDeleteItem: function(id, confirm = false) {
this.setState({ this.setState({
loading: true, loading: true,
page: 1 page: 1
@@ -263,8 +327,27 @@ define(
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: this.props.endpoint, endpoint: this.props.endpoint,
action: 'delete', action: 'delete',
data: id data: {
}).done(function() { id: id,
confirm: confirm
}
}).done(function(response) {
if(confirm === true) {
if(
this.props.messages !== undefined
&& this.props.messages['onConfirmDelete'] !== undefined
) {
this.props.messages.onConfirmDelete(response);
}
} else {
if(
this.props.messages !== undefined
&& this.props.messages['onDelete'] !== undefined
) {
this.props.messages.onDelete(response);
}
}
this.getItems(); this.getItems();
}.bind(this)); }.bind(this));
}, },
@@ -279,6 +362,11 @@ define(
this.setState({ loading: true }); this.setState({ loading: true });
var data = params || {}; var data = params || {};
var callback = ((data['onSuccess'] !== undefined)
? data['onSuccess']
: function() {}
);
delete data.onSuccess;
data.listing = { data.listing = {
offset: 0, offset: 0,
@@ -293,8 +381,9 @@ define(
endpoint: this.props.endpoint, endpoint: this.props.endpoint,
action: 'bulk_action', action: 'bulk_action',
data: data data: data
}).done(function() { }).done(function(response) {
this.getItems(); this.getItems();
callback(response);
}.bind(this)); }.bind(this));
}, },
handleSearch: function(search) { handleSearch: function(search) {
@@ -414,6 +503,24 @@ define(
// bulk actions // bulk actions
var bulk_actions = this.props.bulk_actions || []; var bulk_actions = this.props.bulk_actions || [];
if(this.state.group === 'trash') {
bulk_actions = [
{
name: 'restore',
label: 'Restore',
onSuccess: this.props.messages.onRestore
},
{
name: 'trash',
label: 'Delete permanently',
onSuccess: this.props.messages.onConfirmDelete,
getData: function() {
return { confirm: true };
}
}
];
}
// item actions // item actions
var item_actions = this.props.item_actions || []; var item_actions = this.props.item_actions || [];
@@ -464,6 +571,7 @@ define(
<ListingItems <ListingItems
onRenderItem={ this.handleRenderItem } onRenderItem={ this.handleRenderItem }
onDeleteItem={ this.handleDeleteItem } onDeleteItem={ this.handleDeleteItem }
onRestoreItem={ this.handleRestoreItem }
columns={ this.props.columns } columns={ this.props.columns }
is_selectable={ bulk_actions.length > 0 } is_selectable={ bulk_actions.length > 0 }
onSelectItem={ this.handleSelectItem } onSelectItem={ this.handleSelectItem }
@@ -471,6 +579,7 @@ define(
selection={ this.state.selection } selection={ this.state.selection }
selected_ids={ this.state.selected_ids } selected_ids={ this.state.selected_ids }
loading={ this.state.loading } loading={ this.state.loading }
group={ this.state.group }
count={ this.state.count } count={ this.state.count }
limit={ this.state.limit } limit={ this.state.limit }
item_actions={ item_actions } item_actions={ item_actions }

View File

@@ -121,6 +121,22 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
// set message // set message
this.setMessage(this.options.message); this.setMessage(this.options.message);
// position notice
this.element.insertAfter(jQuery('h2.title'));
// set class name
switch(this.options.type) {
case 'success':
this.element.addClass('updated');
break;
case 'system':
this.element.addClass('update-nag');
break;
case 'error':
this.element.addClass('error');
break;
}
// make the notice appear // make the notice appear
this.element.fadeIn(200); this.element.fadeIn(200);

View File

@@ -59,6 +59,63 @@ define(
}, },
]; ];
var messages = {
onDelete: function(response) {
var count = ~~response.subscribers;
var message = null;
if(count === 1) {
message = (
'1 subscriber was moved to the trash.'
).replace('%$1d', count);
} else if(count > 1) {
message = (
'%$1d subscribers were moved to the trash.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
},
onConfirmDelete: function(response) {
var count = ~~response.subscribers;
var message = null;
if(count === 1) {
message = (
'1 subscriber was permanently deleted.'
).replace('%$1d', count);
} else if(count > 1) {
message = (
'%$1d subscribers were permanently deleted.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
},
onRestore: function(response) {
var count = ~~response.subscribers;
var message = null;
if(count === 1) {
message = (
'1 subscriber has been restored from the trash.'
).replace('%$1d', count);
} else if(count > 1) {
message = (
'%$1d subscribers have been restored from the trash.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
}
};
var bulk_actions = [ var bulk_actions = [
{ {
name: 'moveToList', name: 'moveToList',
@@ -77,6 +134,13 @@ define(
return { return {
segment_id: ~~(jQuery('#move_to_segment').val()) segment_id: ~~(jQuery('#move_to_segment').val())
} }
},
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers were moved to list <strong>%$2s</strong>.'
.replace('%$1d', ~~response.subscribers)
.replace('%$2s', response.segment)
);
} }
}, },
{ {
@@ -96,6 +160,13 @@ define(
return { return {
segment_id: ~~(jQuery('#add_to_segment').val()) segment_id: ~~(jQuery('#add_to_segment').val())
} }
},
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers were added to list <strong>%$2s</strong>.'
.replace('%$1d', ~~response.subscribers)
.replace('%$2s', response.segment)
);
} }
}, },
{ {
@@ -115,11 +186,45 @@ define(
return { return {
segment_id: ~~(jQuery('#remove_from_segment').val()) segment_id: ~~(jQuery('#remove_from_segment').val())
} }
},
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers were removed from list <strong>%$2s</strong>.'
.replace('%$1d', ~~response.subscribers)
.replace('%$2s', response.segment)
);
}
},
{
name: 'removeFromAllLists',
label: 'Remove from all lists',
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers were removed from all lists.'
.replace('%$1d', ~~response.subscribers)
.replace('%$2s', response.segment)
);
}
},
{
name: 'confirmUnconfirmed',
label: 'Confirm unconfirmed',
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers have been confirmed.'
.replace('%$1d', ~~response.subscribers)
);
} }
}, },
{ {
name: 'trash', name: 'trash',
label: 'Trash' label: 'Trash',
getData: function() {
return {
confirm: false
}
},
onSuccess: messages.onDelete
} }
]; ];
@@ -153,14 +258,14 @@ define(
return segment.name; return segment.name;
}).join(', '); }).join(', ');
var row_actions = false;
return ( return (
<div> <div>
<td className={ row_classes }> <td className={ row_classes }>
<strong> <strong><Link to={ `/edit/${ subscriber.id }` }>
<Link to={ `/edit/${ subscriber.id }` }>
{ subscriber.email } { subscriber.email }
</Link> </Link></strong>
</strong>
{ actions } { actions }
</td> </td>
<td className="column" data-colname="First name"> <td className="column" data-colname="First name">
@@ -195,7 +300,9 @@ define(
endpoint="subscribers" endpoint="subscribers"
onRenderItem={ this.renderItem } onRenderItem={ this.renderItem }
columns={ columns } columns={ columns }
bulk_actions={ bulk_actions } /> bulk_actions={ bulk_actions }
messages={ messages }
/>
</div> </div>
); );
} }

View File

@@ -54,6 +54,7 @@ class Migrator {
'email varchar(150) NOT NULL,', 'email varchar(150) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "unconfirmed",', 'status varchar(12) NOT NULL DEFAULT "unconfirmed",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY email (email)' 'UNIQUE KEY email (email)'
@@ -82,6 +83,7 @@ class Migrator {
'preheader varchar(250) NOT NULL,', 'preheader varchar(250) NOT NULL,',
'body longtext,', 'body longtext,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)' 'PRIMARY KEY (id)'
); );
@@ -106,6 +108,7 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,', 'name varchar(90) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id),',
'UNIQUE KEY name (name)' 'UNIQUE KEY name (name)'
@@ -170,8 +173,7 @@ class Migrator {
'newsletter_type varchar(90) NOT NULL,', 'newsletter_type varchar(90) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),', 'PRIMARY KEY (id)'
'UNIQUE KEY name_newsletter_type (newsletter_type, name)'
); );
return $this->sqlify(__FUNCTION__, $attributes); return $this->sqlify(__FUNCTION__, $attributes);
} }

View File

@@ -71,10 +71,17 @@ class Handler {
return $this->model; return $this->model;
} }
function count() {
return (int)$this->model->count();
}
function getSelectionIds() { function getSelectionIds() {
$models = $this->getSelection()->select('id')->findMany(); $models = $this->getSelection()
->select('id')
->findArray();
return array_map(function($model) { return array_map(function($model) {
return (int)$model->id; return (int)$model['id'];
}, $models); }, $models);
} }

View File

@@ -69,18 +69,8 @@ class Subscriber extends Model {
foreach($filters as $filter) { foreach($filters as $filter) {
if($filter['name'] === 'segment') { if($filter['name'] === 'segment') {
$segment = Segment::findOne($filter['value']); $segment = Segment::findOne($filter['value']);
if($segment !== false) { if($segment !== false) {
/*$orm = $orm
->select(MP_SUBSCRIBERS_TABLE.'.*')
->select('subscriber_segment.id', 'subscriber_segment_id')
->join(
MP_SUBSCRIBER_SEGMENT_TABLE,
MP_SUBSCRIBERS_TABLE.'.id = subscriber_segment.subscriber_id',
'subscriber_segment'
)
->where('subscriber_segment.segment_id', (int)$filter['value']);*/
$orm = $segment->subscribers(); $orm = $segment->subscribers();
} }
} }
@@ -93,36 +83,48 @@ class Subscriber extends Model {
array( array(
'name' => 'all', 'name' => 'all',
'label' => __('All'), 'label' => __('All'),
'count' => Subscriber::count() 'count' => Subscriber::whereNull('deleted_at')->count()
), ),
array( array(
'name' => 'subscribed', 'name' => 'subscribed',
'label' => __('Subscribed'), 'label' => __('Subscribed'),
'count' => Subscriber::where('status', 'subscribed')->count() 'count' => Subscriber::whereNull('deleted_at')
->where('status', 'subscribed')
->count()
), ),
array( array(
'name' => 'unconfirmed', 'name' => 'unconfirmed',
'label' => __('Unconfirmed'), 'label' => __('Unconfirmed'),
'count' => Subscriber::where('status', 'unconfirmed')->count() 'count' => Subscriber::whereNull('deleted_at')
->where('status', 'unconfirmed')
->count()
), ),
array( array(
'name' => 'unsubscribed', 'name' => 'unsubscribed',
'label' => __('Unsubscribed'), 'label' => __('Unsubscribed'),
'count' => Subscriber::where('status', 'unsubscribed')->count() 'count' => Subscriber::whereNull('deleted_at')
->where('status', 'unsubscribed')
->count()
),
array(
'name' => 'trash',
'label' => __('Trash'),
'count' => Subscriber::whereNotNull('deleted_at')->count()
) )
); );
} }
static function groupBy($orm, $group = null) { static function groupBy($orm, $group = null) {
if($group === null or !in_array( if($group === 'trash') {
$group, return $orm->whereNotNull('deleted_at');
array('subscribed', 'unconfirmed', 'unsubscribed') } else {
)) { $orm = $orm->whereNull('deleted_at');
return $orm;
}
if(in_array($group, array('subscribed', 'unsubscribed', 'unconfirmed'))) {
return $orm->where('status', $group); return $orm->where('status', $group);
} }
}
}
static function filterWithCustomFields($orm) { static function filterWithCustomFields($orm) {
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*'); $orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
@@ -197,6 +199,7 @@ class Subscriber extends Model {
$segment = Segment::findOne($segment_id); $segment = Segment::findOne($segment_id);
if($segment !== false) { if($segment !== false) {
$subscribers_count = 0;
$subscribers = $listing->getSelection()->findMany(); $subscribers = $listing->getSelection()->findMany();
foreach($subscribers as $subscriber) { foreach($subscribers as $subscriber) {
// remove subscriber from all segments // remove subscriber from all segments
@@ -207,8 +210,13 @@ class Subscriber extends Model {
$association->subscriber_id = $subscriber->id; $association->subscriber_id = $subscriber->id;
$association->segment_id = $segment->id; $association->segment_id = $segment->id;
$association->save(); $association->save();
$subscribers_count++;
} }
return true; return array(
'subscribers' => $subscribers_count,
'segment' => $segment->name
);
} }
return false; return false;
} }
@@ -223,6 +231,67 @@ class Subscriber extends Model {
SubscriberSegment::whereIn('subscriber_id', $subscriber_ids) SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)
->where('segment_id', $segment->id) ->where('segment_id', $segment->id)
->deleteMany(); ->deleteMany();
return array(
'subscribers' => count($subscriber_ids),
'segment' => $segment->name
);
}
return false;
}
static function removeFromAllLists($listing) {
$segments = Segment::findMany();
$segment_ids = array_map(function($segment) {
return $segment->id();
}, $segments);
if(!empty($segment_ids)) {
// delete relations with segment
$subscriber_ids = $listing->getSelectionIds();
SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)
->whereIn('segment_id', $segment_ids)
->deleteMany();
return array(
'subscribers' => count($subscriber_ids)
);
}
return false;
}
static function confirmUnconfirmed($listing) {
$subscriber_ids = $listing->getSelectionIds();
$subscribers = Subscriber::whereIn('id', $subscriber_ids)
->where('status', 'unconfirmed')
->findMany();
if(!empty($subscribers)) {
$subscribers_count = 0;
foreach($subscribers as $subscriber) {
$subscriber->set('status', 'subscribed');
if($subscriber->save() === true) {
$subscribers_count++;
}
}
return array(
'subscribers' => $subscribers_count
);
}
return false;
}
static function resendConfirmationEmail($listing) {
$subscriber_ids = $listing->getSelectionIds();
$subscribers = Subscriber::whereIn('id', $subscriber_ids)
->where('status', 'unconfirmed')
->findMany();
if(!empty($subscribers)) {
foreach($subscribers as $subscriber) {
// TODO: resend confirmation email
}
return true; return true;
} }
return false; return false;
@@ -233,24 +302,64 @@ class Subscriber extends Model {
$segment = Segment::findOne($segment_id); $segment = Segment::findOne($segment_id);
if($segment !== false) { if($segment !== false) {
$subscribers_count = 0;
$subscribers = $listing->getSelection()->findMany(); $subscribers = $listing->getSelection()->findMany();
foreach($subscribers as $subscriber) { foreach($subscribers as $subscriber) {
// create relation with segment // create relation with segment
$association = \MailPoet\Models\SubscriberSegment::create(); $association = \MailPoet\Models\SubscriberSegment::create();
$association->subscriber_id = $subscriber->id; $association->subscriber_id = $subscriber->id;
$association->segment_id = $segment->id; $association->segment_id = $segment->id;
$association->save(); if($association->save()) {
$subscribers_count++;
} }
return true; }
return array(
'subscribers' => $subscribers_count,
'segment' => $segment->name
);
} }
return false; return false;
} }
static function trash($listing) { static function trash($listing, $data = array()) {
$confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
if($confirm_delete) {
// delete relations with all segments // delete relations with all segments
$subscriber_ids = $listing->getSelectionIds(); $subscribers = $listing->getSelection()->findResultSet();
\MailPoet\Models\SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)->deleteMany();
return $listing->getSelection()->deleteMany(); if(!empty($subscribers)) {
$subscribers_count = 0;
foreach($subscribers as $subscriber) {
if($subscriber->delete()) {
$subscribers_count++;
}
}
return array(
'subscribers' => $subscribers_count
);
}
return false;
} else {
// soft delete
$subscribers = $listing->getSelection()
->findResultSet()
->set_expr('deleted_at', 'NOW()')
->save();
return array(
'subscribers' => $subscribers->count()
);
}
}
static function restore($listing, $data = array()) {
$subscribers = $listing->getSelection()
->findResultSet()
->set_expr('deleted_at', 'NULL')
->save();
return array(
'subscribers' => $subscribers->count()
);
} }
} }

View File

@@ -53,10 +53,28 @@ class Subscribers {
wp_send_json($result); wp_send_json($result);
} }
function delete($id) { function restore($id) {
$subscriber = Subscriber::findOne($id); $subscriber = Subscriber::findOne($id);
if($subscriber !== false) { if($subscriber !== false) {
$result = $subscriber->delete(); $subscriber->set_expr('deleted_at', 'NULL');
$result = array('subscribers' => (int)$subscriber->save());
} else {
$result = false;
}
wp_send_json($result);
}
function delete($data = array()) {
$subscriber = Subscriber::findOne($data['id']);
$confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
if($subscriber !== false) {
if($confirm_delete) {
$subscriber->delete();
$result = array('subscribers' => 1);
} else {
$subscriber->set_expr('deleted_at', 'NOW()');
$result = array('subscribers' => (int)$subscriber->save());
}
} else { } else {
$result = false; $result = false;
} }

View File

@@ -1,10 +1,10 @@
<!-- system notices --> <!-- system notices -->
<div id="mailpoet_notice_system" class="mailpoet_notice update-nag" style="display:none;"></div> <div id="mailpoet_notice_system" class="mailpoet_notice" style="display:none;"></div>
<!-- main container --> <!-- main container -->
<div class="wrap"> <div class="wrap">
<!-- notices --> <!-- notices -->
<div id="mailpoet_notice_error" class="mailpoet_notice error" style="display:none;"></div> <div id="mailpoet_notice_error" class="mailpoet_notice" style="display:none;"></div>
<div id="mailpoet_notice_success" class="mailpoet_notice updated" style="display:none;"></div> <div id="mailpoet_notice_success" class="mailpoet_notice" style="display:none;"></div>
<!-- title block --> <!-- title block -->
<% block title %><% endblock %> <% block title %><% endblock %>

View File

@@ -47,7 +47,7 @@
<% include 'settings/bounce.html' %> <% include 'settings/bounce.html' %>
</div> </div>
<p class="submit mailpoet_settings_submit"> <p class="submit mailpoet_settings_submit" style="display:none;">
<input <input
type="submit" type="submit"
class="button button-primary" class="button button-primary"