diff --git a/assets/js/src/form/form.jsx b/assets/js/src/form/form.jsx index e6d25d42b7..ff44795b6f 100644 --- a/assets/js/src/form/form.jsx +++ b/assets/js/src/form/form.jsx @@ -276,7 +276,11 @@ define( this.props.messages['created'](); } } else { - this.setState({ errors: response }); + if(response === false) { + // unknown error occurred + } else { + this.setState({ errors: response }); + } } }.bind(this)); }, diff --git a/assets/js/src/listing/listing.jsx b/assets/js/src/listing/listing.jsx index 694b67ab2e..2163667427 100644 --- a/assets/js/src/listing/listing.jsx +++ b/assets/js/src/listing/listing.jsx @@ -241,12 +241,12 @@ define( var data = params || {}; - data.selection = selected_ids; data.listing = { offset: 0, limit: 0, group: this.state.group, - search: this.state.search + search: this.state.search, + selection: selected_ids } MailPoet.Ajax.post({ diff --git a/assets/js/src/newsletters/list.jsx b/assets/js/src/newsletters/list.jsx index d0688675b9..f15dd7c48e 100644 --- a/assets/js/src/newsletters/list.jsx +++ b/assets/js/src/newsletters/list.jsx @@ -27,6 +27,13 @@ define( } ]; + var bulk_actions = [ + { + name: 'trash', + label: 'Trash' + } + ]; + var NewsletterList = React.createClass({ renderItem: function(newsletter, actions) { var rowClasses = classNames( @@ -57,8 +64,8 @@ define( + columns={columns} + bulk_actions={ bulk_actions } /> ); } }); diff --git a/assets/js/src/segments/list.jsx b/assets/js/src/segments/list.jsx index d38c7a8944..25fc720bee 100644 --- a/assets/js/src/segments/list.jsx +++ b/assets/js/src/segments/list.jsx @@ -27,6 +27,13 @@ define( } ]; + var bulk_actions = [ + { + name: 'trash', + label: 'Trash' + } + ]; + var SegmentList = React.createClass({ renderItem: function(segment, actions) { var rowClasses = classNames( @@ -57,8 +64,8 @@ define( + columns={columns} + bulk_actions={ bulk_actions } /> ); } }); diff --git a/assets/js/src/subscribers/list.jsx b/assets/js/src/subscribers/list.jsx index 5512bfcbe9..0424b2b863 100644 --- a/assets/js/src/subscribers/list.jsx +++ b/assets/js/src/subscribers/list.jsx @@ -67,7 +67,7 @@ define( action: 'listing', data: { 'offset': 0, - 'limit': 5, + 'limit': 100, 'search': '', 'sort_by': 'name', 'sort_order': 'asc' @@ -145,7 +145,7 @@ define( var bulk_actions = [ { - name: 'move', + name: 'moveToList', label: 'Move to list...', onSelect: function() { return ( @@ -161,7 +161,7 @@ define( } }, { - name: 'add', + name: 'addToList', label: 'Add to list...', onSelect: function() { return ( @@ -177,7 +177,7 @@ define( } }, { - name: 'remove', + name: 'removeFromList', label: 'Remove from list...', onSelect: function() { return ( @@ -198,7 +198,7 @@ define( } ]; - var List = React.createClass({ + var SubscriberList = React.createClass({ renderItem: function(subscriber, actions) { var row_classes = classNames( 'manage-column', @@ -259,6 +259,6 @@ define( } }); - return List; + return SubscriberList; } ); \ No newline at end of file diff --git a/lib/Config/Initializer.php b/lib/Config/Initializer.php index 2c9aed8f8a..b8f5c3fd2c 100644 --- a/lib/Config/Initializer.php +++ b/lib/Config/Initializer.php @@ -29,6 +29,9 @@ class Initializer { \ORM::configure('username', Env::$db_username); \ORM::configure('password', Env::$db_password); \ORM::configure('logging', WP_DEBUG); + \ORM::configure('driver_options', array( + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + )); $subscribers = Env::$db_prefix . 'subscribers'; $settings = Env::$db_prefix . 'settings'; diff --git a/lib/Listing/BulkAction.php b/lib/Listing/BulkAction.php new file mode 100644 index 0000000000..4b0f91466c --- /dev/null +++ b/lib/Listing/BulkAction.php @@ -0,0 +1,28 @@ +model = $model; + $this->data = $data; + + $this->listing = new Handler( + \Model::factory($this->model), + $this->data['listing'] + ); + return $this; + } + + function apply() { + return call_user_func_array( + array($this->model, $this->data['action']), + array($this->listing, $this->data) + ); + } +} \ No newline at end of file diff --git a/lib/Listing/Handler.php b/lib/Listing/Handler.php index 5f6a21f862..7a6db80ead 100644 --- a/lib/Listing/Handler.php +++ b/lib/Listing/Handler.php @@ -20,7 +20,9 @@ class Handler { 'sort_by' => (isset($data['sort_by']) ? $data['sort_by'] : 'id'), 'sort_order' => (isset($data['sort_order']) ? $data['sort_order'] : 'asc'), // grouping - 'group' => (isset($data['group']) ? $data['group'] : null) + 'group' => (isset($data['group']) ? $data['group'] : null), + // selection + 'selection' => (isset($data['selection']) ? $data['selection'] : null) ); $this->setSearch(); @@ -47,18 +49,18 @@ class Handler { return $this->model->filter('group', $this->data['group']); } - function getSelection($ids = array()) { - if(!empty($ids)) { - $this->model->whereIn('id', $ids); + function getSelection() { + if(!empty($this->data['selection'])) { + $this->model->whereIn('id', $this->data['selection']); } 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 getSelectionIds() { + $models = $this->getSelection()->select('id')->findMany(); + return array_map(function($model) { + return (int)$model->id; + }, $models); } function get() { diff --git a/lib/Models/Newsletter.php b/lib/Models/Newsletter.php index 2f2d915fca..5281a2122f 100644 --- a/lib/Models/Newsletter.php +++ b/lib/Models/Newsletter.php @@ -48,10 +48,18 @@ class Newsletter extends Model { $saved = $newsletter->save(); - if($saved === false) { - return $newsletter->getValidationErrors(); - } else { + if($saved === true) { return true; + } else { + $errors = $newsletter->getValidationErrors(); + if(!empty($errors)) { + return $errors; + } } + return false; + } + + static function trash($listing) { + return $listing->getSelection()->deleteMany(); } } diff --git a/lib/Models/Segment.php b/lib/Models/Segment.php index d03242e8fc..80b6330a63 100644 --- a/lib/Models/Segment.php +++ b/lib/Models/Segment.php @@ -57,10 +57,18 @@ class Segment extends Model { $saved = $segment->save(); - if($saved === false) { - return $segment->getValidationErrors(); - } else { + if($saved === true) { return true; + } else { + $errors = $segment->getValidationErrors(); + if(!empty($errors)) { + return $errors; + } } + return false; + } + + static function trash($listing) { + return $listing->getSelection()->deleteMany(); } } diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php index 7a55b083de..bf48e9ab0f 100644 --- a/lib/Models/Subscriber.php +++ b/lib/Models/Subscriber.php @@ -15,7 +15,7 @@ class Subscriber extends Model { )); } - function delete() { + function delete() { // delete all relations to segments SubscriberSegment::where('subscriber_id', $this->id)->deleteMany(); @@ -95,10 +95,76 @@ class Subscriber extends Model { $saved = $subscriber->save(); - if($saved === false) { - return $subscriber->getValidationErrors(); + if($saved === true) { + return true; } else { + $errors = $subscriber->getValidationErrors(); + if(!empty($errors)) { + return $errors; + } + } + return false; + } + + static function moveToList($listing, $data = array()) { + $segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0); + $segment = Segment::findOne($segment_id); + + if($segment !== false) { + $subscribers = $listing->getSelection()->findMany(); + 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(); + } return true; } + return false; + } + + static function removeFromList($listing, $data = array()) { + $segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0); + $segment = Segment::findOne($segment_id); + + if($segment !== false) { + // delete relations with segment + $subscriber_ids = $listing->getSelectionIds(); + SubscriberSegment::whereIn('subscriber_id', $subscriber_ids) + ->where('segment_id', $segment->id) + ->deleteMany(); + return true; + } + return false; + } + + static function addToList($listing, $data = array()) { + $segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0); + $segment = Segment::findOne($segment_id); + + if($segment !== false) { + $subscribers = $listing->getSelection()->findMany(); + foreach($subscribers as $subscriber) { + // create relation with segment + $association = \MailPoet\Models\SubscriberSegment::create(); + $association->subscriber_id = $subscriber->id; + $association->segment_id = $segment->id; + $association->save(); + } + return true; + } + return false; + } + + static function trash($listing) { + // delete relations with all segments + $subscriber_ids = $listing->getSelectionIds(); + \MailPoet\Models\SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)->deleteMany(); + + return $listing->getSelection()->deleteMany(); } } diff --git a/lib/Router/Newsletters.php b/lib/Router/Newsletters.php index dcca8b14dd..5afb79dd8a 100644 --- a/lib/Router/Newsletters.php +++ b/lib/Router/Newsletters.php @@ -62,4 +62,13 @@ class Newsletters { $mailer = new Bridge($newsletter, $subscribers); wp_send_json($mailer->send()); } + + function bulk_action($data = array()) { + $bulk_action = new Listing\BulkAction( + '\MailPoet\Models\Newsletter', + $data + ); + + wp_send_json($bulk_action->apply()); + } } diff --git a/lib/Router/Router.php b/lib/Router/Router.php index 4110711a01..3742c7738f 100644 --- a/lib/Router/Router.php +++ b/lib/Router/Router.php @@ -24,7 +24,7 @@ class Router { $class = ucfirst($_POST['endpoint']); $endpoint = __NAMESPACE__ . "\\" . $class; $method = $_POST['method']; - $data = isset($_POST['data']) ? $_POST['data'] : array(); + $data = isset($_POST['data']) ? stripslashes_deep($_POST['data']) : array(); $endpoint = new $endpoint(); $endpoint->$method($data); } diff --git a/lib/Router/Segments.php b/lib/Router/Segments.php index 8b5113b79f..b0936048a4 100644 --- a/lib/Router/Segments.php +++ b/lib/Router/Segments.php @@ -53,4 +53,13 @@ class Segments { wp_send_json($result); } + + function bulk_action($data = array()) { + $bulk_action = new Listing\BulkAction( + '\MailPoet\Models\Segment', + $data + ); + + wp_send_json($bulk_action->apply()); + } } diff --git a/lib/Router/Subscribers.php b/lib/Router/Subscribers.php index 16dc711fa2..0b00a308b4 100644 --- a/lib/Router/Subscribers.php +++ b/lib/Router/Subscribers.php @@ -30,69 +30,6 @@ 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); @@ -112,4 +49,13 @@ class Subscribers { } wp_send_json($result); } + + function bulk_action($data = array()) { + $bulk_action = new Listing\BulkAction( + '\MailPoet\Models\Subscriber', + $data + ); + + wp_send_json($bulk_action->apply()); + } } diff --git a/lib/Util/Sudzy/ValidModel.php b/lib/Util/Sudzy/ValidModel.php index 2406ffaaa0..d195af541c 100644 --- a/lib/Util/Sudzy/ValidModel.php +++ b/lib/Util/Sudzy/ValidModel.php @@ -14,35 +14,35 @@ abstract class ValidModel extends \Model // + ON_SET throws immediately when field is set() // + ON_SAVE throws on save() // + NEVER means an exception is never thrown; check for ->getValidaionErrors() - ); + ); const ON_SET = 'set'; const ON_SAVE = 'save'; const NEVER = null; public function __construct($validatorInstance = null) { - $this->_validator = $validatorInstance; + $this->_validator = $validatorInstance; } public function setValidationOptions($options) { - $this->_validationOptions = array_merge($this->_validationOptions, $options); + $this->_validationOptions = array_merge($this->_validationOptions, $options); } public function addValidation($field, $validation, $message) { - if (!isset($this->_validations[$field])) { - $this->_validations[$field] = array(); - } - $this->_validations[$field][] = array( - 'validation' => $validation, - 'message' => $message + if (!isset($this->_validations[$field])) { + $this->_validations[$field] = array(); + } + $this->_validations[$field][] = array( + 'validation' => $validation, + 'message' => $message ); } public function addValidations($field, $validators) { - foreach ($validators as $validation => $message) { - $this->addValidation($field, $validation, $message); - } + foreach ($validators as $validation => $message) { + $this->addValidation($field, $validation, $message); + } } // /** @@ -65,39 +65,39 @@ abstract class ValidModel extends \Model **/ public function validateField($field, $value) { - $this->setupValidationEngine(); + $this->setupValidationEngine(); - if (!isset($this->_validations[$field])) { + if (!isset($this->_validations[$field])) { return true; // No validations, return true by default + } + + $success = true; + foreach ($this->_validations[$field] as $v) { + $checks = explode(' ', $v['validation']); + foreach ($checks as $check) { + $params = explode('|', $check); + $check = array_shift($params); + + if ($this->_validator->executeOne($check, $value, $params)) { + $success = $success && true; + } else { + $this->addValidationError($v['message'], $field); + $success = false; + } + } + } + return $success; } - $success = true; - foreach ($this->_validations[$field] as $v) { - $checks = explode(' ', $v['validation']); - foreach ($checks as $check) { - $params = explode('|', $check); - $check = array_shift($params); - - if ($this->_validator->executeOne($check, $value, $params)) { - $success = $success && true; - } else { - $this->addValidationError($v['message'], $field); - $success = false; - } - } + public function getValidationErrors() + { + return $this->_validationErrors; } - return $success; - } - public function getValidationErrors() - { - return $this->_validationErrors; - } - - public function resetValidationErrors() - { - $this->_validationErrors = array(); - } + public function resetValidationErrors() + { + $this->_validationErrors = array(); + } /////////////////// // Overloaded methods @@ -107,7 +107,7 @@ abstract class ValidModel extends \Model */ public function __set($name, $value) { - $this->validateAndSet($name, $value); + $this->validateAndSet($name, $value); } /** @@ -116,16 +116,17 @@ abstract class ValidModel extends \Model public function save() { if ($this->isNew()) { //Fields populated by create() or hydrate() don't pass through set() - foreach( array_keys($this->_validations) as $field) { - $this->validateField($field, $this->$field); - } + foreach( array_keys($this->_validations) as $field) { + $this->validateField($field, $this->$field); } + } - $errs = $this->getValidationErrors(); - if (!empty($errs)) - $this->doValidationError(self::ON_SAVE); + $errs = $this->getValidationErrors(); + if (!empty($errs)) { + $this->doValidationError(self::ON_SAVE); + } - parent::save(); + parent::save(); } /** @@ -133,14 +134,14 @@ abstract class ValidModel extends \Model */ public function set($key, $value = null) { - if(is_array($key)) { + if(is_array($key)) { // multiple values - foreach($key as $field => $value) { - $this->validateAndSet($field, $value); - } - } else { - $this->validateAndSet($key, $value); + foreach($key as $field => $value) { + $this->validateAndSet($field, $value); } + } else { + $this->validateAndSet($key, $value); + } } @@ -148,20 +149,21 @@ abstract class ValidModel extends \Model // Protected methods protected function doValidationError($context) { - if ($context == $this->_validationOptions['throw']) - throw new \Sudzy\ValidationException($this->_validationErrors); + if ($context == $this->_validationOptions['throw']) { + throw new \Sudzy\ValidationException($this->_validationErrors); + } } protected function addValidationError($msg, $field = null) { - if ($this->_validationOptions['indexedErrors'] && $field !== null) { + if ($this->_validationOptions['indexedErrors'] && $field !== null) { // Only keep the first error found on a field - if (!isset($this->_validationErrors[$field])) { - $this->_validationErrors[$field] = $msg; - } - } else { - $this->_validationErrors[] = $msg; + if (!isset($this->_validationErrors[$field])) { + $this->_validationErrors[$field] = $msg; } + } else { + $this->_validationErrors[] = $msg; + } } /** @@ -169,12 +171,12 @@ abstract class ValidModel extends \Model */ protected function validateAndSet($name, $value) { - if (!$this->validateField($name, $value)) $this->doValidationError(self::ON_SET); - parent::set($name, $value); + if (!$this->validateField($name, $value)) $this->doValidationError(self::ON_SET); + parent::set($name, $value); } protected function setupValidationEngine() { - if (null == $this->_validator) $this->_validator = new \Sudzy\Engine(); // Is lazy setup worth it? + if (null == $this->_validator) $this->_validator = new \Sudzy\Engine(); // Is lazy setup worth it? } -} + }