diff --git a/assets/js/src/ajax.js b/assets/js/src/ajax.js index adc251e1e8..1e9b9d1208 100644 --- a/assets/js/src/ajax.js +++ b/assets/js/src/ajax.js @@ -35,18 +35,22 @@ define('ajax', ['mailpoet', 'jquery'], function(MailPoet, jQuery) { this.options.token = window.mailpoet_token; } }, - request: function(method, options) { - // set options - this.init(options); - - // set request params - var params = { + getParams: function() { + return { action: 'mailpoet', token: this.options.token, endpoint: this.options.endpoint, method: this.options.action, 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 if(method === 'get') { diff --git a/assets/js/src/listing/bulk_actions.jsx b/assets/js/src/listing/bulk_actions.jsx index b1ac6d06b9..a30b3c7981 100644 --- a/assets/js/src/listing/bulk_actions.jsx +++ b/assets/js/src/listing/bulk_actions.jsx @@ -1,21 +1,54 @@ -define(['react'], function(React) { - +define([ + 'react', + 'mailpoet' +], +function( + React, + MailPoet +) { var ListingBulkActions = React.createClass({ + getInitialState: function() { + return { + action: false, + extra: false + } + }, handleChangeAction: function(e) { + this.setState({ + action: e.target.value, + extra: false + }); + var action = this.getSelectedAction(); + // action on select callback if(action !== null && action['onSelect'] !== undefined) { - action.onSelect(e); + this.setState({ + extra: action.onSelect(e) + }); } }, handleApplyAction: function(e) { e.preventDefault(); 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) { - action.onApply(); + data.action = this.state.action; + + if(data.action) { + this.props.onBulkAction(selected_ids, data); } + + this.setState({ + action: false, + extra: false + }); }, getSelectedAction: function() { var selected_action = jQuery(this.refs.action.getDOMNode()).val(); @@ -43,13 +76,13 @@ define(['react'], function(React) { Select bulk action - {this.props.bulk_actions.map(function(action, index) { return ( ); }.bind(this))} @@ -59,6 +92,8 @@ define(['react'], function(React) { type="submit" defaultValue="Apply" className="button action" /> + + { this.state.extra } ); } diff --git a/assets/js/src/listing/listing.jsx b/assets/js/src/listing/listing.jsx index 22e0c0e658..694b67ab2e 100644 --- a/assets/js/src/listing/listing.jsx +++ b/assets/js/src/listing/listing.jsx @@ -200,6 +200,8 @@ define( getItems: function() { this.setState({ loading: true }); + this.clearSelection(); + MailPoet.Ajax.post({ endpoint: this.props.endpoint, action: 'listing', @@ -234,6 +236,27 @@ define( this.getItems(); }.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) { this.setState({ search: search, @@ -248,7 +271,6 @@ define( sort_by: sort_by, sort_order: sort_order, }, function() { - this.clearSelection(); this.getItems(); }.bind(this)); }, @@ -268,7 +290,6 @@ define( }, handleSelectItems: function(is_checked) { if(is_checked === false) { - this.clearSelection(); } else { var selected_ids = this.state.items.map(function(item) { return ~~item.id; @@ -282,7 +303,6 @@ define( }, handleSelectAll: function() { if(this.state.selection === 'all') { - this.clearSelection(); } else { this.setState({ selection: 'all', @@ -306,7 +326,6 @@ define( search: '', page: 1 }, function() { - this.clearSelection(); this.getItems(); }.bind(this)); }, @@ -354,7 +373,10 @@ define( search={ this.state.search } />
+ bulk_actions={ bulk_actions } + selection={ this.state.selection } + selected_ids={ this.state.selected_ids } + onBulkAction={ this.handleBulkAction } /> 0 } onSelectItem={ this.handleSelectItem } onSelectAll={ this.handleSelectAll } - selected_ids={ this.state.selected_ids } selection={ this.state.selection } + selected_ids={ this.state.selected_ids } loading={ this.state.loading } count={ this.state.count } limit={ this.state.limit } @@ -402,7 +424,10 @@ define(
+ bulk_actions={ bulk_actions } + selection={ this.state.selection } + selected_ids={ this.state.selected_ids } + onBulkAction={ this.handleBulkAction } /> + { item.name } + + ); + }); + + return ( + + ); + } + }); + var bulk_actions = [ { name: 'move', - label: 'Move to list...' + label: 'Move to list...', + onSelect: function() { + return ( + + ); + }, + getData: function() { + return { + segment_id: ~~(jQuery('#move_to_segment').val()) + } + } }, { name: 'add', - label: 'Add to list...' + label: 'Add to list...', + onSelect: function() { + return ( + + ); + }, + getData: function() { + return { + segment_id: ~~(jQuery('#add_to_segment').val()) + } + } }, { name: 'remove', - label: 'Remove from list...' + label: 'Remove from list...', + onSelect: function() { + return ( + + ); + }, + getData: function() { + return { + segment_id: ~~(jQuery('#remove_from_segment').val()) + } + } + }, + { + name: 'trash', + label: 'Trash' } ]; diff --git a/lib/Listing/Handler.php b/lib/Listing/Handler.php index 1289e01894..5f6a21f862 100644 --- a/lib/Listing/Handler.php +++ b/lib/Listing/Handler.php @@ -29,7 +29,7 @@ class Handler { } private function setSearch() { - if($this->data['search'] === null) { + if(empty($this->data['search'])) { return; } return $this->model->filter('search', $this->data['search']); @@ -47,6 +47,20 @@ class Handler { 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() { return array( 'count' => $this->model->count(), @@ -55,7 +69,7 @@ class Handler { 'items' => $this->model ->offset($this->data['offset']) ->limit($this->data['limit']) - ->find_array() + ->findArray() ); } } \ No newline at end of file diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php index a3e0e399ff..7a55b083de 100644 --- a/lib/Models/Subscriber.php +++ b/lib/Models/Subscriber.php @@ -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 = '') { if(strlen(trim($search) === 0)) { return $orm; diff --git a/lib/Router/Subscribers.php b/lib/Router/Subscribers.php index af48cc18fe..16dc711fa2 100644 --- a/lib/Router/Subscribers.php +++ b/lib/Router/Subscribers.php @@ -3,6 +3,7 @@ namespace MailPoet\Router; use MailPoet\Listing; use MailPoet\Models\Subscriber; +use MailPoet\Models\SubscriberSegment; if(!defined('ABSPATH')) exit; @@ -29,6 +30,69 @@ class Subscribers { 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() { $collection = Subscriber::findArray(); wp_send_json($collection);