diff --git a/assets/js/src/form/fields/selection.jsx b/assets/js/src/form/fields/selection.jsx index 3dae6bcac4..d6f8c71172 100644 --- a/assets/js/src/form/fields/selection.jsx +++ b/assets/js/src/form/fields/selection.jsx @@ -1,10 +1,12 @@ define([ 'react', + 'react-dom', 'jquery', 'select2' ], function( React, + ReactDOM, jQuery ) { var Selection = React.createClass({ @@ -21,12 +23,12 @@ function( this.setupSelect2(); }, setupSelect2: function() { - if(this.state.initialized === true) { + if(!this.props.field.multiple || this.state.initialized === true) { return; } - if(this.props.field.select2 && Object.keys(this.props.item).length > 0) { - var select2 = jQuery('#'+this.props.field.id).select2({ + if(Object.keys(this.props.item).length > 0) { + var select2 = jQuery('#'+this.refs.select.id).select2({ width: (this.props.width || ''), templateResult: function(item) { if (item.element && item.element.selected) { @@ -37,8 +39,7 @@ function( } }); - select2.on('change', this.handleChange) - + select2.on('change', this.handleChange); select2.select2( 'val', this.props.item[this.props.field.name] @@ -55,11 +56,16 @@ function( }); } }, - handleChange: function() { + handleChange: function(e) { if(this.props.onValueChange !== undefined) { + if(this.props.field.multiple) { + value = jQuery('#'+this.refs.select.id).select2('val'); + } else { + value = e.target.value; + } this.props.onValueChange({ target: { - value: jQuery('#'+this.props.field.id).select2('val'), + value: value, name: this.props.field.name } }); @@ -89,7 +95,8 @@ function( return ( ); }.bind(this))} diff --git a/assets/js/src/newsletters/send.jsx b/assets/js/src/newsletters/send.jsx index de807e3b5f..a4ddc6a587 100644 --- a/assets/js/src/newsletters/send.jsx +++ b/assets/js/src/newsletters/send.jsx @@ -34,8 +34,7 @@ define( placeholder: "Select a list", id: "mailpoet_segments", endpoint: "segments", - multiple: true, - select2: true + multiple: true }, { name: 'sender', diff --git a/assets/js/src/segments/list.jsx b/assets/js/src/segments/list.jsx index cf8861b5d0..0866e960ce 100644 --- a/assets/js/src/segments/list.jsx +++ b/assets/js/src/segments/list.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { Router, Route, Link } from 'react-router' +import { Router, Link } from 'react-router' import jQuery from 'jquery' import MailPoet from 'mailpoet' @@ -40,10 +40,10 @@ var columns = [ } ]; -var messages = { +const messages = { onTrash: function(response) { if(response) { - var message = null; + let message = null; if(~~response === 1) { message = ( '1 segment was moved to the trash.' @@ -61,7 +61,7 @@ var messages = { }, onDelete: function(response) { if(response) { - var message = null; + let message = null; if(~~response === 1) { message = ( '1 segment was permanently deleted.' @@ -79,7 +79,7 @@ var messages = { }, onRestore: function(response) { if(response) { - var message = null; + let message = null; if(~~response === 1) { message = ( '1 segment has been restored from the trash.' @@ -97,7 +97,7 @@ var messages = { } }; -var item_actions = [ +const item_actions = [ { name: 'edit', label: 'Edit', @@ -133,7 +133,7 @@ var item_actions = [ } ]; -var bulk_actions = [ +const bulk_actions = [ { name: 'trash', label: 'Trash', @@ -141,7 +141,7 @@ var bulk_actions = [ } ]; -var SegmentList = React.createClass({ +const SegmentList = React.createClass({ renderItem: function(segment, actions) { var rowClasses = classNames( 'manage-column', diff --git a/lib/Config/Initializer.php b/lib/Config/Initializer.php index d0ca7ef5bd..f4cc1cf78a 100644 --- a/lib/Config/Initializer.php +++ b/lib/Config/Initializer.php @@ -42,6 +42,7 @@ class Initializer { $forms = Env::$db_prefix . 'forms'; $subscriber_segment = Env::$db_prefix . 'subscriber_segment'; $newsletter_segment = Env::$db_prefix . 'newsletter_segment'; + $form_segment = Env::$db_prefix . 'form_segment'; $custom_fields = Env::$db_prefix . 'custom_fields'; $subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field'; $newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields'; @@ -55,6 +56,7 @@ class Initializer { define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment); define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates); define('MP_NEWSLETTER_SEGMENT_TABLE', $newsletter_segment); + define('MP_FORM_SEGMENT_TABLE', $form_segment); define('MP_CUSTOM_FIELDS_TABLE', $custom_fields); define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field); define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields); diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php index c2a00f000b..199f3a76b3 100644 --- a/lib/Config/Migrator.php +++ b/lib/Config/Migrator.php @@ -17,6 +17,7 @@ class Migrator { 'segments', 'subscriber_segment', 'newsletter_segment', + 'form_segment', 'custom_fields', 'subscriber_custom_field', 'newsletter_option_fields', @@ -142,6 +143,18 @@ class Migrator { return $this->sqlify(__FUNCTION__, $attributes); } + function form_segment() { + $attributes = array( + 'id mediumint(9) NOT NULL AUTO_INCREMENT,', + 'form_id mediumint(9) NOT NULL,', + 'segment_id mediumint(9) NOT NULL,', + 'created_at TIMESTAMP NOT NULL DEFAULT 0,', + 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', + 'PRIMARY KEY (id)' + ); + return $this->sqlify(__FUNCTION__, $attributes); + } + function custom_fields() { $attributes = array( 'id mediumint(9) NOT NULL AUTO_INCREMENT,', diff --git a/lib/Models/Form.php b/lib/Models/Form.php index fcb7f92d76..b107085a5f 100644 --- a/lib/Models/Form.php +++ b/lib/Models/Form.php @@ -14,6 +14,15 @@ class Form extends Model { )); } + function segments() { + return $this->has_many_through( + __NAMESPACE__.'\Segment', + __NAMESPACE__.'\FormSegment', + 'form_id', + 'segment_id' + ); + } + static function search($orm, $search = '') { return $orm->where_like('name', '%'.$search.'%'); } @@ -56,57 +65,28 @@ class Form extends Model { $form->set($data); } - $saved = $form->save(); - - if($saved === true) { - return true; - } else { - $errors = $form->getValidationErrors(); - if(!empty($errors)) { - return $errors; - } + try { + $form->save(); + return $form; + } catch(Exception $e) { + return $form->getValidationErrors(); } 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 - ); + function duplicate($data = array()) { + $duplicate = parent::duplicate($data); + + if($duplicate !== false) { + foreach($this->segments()->findResultSet() as $relation) { + $new_relation = FormSegment::create(); + $new_relation->set('segment_id', $relation->id); + $new_relation->set('form_id', $duplicate->id); + $new_relation->save(); } - return false; - } else { - // soft delete - $forms = $listing->getSelection() - ->findResultSet() - ->set_expr('deleted_at', 'NOW()') - ->save(); - return array( - 'segments' => $forms->count() - ); + return $duplicate; } - } - - static function restore($listing, $data = array()) { - $forms = $listing->getSelection() - ->findResultSet() - ->set_expr('deleted_at', 'NULL') - ->save(); - - return array( - 'segments' => $forms->count() - ); + return false; } } diff --git a/lib/Models/FormSegment.php b/lib/Models/FormSegment.php new file mode 100644 index 0000000000..53cc07f255 --- /dev/null +++ b/lib/Models/FormSegment.php @@ -0,0 +1,12 @@ +has_many_through( + __NAMESPACE__.'\Form', + __NAMESPACE__.'\FormSegment', + 'segment_id', + 'form_id' + ); + } + static function search($orm, $search = '') { return $orm->where_like('name', '%'.$search.'%'); } diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php index 7b024eb04c..7b95596e17 100644 --- a/lib/Models/Subscriber.php +++ b/lib/Models/Subscriber.php @@ -15,7 +15,16 @@ class Subscriber extends Model { )); } - function delete() { + function segments() { + return $this->has_many_through( + __NAMESPACE__.'\Segment', + __NAMESPACE__.'\SubscriberSegment', + 'subscriber_id', + 'segment_id' + ); + } + + function delete() { // delete all relations to segments SubscriberSegment::where('subscriber_id', $this->id)->deleteMany(); @@ -42,7 +51,9 @@ class Subscriber extends Model { ); foreach($segments as $segment) { - $subscribers_count = $segment->subscribers()->count(); + $subscribers_count = $segment->subscribers() + ->whereNull('deleted_at') + ->count(); if($subscribers_count > 0) { $segment_list[] = array( 'label' => sprintf('%s (%d)', $segment->name, $subscribers_count), @@ -143,15 +154,6 @@ class Subscriber extends Model { return $orm; } - function segments() { - return $this->has_many_through( - __NAMESPACE__.'\Segment', - __NAMESPACE__.'\SubscriberSegment', - 'subscriber_id', - 'segment_id' - ); - } - function customFields() { return $this->has_many_through( __NAMESPACE__.'\CustomField', diff --git a/lib/Router/Forms.php b/lib/Router/Forms.php index 607aabeffe..ebf66b7fbf 100644 --- a/lib/Router/Forms.php +++ b/lib/Router/Forms.php @@ -1,6 +1,7 @@ asArray()); + $segments = $form->segments(); + $form = $form->asArray(); + $form['segments'] = array_map(function($segment) { + return $segment['id']; + }, $segments->findArray()); + + wp_send_json($form); } } @@ -28,6 +35,17 @@ class Forms { $listing_data = $listing->get(); + // fetch segments relations for each returned item + foreach($listing_data['items'] as &$item) { + // form's segments + $relations = FormSegment::select('segment_id') + ->where('form_id', $item['id']) + ->findMany(); + $item['segments'] = array_map(function($relation) { + return $relation->segment_id; + }, $relations); + } + wp_send_json($listing_data); } @@ -37,53 +55,90 @@ class Forms { } function save($data = array()) { - $result = Form::createOrUpdate($data); + if(isset($data['segments'])) { + $segment_ids = $data['segments']; + unset($data['segments']); + } - if($result !== true) { - wp_send_json($result); + $form = Form::createOrUpdate($data); + + if($form->id() && !empty($segment_ids)) { + // remove previous relationships with segments + FormSegment::where('form_id', $form->id())->deleteMany(); + + // create relationship with segments + foreach($segment_ids as $segment_id) { + $relation = FormSegment::create(); + $relation->segment_id = $segment_id; + $relation->form_id = $form->id(); + $relation->save(); + } + } + + if($form === false) { + wp_send_json($form->getValidationErrors()); } else { wp_send_json(true); } } function restore($id) { + $result = false; + $form = Form::findOne($id); if($form !== false) { - $form->set_expr('deleted_at', 'NULL'); - $result = $form->save(); - } else { - $result = false; + $result = $form->restore(); } + wp_send_json($result); } - function delete($data = array()) { - $form = Form::findOne($data['id']); - $confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN); + function trash($id) { + $result = false; + + $form = Form::findOne($id); if($form !== false) { - if($confirm_delete) { - $form->delete(); - $result = true; - } else { - $form->set_expr('deleted_at', 'NOW()'); - $result = $form->save(); - } - } else { - $result = false; + $result = $form->trash(); } + + wp_send_json($result); + } + + function delete($id) { + $result = false; + + $form = Form::findOne($id); + if($form !== false) { + $form->delete(); + $result = 1; + } + wp_send_json($result); } function duplicate($id) { $result = false; - $form = Form::duplicate($id); + $form = Form::findOne($id); if($form !== false) { - $result = $form; + $data = array( + 'name' => sprintf(__('Copy of %s'), $form->name) + ); + $result = $form->duplicate($data)->asArray(); } + wp_send_json($result); } + function item_action($data = array()) { + $item_action = new Listing\ItemAction( + '\MailPoet\Models\Form', + $data + ); + + wp_send_json($item_action->apply()); + } + function bulk_action($data = array()) { $bulk_action = new Listing\BulkAction( '\MailPoet\Models\Form',