implemented all bulk actions for Subscribers

This commit is contained in:
Jonathan Labreuille
2015-09-16 19:44:46 +02:00
parent 4e6768c4f1
commit cbcd614b6f
7 changed files with 319 additions and 29 deletions

View File

@ -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') {

View File

@ -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>
); );
} }

View File

@ -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 }

View File

@ -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'
} }
]; ];

View File

@ -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()
); );
} }
} }

View File

@ -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;

View File

@ -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);