implemented all bulk actions for Subscribers
This commit is contained in:
@ -35,18 +35,22 @@ define('ajax', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
this.options.token = window.mailpoet_token;
|
this.options.token = window.mailpoet_token;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
request: function(method, options) {
|
getParams: function() {
|
||||||
// set options
|
return {
|
||||||
this.init(options);
|
|
||||||
|
|
||||||
// set request params
|
|
||||||
var params = {
|
|
||||||
action: 'mailpoet',
|
action: 'mailpoet',
|
||||||
token: this.options.token,
|
token: this.options.token,
|
||||||
endpoint: this.options.endpoint,
|
endpoint: this.options.endpoint,
|
||||||
method: this.options.action,
|
method: this.options.action,
|
||||||
data: this.options.data || {}
|
data: this.options.data || {}
|
||||||
}, jqXHR;
|
}
|
||||||
|
},
|
||||||
|
request: function(method, options) {
|
||||||
|
// set options
|
||||||
|
this.init(options);
|
||||||
|
|
||||||
|
// set request params
|
||||||
|
var params = this.getParams();
|
||||||
|
var jqXHR;
|
||||||
|
|
||||||
// make ajax request depending on method
|
// make ajax request depending on method
|
||||||
if(method === 'get') {
|
if(method === 'get') {
|
||||||
|
@ -1,21 +1,54 @@
|
|||||||
define(['react'], function(React) {
|
define([
|
||||||
|
'react',
|
||||||
|
'mailpoet'
|
||||||
|
],
|
||||||
|
function(
|
||||||
|
React,
|
||||||
|
MailPoet
|
||||||
|
) {
|
||||||
var ListingBulkActions = React.createClass({
|
var ListingBulkActions = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
action: false,
|
||||||
|
extra: false
|
||||||
|
}
|
||||||
|
},
|
||||||
handleChangeAction: function(e) {
|
handleChangeAction: function(e) {
|
||||||
|
this.setState({
|
||||||
|
action: e.target.value,
|
||||||
|
extra: false
|
||||||
|
});
|
||||||
|
|
||||||
var action = this.getSelectedAction();
|
var action = this.getSelectedAction();
|
||||||
|
|
||||||
|
// action on select callback
|
||||||
if(action !== null && action['onSelect'] !== undefined) {
|
if(action !== null && action['onSelect'] !== undefined) {
|
||||||
action.onSelect(e);
|
this.setState({
|
||||||
|
extra: action.onSelect(e)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleApplyAction: function(e) {
|
handleApplyAction: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var action = this.getSelectedAction();
|
var action = this.getSelectedAction();
|
||||||
|
var selected_ids = (this.props.selection !== 'all')
|
||||||
|
? this.props.selected_ids
|
||||||
|
: [];
|
||||||
|
var data = (action['getData'] !== undefined)
|
||||||
|
? action.getData()
|
||||||
|
: {};
|
||||||
|
|
||||||
if(action !== null && action['onApply'] !== undefined) {
|
data.action = this.state.action;
|
||||||
action.onApply();
|
|
||||||
|
if(data.action) {
|
||||||
|
this.props.onBulkAction(selected_ids, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
action: false,
|
||||||
|
extra: false
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getSelectedAction: function() {
|
getSelectedAction: function() {
|
||||||
var selected_action = jQuery(this.refs.action.getDOMNode()).val();
|
var selected_action = jQuery(this.refs.action.getDOMNode()).val();
|
||||||
@ -43,13 +76,13 @@ define(['react'], function(React) {
|
|||||||
Select bulk action
|
Select bulk action
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<select ref="action" onChange={this.handleChangeAction}>
|
<select ref="action" value={ this.state.action } onChange={this.handleChangeAction}>
|
||||||
<option value="">Bulk Actions</option>
|
<option value="">Bulk Actions</option>
|
||||||
{this.props.bulk_actions.map(function(action, index) {
|
{this.props.bulk_actions.map(function(action, index) {
|
||||||
return (
|
return (
|
||||||
<option
|
<option
|
||||||
value={action.name}
|
value={ action.name }
|
||||||
key={index}
|
key={ 'action-' + index }
|
||||||
>{ action.label }</option>
|
>{ action.label }</option>
|
||||||
);
|
);
|
||||||
}.bind(this))}
|
}.bind(this))}
|
||||||
@ -59,6 +92,8 @@ define(['react'], function(React) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
defaultValue="Apply"
|
defaultValue="Apply"
|
||||||
className="button action" />
|
className="button action" />
|
||||||
|
|
||||||
|
{ this.state.extra }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,8 @@ define(
|
|||||||
getItems: function() {
|
getItems: function() {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
this.clearSelection();
|
||||||
|
|
||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'listing',
|
action: 'listing',
|
||||||
@ -234,6 +236,27 @@ define(
|
|||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
handleBulkAction: function(selected_ids, params) {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
var data = params || {};
|
||||||
|
|
||||||
|
data.selection = selected_ids;
|
||||||
|
data.listing = {
|
||||||
|
offset: 0,
|
||||||
|
limit: 0,
|
||||||
|
group: this.state.group,
|
||||||
|
search: this.state.search
|
||||||
|
}
|
||||||
|
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
endpoint: this.props.endpoint,
|
||||||
|
action: 'bulk_action',
|
||||||
|
data: data
|
||||||
|
}).done(function() {
|
||||||
|
this.getItems();
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
handleSearch: function(search) {
|
handleSearch: function(search) {
|
||||||
this.setState({
|
this.setState({
|
||||||
search: search,
|
search: search,
|
||||||
@ -248,7 +271,6 @@ define(
|
|||||||
sort_by: sort_by,
|
sort_by: sort_by,
|
||||||
sort_order: sort_order,
|
sort_order: sort_order,
|
||||||
}, function() {
|
}, function() {
|
||||||
this.clearSelection();
|
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -268,7 +290,6 @@ define(
|
|||||||
},
|
},
|
||||||
handleSelectItems: function(is_checked) {
|
handleSelectItems: function(is_checked) {
|
||||||
if(is_checked === false) {
|
if(is_checked === false) {
|
||||||
this.clearSelection();
|
|
||||||
} else {
|
} else {
|
||||||
var selected_ids = this.state.items.map(function(item) {
|
var selected_ids = this.state.items.map(function(item) {
|
||||||
return ~~item.id;
|
return ~~item.id;
|
||||||
@ -282,7 +303,6 @@ define(
|
|||||||
},
|
},
|
||||||
handleSelectAll: function() {
|
handleSelectAll: function() {
|
||||||
if(this.state.selection === 'all') {
|
if(this.state.selection === 'all') {
|
||||||
this.clearSelection();
|
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
selection: 'all',
|
selection: 'all',
|
||||||
@ -306,7 +326,6 @@ define(
|
|||||||
search: '',
|
search: '',
|
||||||
page: 1
|
page: 1
|
||||||
}, function() {
|
}, function() {
|
||||||
this.clearSelection();
|
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -354,7 +373,10 @@ define(
|
|||||||
search={ this.state.search } />
|
search={ this.state.search } />
|
||||||
<div className="tablenav top clearfix">
|
<div className="tablenav top clearfix">
|
||||||
<ListingBulkActions
|
<ListingBulkActions
|
||||||
bulk_actions={ bulk_actions } />
|
bulk_actions={ bulk_actions }
|
||||||
|
selection={ this.state.selection }
|
||||||
|
selected_ids={ this.state.selected_ids }
|
||||||
|
onBulkAction={ this.handleBulkAction } />
|
||||||
<ListingFilters filters={ this.state.filters } />
|
<ListingFilters filters={ this.state.filters } />
|
||||||
<ListingPages
|
<ListingPages
|
||||||
count={ this.state.count }
|
count={ this.state.count }
|
||||||
@ -381,8 +403,8 @@ define(
|
|||||||
is_selectable={ bulk_actions.length > 0 }
|
is_selectable={ bulk_actions.length > 0 }
|
||||||
onSelectItem={ this.handleSelectItem }
|
onSelectItem={ this.handleSelectItem }
|
||||||
onSelectAll={ this.handleSelectAll }
|
onSelectAll={ this.handleSelectAll }
|
||||||
selected_ids={ this.state.selected_ids }
|
|
||||||
selection={ this.state.selection }
|
selection={ this.state.selection }
|
||||||
|
selected_ids={ this.state.selected_ids }
|
||||||
loading={ this.state.loading }
|
loading={ this.state.loading }
|
||||||
count={ this.state.count }
|
count={ this.state.count }
|
||||||
limit={ this.state.limit }
|
limit={ this.state.limit }
|
||||||
@ -402,7 +424,10 @@ define(
|
|||||||
</table>
|
</table>
|
||||||
<div className="tablenav bottom">
|
<div className="tablenav bottom">
|
||||||
<ListingBulkActions
|
<ListingBulkActions
|
||||||
bulk_actions={ bulk_actions } />
|
bulk_actions={ bulk_actions }
|
||||||
|
selection={ this.state.selection }
|
||||||
|
selected_ids={ this.state.selected_ids }
|
||||||
|
onBulkAction={ this.handleBulkAction } />
|
||||||
<ListingPages
|
<ListingPages
|
||||||
count={ this.state.count }
|
count={ this.state.count }
|
||||||
page={ this.state.page }
|
page={ this.state.page }
|
||||||
|
@ -2,12 +2,17 @@ define(
|
|||||||
[
|
[
|
||||||
'react',
|
'react',
|
||||||
'listing/listing.jsx',
|
'listing/listing.jsx',
|
||||||
'classnames'
|
'classnames',
|
||||||
|
'mailpoet',
|
||||||
|
'jquery',
|
||||||
|
'select2'
|
||||||
],
|
],
|
||||||
function(
|
function(
|
||||||
React,
|
React,
|
||||||
Listing,
|
Listing,
|
||||||
classNames
|
classNames,
|
||||||
|
MailPoet,
|
||||||
|
jQuery
|
||||||
) {
|
) {
|
||||||
var columns = [
|
var columns = [
|
||||||
{
|
{
|
||||||
@ -42,18 +47,154 @@ define(
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var ItemSelection = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
items: [],
|
||||||
|
selected: false,
|
||||||
|
multiple: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.loadItems();
|
||||||
|
},
|
||||||
|
loadItems: function() {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
endpoint: this.props.endpoint,
|
||||||
|
action: 'listing',
|
||||||
|
data: {
|
||||||
|
'offset': 0,
|
||||||
|
'limit': 5,
|
||||||
|
'search': '',
|
||||||
|
'sort_by': 'name',
|
||||||
|
'sort_order': 'asc'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function(response) {
|
||||||
|
if(this.isMounted()) {
|
||||||
|
if(response === false) {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
items: []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
items: response.items
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
handleChange: function() {
|
||||||
|
var new_value = this.refs.selection.getDOMNode().value;
|
||||||
|
|
||||||
|
if(this.state.multiple === false) {
|
||||||
|
if(new_value.trim().length === 0) {
|
||||||
|
new_value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
selected: new_value
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var selected_values = this.state.selected || [];
|
||||||
|
|
||||||
|
if(selected_values.indexOf(new_value) !== -1) {
|
||||||
|
// value already present so remove it
|
||||||
|
selected_values.splice(selected_values.indexOf(new_value), 1);
|
||||||
|
} else {
|
||||||
|
selected_values.push(new_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
selected: selected_values
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getSelected: function() {
|
||||||
|
return this.state.selected;
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var options = this.state.items.map(function(item, index) {
|
||||||
|
return (
|
||||||
|
<option
|
||||||
|
key={ 'action-' + index }
|
||||||
|
value={ item.id }>
|
||||||
|
{ item.name }
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
ref="selection"
|
||||||
|
id={ this.props.id }
|
||||||
|
value={ this.state.selected }
|
||||||
|
onChange={ this.handleChange }
|
||||||
|
multiple={ this.state.multiple }>
|
||||||
|
<option value="">Select a list</option>
|
||||||
|
{ options }
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var bulk_actions = [
|
var bulk_actions = [
|
||||||
{
|
{
|
||||||
name: 'move',
|
name: 'move',
|
||||||
label: 'Move to list...'
|
label: 'Move to list...',
|
||||||
|
onSelect: function() {
|
||||||
|
return (
|
||||||
|
<ItemSelection
|
||||||
|
endpoint="segments"
|
||||||
|
id="move_to_segment" />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#move_to_segment').val())
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'add',
|
name: 'add',
|
||||||
label: 'Add to list...'
|
label: 'Add to list...',
|
||||||
|
onSelect: function() {
|
||||||
|
return (
|
||||||
|
<ItemSelection
|
||||||
|
endpoint="segments"
|
||||||
|
id="add_to_segment" />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#add_to_segment').val())
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
label: 'Remove from list...'
|
label: 'Remove from list...',
|
||||||
|
onSelect: function() {
|
||||||
|
return (
|
||||||
|
<ItemSelection
|
||||||
|
endpoint="segments"
|
||||||
|
id="remove_from_segment" />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#remove_from_segment').val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash',
|
||||||
|
label: 'Trash'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function setSearch() {
|
private function setSearch() {
|
||||||
if($this->data['search'] === null) {
|
if(empty($this->data['search'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return $this->model->filter('search', $this->data['search']);
|
return $this->model->filter('search', $this->data['search']);
|
||||||
@ -47,6 +47,20 @@ class Handler {
|
|||||||
return $this->model->filter('group', $this->data['group']);
|
return $this->model->filter('group', $this->data['group']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelection($ids = array()) {
|
||||||
|
if(!empty($ids)) {
|
||||||
|
$this->model->whereIn('id', $ids);
|
||||||
|
}
|
||||||
|
return $this->model;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectionIds($ids = array()) {
|
||||||
|
$subscribers = $this->getSelection($ids)->select('id')->findMany();
|
||||||
|
return array_map(function($subscriber) {
|
||||||
|
return (int)$subscriber->id;
|
||||||
|
}, $subscribers);
|
||||||
|
}
|
||||||
|
|
||||||
function get() {
|
function get() {
|
||||||
return array(
|
return array(
|
||||||
'count' => $this->model->count(),
|
'count' => $this->model->count(),
|
||||||
@ -55,7 +69,7 @@ class Handler {
|
|||||||
'items' => $this->model
|
'items' => $this->model
|
||||||
->offset($this->data['offset'])
|
->offset($this->data['offset'])
|
||||||
->limit($this->data['limit'])
|
->limit($this->data['limit'])
|
||||||
->find_array()
|
->findArray()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,6 +15,13 @@ class Subscriber extends Model {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
// delete all relations to segments
|
||||||
|
SubscriberSegment::where('subscriber_id', $this->id)->deleteMany();
|
||||||
|
|
||||||
|
parent::delete();
|
||||||
|
}
|
||||||
|
|
||||||
static function search($orm, $search = '') {
|
static function search($orm, $search = '') {
|
||||||
if(strlen(trim($search) === 0)) {
|
if(strlen(trim($search) === 0)) {
|
||||||
return $orm;
|
return $orm;
|
||||||
|
@ -3,6 +3,7 @@ namespace MailPoet\Router;
|
|||||||
|
|
||||||
use MailPoet\Listing;
|
use MailPoet\Listing;
|
||||||
use MailPoet\Models\Subscriber;
|
use MailPoet\Models\Subscriber;
|
||||||
|
use MailPoet\Models\SubscriberSegment;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
@ -29,6 +30,69 @@ class Subscribers {
|
|||||||
wp_send_json($listing->get());
|
wp_send_json($listing->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bulk_action($data = array()) {
|
||||||
|
$action = $data['action'];
|
||||||
|
$selection = (isset($data['selection']) ? $data['selection'] : null);
|
||||||
|
$listing_data = $data['listing'];
|
||||||
|
|
||||||
|
$listing = new Listing\Handler(
|
||||||
|
\Model::factory('\MailPoet\Models\Subscriber'),
|
||||||
|
$listing_data
|
||||||
|
);
|
||||||
|
|
||||||
|
$selected = $listing->getSelection($selection);
|
||||||
|
$subscribers = $selected->findMany();
|
||||||
|
|
||||||
|
$result = false;
|
||||||
|
switch($action) {
|
||||||
|
case 'move':
|
||||||
|
$segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0);
|
||||||
|
foreach($subscribers as $subscriber) {
|
||||||
|
// remove subscriber from all segments
|
||||||
|
SubscriberSegment::where('subscriber_id', $subscriber->id)->deleteMany();
|
||||||
|
|
||||||
|
// create relation with segment
|
||||||
|
$association = SubscriberSegment::create();
|
||||||
|
$association->subscriber_id = $subscriber->id;
|
||||||
|
$association->segment_id = $segment_id;
|
||||||
|
$association->save();
|
||||||
|
}
|
||||||
|
$result = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'remove':
|
||||||
|
$segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0);
|
||||||
|
// delete relations with segment
|
||||||
|
$subscriber_ids = $listing->getSelectionIds($selection);
|
||||||
|
$result = SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)
|
||||||
|
->where('segment_id', $segment_id)
|
||||||
|
->deleteMany();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'add':
|
||||||
|
$segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0);
|
||||||
|
foreach($subscribers as $subscriber) {
|
||||||
|
// create relation with segment
|
||||||
|
$association = SubscriberSegment::create();
|
||||||
|
$association->subscriber_id = $subscriber->id;
|
||||||
|
$association->segment_id = $segment_id;
|
||||||
|
$association->save();
|
||||||
|
}
|
||||||
|
$result = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'trash':
|
||||||
|
// delete relations with all segments
|
||||||
|
$subscriber_ids = $listing->getSelectionIds($selection);
|
||||||
|
SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)->deleteMany();
|
||||||
|
|
||||||
|
$result = $selected->deleteMany();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json($result);
|
||||||
|
}
|
||||||
|
|
||||||
function getAll() {
|
function getAll() {
|
||||||
$collection = Subscriber::findArray();
|
$collection = Subscriber::findArray();
|
||||||
wp_send_json($collection);
|
wp_send_json($collection);
|
||||||
|
Reference in New Issue
Block a user