diff --git a/assets/js/src/forms/form.jsx b/assets/js/src/forms/form.jsx
new file mode 100644
index 0000000000..113b2f52fb
--- /dev/null
+++ b/assets/js/src/forms/form.jsx
@@ -0,0 +1,56 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { Router, History } from 'react-router'
+import MailPoet from 'mailpoet'
+import Form from 'form/form.jsx'
+
+const fields = [
+ {
+ name: 'name',
+ label: 'Name',
+ type: 'text'
+ },
+ {
+ name: 'segments',
+ label: 'Lists',
+ type: 'selection',
+ endpoint: 'segments'
+ }
+]
+
+const messages = {
+ updated: function() {
+ MailPoet.Notice.success('Form successfully updated!');
+ },
+ created: function() {
+ MailPoet.Notice.success('Form successfully added!');
+ }
+}
+
+const FormForm = React.createClass({
+ mixins: [
+ History
+ ],
+ render() {
+ return (
+
+ );
+ }
+});
+
+module.exports = FormForm
\ No newline at end of file
diff --git a/assets/js/src/forms/forms.jsx b/assets/js/src/forms/forms.jsx
new file mode 100644
index 0000000000..2388b491c6
--- /dev/null
+++ b/assets/js/src/forms/forms.jsx
@@ -0,0 +1,29 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { Router, Route, IndexRoute } from 'react-router'
+import FormList from 'forms/list.jsx'
+import FormForm from 'forms/form.jsx'
+import createHashHistory from 'history/lib/createHashHistory'
+
+let history = createHashHistory({ queryKey: false })
+
+const App = React.createClass({
+ render() {
+ return this.props.children
+ }
+});
+
+let container = document.getElementById('forms_container');
+
+if(container) {
+ ReactDOM.render((
+
+
+
+
+
+
+
+
+ ), container);
+}
\ No newline at end of file
diff --git a/assets/js/src/forms/list.jsx b/assets/js/src/forms/list.jsx
new file mode 100644
index 0000000000..a830f65f06
--- /dev/null
+++ b/assets/js/src/forms/list.jsx
@@ -0,0 +1,178 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { Router, Link, History } from 'react-router'
+import Listing from 'listing/listing.jsx'
+import classNames from 'classnames'
+import MailPoet from 'mailpoet'
+
+const columns = [
+ {
+ name: 'name',
+ label: 'Name',
+ sortable: true
+ },
+ {
+ name: 'created_at',
+ label: 'Created on',
+ sortable: true
+ }
+];
+
+const messages = {
+ onTrash: function(response) {
+ let count = ~~response.forms;
+ let message = null;
+
+ if(count === 1 || response === true) {
+ message = (
+ '1 form was moved to the trash.'
+ );
+ } else if(count > 1) {
+ message = (
+ '%$1d forms were moved to the trash.'
+ ).replace('%$1d', count);
+ }
+
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
+ },
+ onDelete: function(response) {
+ let count = ~~response.forms;
+ let message = null;
+
+ if(count === 1 || response === true) {
+ message = (
+ '1 form was permanently deleted.'
+ );
+ } else if(count > 1) {
+ message = (
+ '%$1d forms were permanently deleted.'
+ ).replace('%$1d', count);
+ }
+
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
+ },
+ onRestore: function(response) {
+ let count = ~~response.forms;
+ let message = null;
+
+ if(count === 1 || response === true) {
+ message = (
+ '1 form has been restored from the trash.'
+ );
+ } else if(count > 1) {
+ message = (
+ '%$1d forms have been restored from the trash.'
+ ).replace('%$1d', count);
+ }
+
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
+ }
+};
+
+const item_actions = [
+ {
+ name: 'edit',
+ link: function(item) {
+ return (
+ Edit
+ );
+ }
+ },
+ {
+ name: 'duplicate_form',
+ refresh: true,
+ link: function(item) {
+ return (
+ Duplicate
+ );
+ },
+ onDuplicate: function(item) {
+ MailPoet.Ajax.post({
+ endpoint: 'forms',
+ action: 'duplicate',
+ data: item.id
+ }).done(function() {
+ MailPoet.Notice.success(
+ ('List "%$1s" has been duplicated.').replace('%$1s', item.name)
+ );
+ });
+ }
+ }
+];
+
+const bulk_actions = [
+ {
+ name: 'trash',
+ label: 'Trash',
+ getData: function() {
+ return {
+ confirm: false
+ }
+ },
+ onSuccess: messages.onDelete
+ }
+];
+
+const FormList = React.createClass({
+ renderItem: function(form, actions) {
+ let row_classes = classNames(
+ 'manage-column',
+ 'column-primary',
+ 'has-row-actions'
+ );
+
+ let segments = mailpoet_segments.filter(function(segment) {
+ return (jQuery.inArray(segment.id, form.segments) !== -1);
+ }).map(function(segment) {
+ return segment.name;
+ }).join(', ');
+
+ return (
+
+
+
+ { form.name }
+
+ { actions }
+ |
+
+ { segments }
+ |
+
+ { form.created_at }
+ |
+
+ );
+ },
+ render() {
+ return (
+
+
+ Forms New
+
+
+
+
+ );
+ }
+});
+
+module.exports = FormList;
\ No newline at end of file
diff --git a/assets/js/src/listing/bulk_actions.jsx b/assets/js/src/listing/bulk_actions.jsx
index b7d44a2359..fcc154d966 100644
--- a/assets/js/src/listing/bulk_actions.jsx
+++ b/assets/js/src/listing/bulk_actions.jsx
@@ -45,12 +45,13 @@ function(
data.action = this.state.action;
+ var callback = function() {};
if(action['onSuccess'] !== undefined) {
- data.onSuccess = action.onSuccess;
+ callback = action.onSuccess;
}
if(data.action) {
- this.props.onBulkAction(selected_ids, data);
+ this.props.onBulkAction(selected_ids, data, callback);
}
this.setState({
diff --git a/assets/js/src/listing/filters.jsx b/assets/js/src/listing/filters.jsx
index 5617619e07..4bf174db19 100644
--- a/assets/js/src/listing/filters.jsx
+++ b/assets/js/src/listing/filters.jsx
@@ -36,6 +36,7 @@ function(
let default_value = false;
if(selected_filters[filter] !== undefined && selected_filters[filter]) {
default_value = selected_filters[filter]
+
} else {
jQuery(`select[name="${filter}"]`).val('');
}
diff --git a/assets/js/src/listing/listing.jsx b/assets/js/src/listing/listing.jsx
index e8ac166d1a..f5d192779d 100644
--- a/assets/js/src/listing/listing.jsx
+++ b/assets/js/src/listing/listing.jsx
@@ -46,8 +46,11 @@ define(
handleRestoreItem: function(id) {
this.props.onRestoreItem(id);
},
- handleDeleteItem: function(id, confirm = false) {
- this.props.onDeleteItem(id, confirm);
+ handleTrashItem: function(id) {
+ this.props.onTrashItem(id);
+ },
+ handleDeleteItem: function(id) {
+ this.props.onDeleteItem(id);
},
handleToggleItem: function(id) {
this.setState({ toggled: !this.state.toggled });
@@ -86,10 +89,26 @@ define(
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
);
+ } else if(action.link) {
+ return (
+
+ { action.link(this.props.item) }
+ {(index < (custom_actions.length - 1)) ? ' | ' : ''}
+
+ );
} else {
return (
-
- { action.link(this.props.item) }
+
+ { action.label }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
);
@@ -123,8 +142,7 @@ define(
href="javascript:;"
onClick={ this.handleDeleteItem.bind(
null,
- this.props.item.id,
- true
+ this.props.item.id
)}
>Delete permanently
@@ -145,10 +163,9 @@ define(
Trash
@@ -233,7 +250,7 @@ define(
- {this.props.items.map(function(item) {
+ {this.props.items.map(function(item, index) {
item.id = parseInt(item.id, 10);
item.selected = (this.props.selected_ids.indexOf(item.id) !== -1);
@@ -244,12 +261,13 @@ define(
onRenderItem={ this.props.onRenderItem }
onDeleteItem={ this.props.onDeleteItem }
onRestoreItem={ this.props.onRestoreItem }
+ onTrashItem={ this.props.onTrashItem }
onRefreshItems={ this.props.onRefreshItems }
selection={ this.props.selection }
is_selectable={ this.props.is_selectable }
item_actions={ this.props.item_actions }
group={ this.props.group }
- key={ 'item-' + item.id }
+ key={ 'item-' + index }
item={ item } />
);
}.bind(this))}
@@ -426,7 +444,27 @@ define(
this.getItems();
}.bind(this));
},
- handleDeleteItem: function(id, confirm = false) {
+ handleTrashItem: function(id) {
+ this.setState({
+ loading: true,
+ page: 1
+ });
+
+ MailPoet.Ajax.post({
+ endpoint: this.props.endpoint,
+ action: 'trash',
+ data: id
+ }).done(function(response) {
+ if(
+ this.props.messages !== undefined
+ && this.props.messages['onTrash'] !== undefined
+ ) {
+ this.props.messages.onTrash(response);
+ }
+ this.getItems();
+ }.bind(this));
+ },
+ handleDeleteItem: function(id) {
this.setState({
loading: true,
page: 1
@@ -435,31 +473,18 @@ define(
MailPoet.Ajax.post({
endpoint: this.props.endpoint,
action: 'delete',
- data: {
- id: id,
- confirm: confirm
- }
+ data: id
}).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);
- }
+ if(
+ this.props.messages !== undefined
+ && this.props.messages['onDelete'] !== undefined
+ ) {
+ this.props.messages.onDelete(response);
}
-
this.getItems();
}.bind(this));
},
- handleBulkAction: function(selected_ids, params) {
+ handleBulkAction: function(selected_ids, params, callback) {
if(
this.state.selection === false
&& this.state.selected_ids.length === 0
@@ -470,12 +495,6 @@ define(
this.setState({ loading: true });
var data = params || {};
- var callback = ((data['onSuccess'] !== undefined)
- ? data['onSuccess']
- : function() {}
- );
- delete data.onSuccess;
-
data.listing = {
offset: 0,
limit: 0,
@@ -621,12 +640,9 @@ define(
onSuccess: this.props.messages.onRestore
},
{
- name: 'trash',
+ name: 'delete',
label: 'Delete permanently',
- onSuccess: this.props.messages.onConfirmDelete,
- getData: function() {
- return { confirm: true };
- }
+ onSuccess: this.props.messages.onDelete
}
];
}
@@ -702,6 +718,7 @@ define(
onRenderItem={ this.handleRenderItem }
onDeleteItem={ this.handleDeleteItem }
onRestoreItem={ this.handleRestoreItem }
+ onTrashItem={ this.handleTrashItem }
onRefreshItems={ this.handleRefreshItems }
columns={ this.props.columns }
is_selectable={ bulk_actions.length > 0 }
diff --git a/assets/js/src/newsletters/list.jsx b/assets/js/src/newsletters/list.jsx
index ec1e670e54..bc17b093b1 100644
--- a/assets/js/src/newsletters/list.jsx
+++ b/assets/js/src/newsletters/list.jsx
@@ -38,7 +38,7 @@ define(
];
var messages = {
- onDelete: function(response) {
+ onTrash: function(response) {
var count = ~~response.newsletters;
var message = null;
@@ -56,7 +56,7 @@ define(
MailPoet.Notice.success(message);
}
},
- onConfirmDelete: function(response) {
+ onDelete: function(response) {
var count = ~~response.newsletters;
var message = null;
@@ -98,12 +98,7 @@ define(
{
name: 'trash',
label: 'Trash',
- getData: function() {
- return {
- confirm: false
- }
- },
- onSuccess: messages.onDelete
+ onSuccess: messages.onTrash
}
];
diff --git a/assets/js/src/segments/list.jsx b/assets/js/src/segments/list.jsx
index d6c7d4b809..cf8861b5d0 100644
--- a/assets/js/src/segments/list.jsx
+++ b/assets/js/src/segments/list.jsx
@@ -41,58 +41,58 @@ var columns = [
];
var messages = {
- onDelete: function(response) {
- var count = ~~response.segments;
- var message = null;
+ onTrash: function(response) {
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 segment was moved to the trash.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d segments were moved to the trash.'
+ ).replace('%$1d', ~~response);
+ }
- if(count === 1 || response === true) {
- message = (
- '1 segment was moved to the trash.'
- );
- } else if(count > 1) {
- message = (
- '%$1d segments were moved to the trash.'
- ).replace('%$1d', count);
- }
-
- if(message !== null) {
- MailPoet.Notice.success(message);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
},
- onConfirmDelete: function(response) {
- var count = ~~response.segments;
- var message = null;
+ onDelete: function(response) {
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 segment was permanently deleted.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d segments were permanently deleted.'
+ ).replace('%$1d', ~~response);
+ }
- if(count === 1 || response === true) {
- message = (
- '1 segment was permanently deleted.'
- );
- } else if(count > 1) {
- message = (
- '%$1d segments were permanently deleted.'
- ).replace('%$1d', count);
- }
-
- if(message !== null) {
- MailPoet.Notice.success(message);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
},
onRestore: function(response) {
- var count = ~~response.segments;
- var message = null;
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 segment has been restored from the trash.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d segments have been restored from the trash.'
+ ).replace('%$1d', ~~response);
+ }
- if(count === 1 || response === true) {
- message = (
- '1 segment has been restored from the trash.'
- );
- } else if(count > 1) {
- message = (
- '%$1d segments have been restored from the trash.'
- ).replace('%$1d', count);
- }
-
- if(message !== null) {
- MailPoet.Notice.success(message);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
}
};
@@ -100,6 +100,7 @@ var messages = {
var item_actions = [
{
name: 'edit',
+ label: 'Edit',
link: function(item) {
return (
Edit
@@ -108,24 +109,17 @@ var item_actions = [
},
{
name: 'duplicate_segment',
- refresh: true,
- link: function(item) {
- return (
- Duplicate
- );
- },
- onDuplicate: function(item) {
- MailPoet.Ajax.post({
+ label: 'Duplicate',
+ onClick: function(item, refresh) {
+ return MailPoet.Ajax.post({
endpoint: 'segments',
action: 'duplicate',
data: item.id
- }).done(function() {
+ }).done(function(response) {
MailPoet.Notice.success(
- ('List "%$1s" has been duplicated.').replace('%$1s', item.name)
+ ('List "%$1s" has been duplicated.').replace('%$1s', response.name)
);
+ refresh();
});
}
},
@@ -143,12 +137,7 @@ var bulk_actions = [
{
name: 'trash',
label: 'Trash',
- getData: function() {
- return {
- confirm: false
- }
- },
- onSuccess: messages.onDelete
+ onSuccess: messages.onTrash
}
];
diff --git a/assets/js/src/subscribers/list.jsx b/assets/js/src/subscribers/list.jsx
index 894d454887..ba34980697 100644
--- a/assets/js/src/subscribers/list.jsx
+++ b/assets/js/src/subscribers/list.jsx
@@ -38,58 +38,58 @@ const columns = [
];
const messages = {
- onDelete: function(response) {
- let count = ~~response.subscribers;
- let message = null;
+ onTrash: function(response) {
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 subscriber was moved to the trash.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d subscribers were moved to the trash.'
+ ).replace('%$1d', ~~response);
+ }
- 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);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
},
- onConfirmDelete: function(response) {
- let count = ~~response.subscribers;
- let message = null;
+ onDelete: function(response) {
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 subscriber was permanently deleted.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d subscribers were permanently deleted.'
+ ).replace('%$1d', ~~response);
+ }
- 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);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
},
onRestore: function(response) {
- let count = ~~response.subscribers;
- let message = null;
+ if(response) {
+ var message = null;
+ if(~~response === 1) {
+ message = (
+ '1 subscriber has been restored from the trash.'
+ );
+ } else if(~~response > 1) {
+ message = (
+ '%$1d subscribers have been restored from the trash.'
+ ).replace('%$1d', ~~response);
+ }
- 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);
+ if(message !== null) {
+ MailPoet.Notice.success(message);
+ }
}
}
};
@@ -179,8 +179,7 @@ const bulk_actions = [
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers were removed from all lists.'
- .replace('%$1d', ~~response.subscribers)
- .replace('%$2s', response.segment)
+ .replace('%$1d', ~~response)
);
}
},
@@ -190,19 +189,14 @@ const bulk_actions = [
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d subscribers have been confirmed.'
- .replace('%$1d', ~~response.subscribers)
+ .replace('%$1d', ~~response)
);
}
},
{
name: 'trash',
label: 'Trash',
- getData: function() {
- return {
- confirm: false
- }
- },
- onSuccess: messages.onDelete
+ onSuccess: messages.onTrash
}
];
diff --git a/lib/Config/Initializer.php b/lib/Config/Initializer.php
index e64019e7f8..d0ca7ef5bd 100644
--- a/lib/Config/Initializer.php
+++ b/lib/Config/Initializer.php
@@ -39,6 +39,7 @@ class Initializer {
$newsletters = Env::$db_prefix . 'newsletters';
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
$segments = Env::$db_prefix . 'segments';
+ $forms = Env::$db_prefix . 'forms';
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
$custom_fields = Env::$db_prefix . 'custom_fields';
@@ -50,6 +51,7 @@ class Initializer {
define('MP_SETTINGS_TABLE', $settings);
define('MP_NEWSLETTERS_TABLE', $newsletters);
define('MP_SEGMENTS_TABLE', $segments);
+ define('MP_FORMS_TABLE', $forms);
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
define('MP_NEWSLETTER_SEGMENT_TABLE', $newsletter_segment);
diff --git a/lib/Config/Menu.php b/lib/Config/Menu.php
index 493f4f6f02..3728188fc4 100644
--- a/lib/Config/Menu.php
+++ b/lib/Config/Menu.php
@@ -41,6 +41,14 @@ class Menu {
'mailpoet-newsletters',
array($this, 'newsletters')
);
+ add_submenu_page(
+ 'mailpoet',
+ __('Forms'),
+ __('Forms'),
+ 'manage_options',
+ 'mailpoet-forms',
+ array($this, 'forms')
+ );
add_submenu_page(
'mailpoet',
__('Subscribers'),
@@ -163,6 +171,13 @@ class Menu {
echo $this->renderer->render('segments.html', $data);
}
+ function forms() {
+ $data = array();
+ $data['segments'] = Segment::findArray();
+
+ echo $this->renderer->render('forms.html', $data);
+ }
+
function newsletters() {
global $wp_roles;
diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php
index 7ed64e4cff..c2a00f000b 100644
--- a/lib/Config/Migrator.php
+++ b/lib/Config/Migrator.php
@@ -21,6 +21,7 @@ class Migrator {
'subscriber_custom_field',
'newsletter_option_fields',
'newsletter_option',
+ 'forms'
);
}
@@ -193,6 +194,19 @@ class Migrator {
return $this->sqlify(__FUNCTION__, $attributes);
}
+ function forms() {
+ $attributes = array(
+ 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
+ 'name varchar(90) NOT NULL,',
+ 'body longtext,',
+ '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,',
+ 'PRIMARY KEY (id)'
+ );
+ return $this->sqlify(__FUNCTION__, $attributes);
+ }
+
private function sqlify($model, $attributes) {
$table = $this->prefix . $model;
diff --git a/lib/Listing/BulkAction.php b/lib/Listing/BulkAction.php
index 6dc435fbed..ebf87bb23a 100644
--- a/lib/Listing/BulkAction.php
+++ b/lib/Listing/BulkAction.php
@@ -5,15 +5,17 @@ if(!defined('ABSPATH')) exit;
class BulkAction {
private $listing = null;
+ private $action = null;
private $data = null;
private $model_class = null;
function __construct($model_class, $data) {
- $this->model_class = $model_class;
+ $this->action = $data['action'];
+ unset($data['action']);
$this->data = $data;
-
+ $this->model_class = $model_class;
$this->listing = new Handler(
- $this->model_class,
+ $model_class,
$this->data['listing']
);
return $this;
@@ -21,8 +23,9 @@ class BulkAction {
function apply() {
return call_user_func_array(
- array($this->model_class, $this->data['action']),
- array($this->listing, $this->data)
+ array($this->model_class, 'bulk'.ucfirst($this->action)),
+ array($this->listing->getSelection(), $this->data)
);
+ return $models->count();
}
}
\ No newline at end of file
diff --git a/lib/Listing/Handler.php b/lib/Listing/Handler.php
index 284753eabf..7f2fa588e3 100644
--- a/lib/Listing/Handler.php
+++ b/lib/Listing/Handler.php
@@ -4,16 +4,13 @@ namespace MailPoet\Listing;
if(!defined('ABSPATH')) exit;
class Handler {
-
private $data = array();
private $model = null;
function __construct($model_class, $data = array()) {
$class = new \ReflectionClass($model_class);
$this->table_name = $class->getStaticPropertyValue('_table');
-
- $this->model = \Model::factory($model_class);
-
+ $this->model = $model_class::select('*');
$this->data = array(
// pagination
'offset' => (isset($data['offset']) ? (int)$data['offset'] : 0),
@@ -31,7 +28,7 @@ class Handler {
'selection' => (isset($data['selection']) ? $data['selection'] : null)
);
- $this->model = $this->setFilter();
+ $this->setFilter();
$this->setSearch();
$this->setGroup();
$this->setOrder();
@@ -59,9 +56,9 @@ class Handler {
private function setFilter() {
if($this->data['filter'] === null) {
- return $this->model;
+ return;
}
- return $this->model->filter('filterBy', $this->data['filter']);
+ $this->model = $this->model->filter('filterBy', $this->data['filter']);
}
function getSelection() {
diff --git a/lib/Listing/ItemAction.php b/lib/Listing/ItemAction.php
new file mode 100644
index 0000000000..537a503001
--- /dev/null
+++ b/lib/Listing/ItemAction.php
@@ -0,0 +1,36 @@
+action = $data['action'];
+ unset($data['action']);
+ $this->model = $model_class::findOne($id);
+ if(!empty($data)) {
+ $this->data = $data;
+ }
+ return $this;
+ }
+
+ function apply() {
+ if($this->data === null) {
+ return call_user_func_array(
+ array($this->model, $this->action),
+ array()
+ );
+ } else {
+ return call_user_func_array(
+ array($this->model, $this->action),
+ array($this->data)
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/Models/Form.php b/lib/Models/Form.php
new file mode 100644
index 0000000000..fcb7f92d76
--- /dev/null
+++ b/lib/Models/Form.php
@@ -0,0 +1,112 @@
+addValidations('name', array(
+ 'required' => __('You need to specify a name.')
+ ));
+ }
+
+ static function search($orm, $search = '') {
+ return $orm->where_like('name', '%'.$search.'%');
+ }
+
+ static function groups() {
+ return array(
+ array(
+ 'name' => 'all',
+ 'label' => __('All'),
+ 'count' => Form::whereNull('deleted_at')->count()
+ ),
+ array(
+ 'name' => 'trash',
+ 'label' => __('Trash'),
+ 'count' => Form::whereNotNull('deleted_at')->count()
+ )
+ );
+ }
+
+ static function groupBy($orm, $group = null) {
+ if($group === 'trash') {
+ return $orm->whereNotNull('deleted_at');
+ } else {
+ $orm = $orm->whereNull('deleted_at');
+ }
+ }
+
+ static function createOrUpdate($data = array()) {
+ $form = false;
+
+ if(isset($data['id']) && (int)$data['id'] > 0) {
+ $form = self::findOne((int)$data['id']);
+ }
+
+ if($form === false) {
+ $form = self::create();
+ $form->hydrate($data);
+ } else {
+ unset($data['id']);
+ $form->set($data);
+ }
+
+ $saved = $form->save();
+
+ if($saved === true) {
+ return true;
+ } else {
+ $errors = $form->getValidationErrors();
+ if(!empty($errors)) {
+ return $errors;
+ }
+ }
+ return false;
+ }
+
+ static function trash($listing, $data = array()) {
+ $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
+ if($confirm_delete) {
+ // delete relations with all segments
+ $forms = $listing->getSelection()->findResultSet();
+ if(!empty($forms)) {
+ $forms_count = 0;
+ foreach($forms as $form) {
+ if($form->delete()) {
+ $forms_count++;
+ }
+ }
+ return array(
+ 'segments' => $forms_count
+ );
+ }
+ return false;
+ } else {
+ // soft delete
+ $forms = $listing->getSelection()
+ ->findResultSet()
+ ->set_expr('deleted_at', 'NOW()')
+ ->save();
+
+ return array(
+ 'segments' => $forms->count()
+ );
+ }
+ }
+
+ static function restore($listing, $data = array()) {
+ $forms = $listing->getSelection()
+ ->findResultSet()
+ ->set_expr('deleted_at', 'NULL')
+ ->save();
+
+ return array(
+ 'segments' => $forms->count()
+ );
+ }
+}
diff --git a/lib/Models/Model.php b/lib/Models/Model.php
index 93716e9be8..98cd7e2910 100644
--- a/lib/Models/Model.php
+++ b/lib/Models/Model.php
@@ -9,6 +9,10 @@ class Model extends \Sudzy\ValidModel {
parent::__construct($customValidators->init());
}
+ static function create() {
+ return parent::create();
+ }
+
function save() {
$this->setTimestamp();
try {
@@ -21,6 +25,54 @@ class Model extends \Sudzy\ValidModel {
}
}
+ function trash() {
+ return $this->set_expr('deleted_at', 'NOW()')->save();
+ }
+
+ static function bulkTrash($orm) {
+ $models = $orm->findResultSet();
+ $models->set_expr('deleted_at', 'NOW()')->save();
+ return $models->count();
+ }
+
+ static function bulkDelete($orm) {
+ $models = $orm->findMany();
+ $count = 0;
+ foreach($models as $model) {
+ $model->delete();
+ $count++;
+ }
+ return $count;
+ }
+
+ function restore() {
+ return $this->set_expr('deleted_at', 'NULl')->save();
+ }
+
+ static function bulkRestore($orm) {
+ $models = $orm->findResultSet();
+ $models->set_expr('deleted_at', 'NULL')->save();
+ return $models->count();
+ }
+
+ function duplicate($data = array()) {
+ $model = get_called_class();
+ $model_data = array_merge($this->asArray(), $data);
+ unset($model_data['id']);
+
+ $duplicate = $model::create();
+ $duplicate->hydrate($model_data);
+ $duplicate->set_expr('created_at', 'NOW()');
+ $duplicate->set_expr('updated_at', 'NOW()');
+ $duplicate->set_expr('deleted_at', 'NULL');
+
+ if($duplicate->save()) {
+ return $duplicate;
+ } else {
+ return false;
+ }
+ }
+
private function setTimestamp() {
if($this->created_at === null) {
$this->set_expr('created_at', 'NOW()');
diff --git a/lib/Models/Newsletter.php b/lib/Models/Newsletter.php
index b004c90b6c..627750ed63 100644
--- a/lib/Models/Newsletter.php
+++ b/lib/Models/Newsletter.php
@@ -143,46 +143,4 @@ class Newsletter extends Model {
}
return false;
}
-
- static function trash($listing, $data = array()) {
- $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
- if($confirm_delete) {
- // delete relations with all segments
- $newsletters = $listing->getSelection()->findResultSet();
-
- if(!empty($newsletters)) {
- $newsletters_count = 0;
- foreach($newsletters as $newsletter) {
- if($newsletter->delete()) {
- $newsletters_count++;
- }
- }
- return array(
- 'newsletters' => $newsletters_count
- );
- }
- return false;
- } else {
- // soft delete
- $newsletters = $listing->getSelection()
- ->findResultSet()
- ->set_expr('deleted_at', 'NOW()')
- ->save();
-
- return array(
- 'newsletters' => $newsletters->count()
- );
- }
- }
-
- static function restore($listing, $data = array()) {
- $newsletters = $listing->getSelection()
- ->findResultSet()
- ->set_expr('deleted_at', 'NULL')
- ->save();
-
- return array(
- 'newsletters' => $newsletters->count()
- );
- }
}
diff --git a/lib/Models/Segment.php b/lib/Models/Segment.php
index d3b562930b..7936e9427e 100644
--- a/lib/Models/Segment.php
+++ b/lib/Models/Segment.php
@@ -17,48 +17,7 @@ class Segment extends Model {
function delete() {
// delete all relations to subscribers
SubscriberSegment::where('segment_id', $this->id)->deleteMany();
-
- return parent::delete();
- }
-
- static function duplicate($id) {
- $segment = self::findOne($id)->asArray();
-
- if($segment !== false) {
- unset($segment['id']);
- $new_segment = self::create();
- $new_segment->hydrate($segment);
-
- $new_segment->set(
- 'name',
- sprintf(__('Copy of %s'), $new_segment->name)
- );
- $new_segment->set_expr('created_at', 'NOW()');
- $new_segment->set_expr('updated_at', 'NOW()');
- $new_segment->save();
-
- $relations = SubscriberSegment::select('subscriber_id')
- ->where('segment_id', $id)
- ->findResultSet();
-
- foreach($relations as $relation) {
- $new_relation = SubscriberSegment::create();
- $new_relation->set('subscriber_id', $relation->subscriber_id);
- $new_relation->set('segment_id', $new_segment->id());
- $new_relation->save();
- }
- return true;
- }
- return false;
- }
-
- function subscribers() {
- return $this->has_many_through(
- __NAMESPACE__.'\Subscriber',
- __NAMESPACE__.'\SubscriberSegment',
- 'segment_id',
- 'subscriber_id'
- );
+ parent::delete();
}
function newsletters() {
@@ -125,45 +84,28 @@ class Segment extends Model {
return false;
}
- static function trash($listing, $data = array()) {
- $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
- if($confirm_delete) {
- // delete relations with all segments
- $segments = $listing->getSelection()->findResultSet();
+ function duplicate($data = array()) {
+ $duplicate = parent::duplicate($data);
- if(!empty($segments)) {
- $segments_count = 0;
- foreach($segments as $segment) {
- if($segment->delete()) {
- $segments_count++;
- }
- }
- return array(
- 'segments' => $segments_count
- );
+ if($duplicate !== false) {
+ foreach($this->subscribers()->findResultSet() as $relation) {
+ $new_relation = SubscriberSegment::create();
+ $new_relation->set('subscriber_id', $relation->id);
+ $new_relation->set('segment_id', $duplicate->id);
+ $new_relation->save();
}
- return false;
- } else {
- // soft delete
- $segments = $listing->getSelection()
- ->findResultSet()
- ->set_expr('deleted_at', 'NOW()')
- ->save();
- return array(
- 'segments' => $segments->count()
- );
+ return $duplicate;
}
+ return false;
}
- static function restore($listing, $data = array()) {
- $segments = $listing->getSelection()
- ->findResultSet()
- ->set_expr('deleted_at', 'NULL')
- ->save();
-
- return array(
- 'segments' => $segments->count()
+ function subscribers() {
+ return $this->has_many_through(
+ __NAMESPACE__.'\Subscriber',
+ __NAMESPACE__.'\SubscriberSegment',
+ 'segment_id',
+ 'subscriber_id'
);
}
}
diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php
index e68227b282..7b024eb04c 100644
--- a/lib/Models/Subscriber.php
+++ b/lib/Models/Subscriber.php
@@ -66,7 +66,7 @@ class Subscriber extends Model {
if($key === 'segment') {
$segment = Segment::findOne($value);
if($segment !== false) {
- $orm = $segment->subscribers();
+ return $segment->subscribers();
}
}
}
@@ -189,12 +189,11 @@ class Subscriber extends Model {
return false;
}
- static function moveToList($listing, $data = array()) {
+ static function bulkMoveToList($orm, $data = array()) {
$segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0);
$segment = Segment::findOne($segment_id);
if($segment !== false) {
- $subscribers_count = 0;
- $subscribers = $listing->getSelection()->findResultSet();
+ $subscribers = $orm->findResultSet();
foreach($subscribers as $subscriber) {
// remove subscriber from all segments
SubscriberSegment::where('subscriber_id', $subscriber->id)->deleteMany();
@@ -204,37 +203,37 @@ class Subscriber extends Model {
$association->subscriber_id = $subscriber->id;
$association->segment_id = $segment->id;
$association->save();
-
- $subscribers_count++;
}
return array(
- 'subscribers' => $subscribers_count,
+ 'subscribers' => $subscribers->count(),
'segment' => $segment->name
);
}
return false;
}
- static function removeFromList($listing, $data = array()) {
+ static function bulkRemoveFromList($orm, $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();
+ $subscribers = $orm->findResultSet();
+ foreach($subscribers as $subscriber) {
+ SubscriberSegment::where('subscriber_id', $subscriber->id)
+ ->where('segment_id', $segment->id)
+ ->deleteMany();
+ }
return array(
- 'subscribers' => count($subscriber_ids),
+ 'subscribers' => $subscribers->count(),
'segment' => $segment->name
);
}
return false;
}
- static function removeFromAllLists($listing) {
+ static function bulkRemoveFromAllLists($orm) {
$segments = Segment::findMany();
$segment_ids = array_map(function($segment) {
return $segment->id();
@@ -242,62 +241,48 @@ class Subscriber extends Model {
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;
+ $subscribers = $orm->findResultSet();
foreach($subscribers as $subscriber) {
- $subscriber->set('status', 'subscribed');
- if($subscriber->save() === true) {
- $subscribers_count++;
- }
+ SubscriberSegment::where('subscriber_id', $subscriber->id)
+ ->whereIn('segment_id', $segment_ids)
+ ->deleteMany();
}
- return array(
- 'subscribers' => $subscribers_count
- );
+ return $subscribers->count();
}
return false;
}
- static function resendConfirmationEmail($listing) {
- $subscriber_ids = $listing->getSelectionIds();
- $subscribers = Subscriber::whereIn('id', $subscriber_ids)
+ static function bulkConfirmUnconfirmed($orm) {
+ $subscribers = $orm->findResultSet();
+ $subscribers->set('status', 'subscribed')->save();
+ return $subscribers->count();
+ }
+
+ static function bulkResendConfirmationEmail($orm) {
+ $subscribers = $orm
->where('status', 'unconfirmed')
- ->findMany();
+ ->findResultSet();
if(!empty($subscribers)) {
foreach($subscribers as $subscriber) {
- // TODO: resend confirmation email
+ // TODO: send confirmation email
+ // $subscriber->sendConfirmationEmail()
}
- return true;
+
+ return $subscribers->count();
}
return false;
}
- static function addToList($listing, $data = array()) {
+ static function bulkAddToList($orm, $data = array()) {
+
$segment_id = (isset($data['segment_id']) ? (int)$data['segment_id'] : 0);
$segment = Segment::findOne($segment_id);
if($segment !== false) {
$subscribers_count = 0;
- $subscribers = $listing->getSelection()->findMany();
+ $subscribers = $orm->findMany();
foreach($subscribers as $subscriber) {
// create relation with segment
$association = \MailPoet\Models\SubscriberSegment::create();
@@ -314,46 +299,4 @@ class Subscriber extends Model {
}
return false;
}
-
- static function trash($listing, $data = array()) {
- $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
- if($confirm_delete) {
- // delete relations with all segments
- $subscribers = $listing->getSelection()->findResultSet();
-
- 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()
- );
- }
}
\ No newline at end of file
diff --git a/lib/Router/Forms.php b/lib/Router/Forms.php
new file mode 100644
index 0000000000..607aabeffe
--- /dev/null
+++ b/lib/Router/Forms.php
@@ -0,0 +1,95 @@
+asArray());
+ }
+ }
+
+ function listing($data = array()) {
+ $listing = new Listing\Handler(
+ '\MailPoet\Models\Form',
+ $data
+ );
+
+ $listing_data = $listing->get();
+
+ wp_send_json($listing_data);
+ }
+
+ function getAll() {
+ $collection = Form::findArray();
+ wp_send_json($collection);
+ }
+
+ function save($data = array()) {
+ $result = Form::createOrUpdate($data);
+
+ if($result !== true) {
+ wp_send_json($result);
+ } else {
+ wp_send_json(true);
+ }
+ }
+
+ function restore($id) {
+ $form = Form::findOne($id);
+ if($form !== false) {
+ $form->set_expr('deleted_at', 'NULL');
+ $result = $form->save();
+ } else {
+ $result = false;
+ }
+ wp_send_json($result);
+ }
+
+ function delete($data = array()) {
+ $form = Form::findOne($data['id']);
+ $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
+ if($form !== false) {
+ if($confirm_delete) {
+ $form->delete();
+ $result = true;
+ } else {
+ $form->set_expr('deleted_at', 'NOW()');
+ $result = $form->save();
+ }
+ } else {
+ $result = false;
+ }
+ wp_send_json($result);
+ }
+
+ function duplicate($id) {
+ $result = false;
+
+ $form = Form::duplicate($id);
+ if($form !== false) {
+ $result = $form;
+ }
+ wp_send_json($result);
+ }
+
+ function bulk_action($data = array()) {
+ $bulk_action = new Listing\BulkAction(
+ '\MailPoet\Models\Form',
+ $data
+ );
+
+ wp_send_json($bulk_action->apply());
+ }
+}
diff --git a/lib/Router/Segments.php b/lib/Router/Segments.php
index 2fdbdce4a4..ca27ee0f90 100644
--- a/lib/Router/Segments.php
+++ b/lib/Router/Segments.php
@@ -66,7 +66,7 @@ class Segments {
}
function getAll() {
- $collection = Segment::find_array();
+ $collection = Segment::findArray();
wp_send_json($collection);
}
@@ -81,38 +81,62 @@ class Segments {
}
function restore($id) {
+ $result = false;
+
$segment = Segment::findOne($id);
if($segment !== false) {
- $segment->set_expr('deleted_at', 'NULL');
- $result = $segment->save();
- } else {
- $result = false;
+ $result = $segment->restore();
}
+
wp_send_json($result);
}
- function delete($data = array()) {
- $segment = Segment::findOne($data['id']);
- $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
+ function trash($id) {
+ $result = false;
+
+ $segment = Segment::findOne($id);
if($segment !== false) {
- if($confirm_delete) {
- $segment->delete();
- $result = true;
- } else {
- $segment->set_expr('deleted_at', 'NOW()');
- $result = $segment->save();
- }
- } else {
- $result = false;
+ $result = $segment->trash();
}
+
+ wp_send_json($result);
+ }
+
+ function delete($id) {
+ $result = false;
+
+ $segment = Segment::findOne($id);
+ if($segment !== false) {
+ $segment->delete();
+ $result = 1;
+ }
+
wp_send_json($result);
}
function duplicate($id) {
- $result = Segment::duplicate($id);
+ $result = false;
+
+ $segment = Segment::findOne($id);
+ if($segment !== false) {
+ $data = array(
+ 'name' => sprintf(__('Copy of %s'), $segment->name)
+ );
+ $result = $segment->duplicate($data)->asArray();
+ }
+
wp_send_json($result);
}
+ function item_action($data = array()) {
+ $item_action = new Listing\ItemAction(
+ '\MailPoet\Models\Segment',
+ $data
+ );
+
+ wp_send_json($item_action->apply());
+ }
+
function bulk_action($data = array()) {
$bulk_action = new Listing\BulkAction(
'\MailPoet\Models\Segment',
diff --git a/lib/Router/Subscribers.php b/lib/Router/Subscribers.php
index cfb8900012..1663f283e0 100644
--- a/lib/Router/Subscribers.php
+++ b/lib/Router/Subscribers.php
@@ -60,33 +60,48 @@ class Subscribers {
}
function restore($id) {
+ $result = false;
+
$subscriber = Subscriber::findOne($id);
if($subscriber !== false) {
- $subscriber->set_expr('deleted_at', 'NULL');
- $result = array('subscribers' => (int)$subscriber->save());
- } else {
- $result = false;
+ $result = $subscriber->restore();
}
+
wp_send_json($result);
}
- function delete($data = array()) {
- $subscriber = Subscriber::findOne($data['id']);
- $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
+ function trash($id) {
+ $result = false;
+
+ $subscriber = Subscriber::findOne($id);
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 {
- $result = false;
+ $result = $subscriber->trash();
}
+
wp_send_json($result);
}
+ function delete($id) {
+ $result = false;
+
+ $subscriber = Subscriber::findOne($id);
+ if($subscriber !== false) {
+ $subscriber->delete();
+ $result = 1;
+ }
+
+ wp_send_json($result);
+ }
+
+ function item_action($data = array()) {
+ $item_action = new Listing\ItemAction(
+ '\MailPoet\Models\Segment',
+ $data
+ );
+
+ wp_send_json($item_action->apply());
+ }
+
function bulk_action($data = array()) {
$bulk_action = new Listing\BulkAction(
'\MailPoet\Models\Subscriber',
diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php
index 4b21026b0a..3cd690db70 100644
--- a/tests/_bootstrap.php
+++ b/tests/_bootstrap.php
@@ -18,7 +18,11 @@ $models = array(
'SubscriberSegment'
);
$destroy = function ($model) {
- Model::factory('\MailPoet\Models\\' . $model)
- ->deleteMany();
+ $class = new \ReflectionClass('\MailPoet\Models\\' . $model);
+ $table = $class->getStaticPropertyValue('_table');
+ $db = ORM::getDb();
+ $db->beginTransaction();
+ $db->exec('TRUNCATE '.$table);
+ $db->commit();
};
array_map($destroy, $models);
diff --git a/views/forms.html b/views/forms.html
new file mode 100644
index 0000000000..c3c4820797
--- /dev/null
+++ b/views/forms.html
@@ -0,0 +1,16 @@
+<% extends 'layout.html' %>
+
+<% block content %>
+
+
+ <%= localize({
+ 'pageTitle': __('Forms'),
+ 'searchLabel': __('Search'),
+ 'loadingItems': __('Loading forms...'),
+ 'noItemsFound': __('No forms found.')
+ }) %>
+
+
+<% endblock %>
diff --git a/webpack.config.js b/webpack.config.js
index 24e6d593d1..f6838db52d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -69,6 +69,7 @@ config.push(_.extend({}, baseConfig, {
'subscribers/subscribers.jsx',
'newsletters/newsletters.jsx',
'segments/segments.jsx',
+ 'forms/forms.jsx',
'settings/tabs.js'
],
newsletter_editor: [