Form editor

- new/edit in forms listing goes to editor
- fixed editor dependencies (via Webpack)
- updated forms table schema
- saving/loading a form works
This commit is contained in:
Jonathan Labreuille
2015-11-02 19:01:01 +01:00
parent 6c8d2be18c
commit 541696863e
21 changed files with 175 additions and 93 deletions

View File

@ -1,3 +1,6 @@
@require 'codemirror/lib/codemirror.css'
@require 'codemirror/theme/neo.css'
icons = '../img/form_editor_icons.png' icons = '../img/form_editor_icons.png'
handle_icon = '../img/handle.png' handle_icon = '../img/handle.png'

View File

@ -86,7 +86,7 @@ const item_actions = [
label: 'Edit', label: 'Edit',
link: function(item) { link: function(item) {
return ( return (
<Link to={ `/edit/${item.id}` }>Edit</Link> <a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>Edit</a>
); );
} }
}, },
@ -117,7 +117,19 @@ const bulk_actions = [
]; ];
const FormList = React.createClass({ const FormList = React.createClass({
renderItem: function(form, actions) { createForm() {
MailPoet.Ajax.post({
endpoint: 'forms',
action: 'save',
data: {
name: "New form"
}
}).done(function(response) {
window.location =
'admin.php?page=mailpoet-form-editor&id='+ parseInt(response, 10);
})
},
renderItem(form, actions) {
let row_classes = classNames( let row_classes = classNames(
'manage-column', 'manage-column',
'column-primary', 'column-primary',
@ -151,7 +163,11 @@ const FormList = React.createClass({
return ( return (
<div> <div>
<h2 className="title"> <h2 className="title">
Forms <Link className="add-new-h2" to="/new">New</Link> Forms <a
className="add-new-h2"
href="javascript:;"
onClick={ this.createForm }
>New</a>
</h2> </h2>
<Listing <Listing

View File

@ -3,6 +3,7 @@ namespace MailPoet\Config;
use \MailPoet\Models\Segment; use \MailPoet\Models\Segment;
use \MailPoet\Models\Setting; use \MailPoet\Models\Setting;
use \MailPoet\Models\Form; use \MailPoet\Models\Form;
use \MailPoet\Form\Renderer as FormRenderer;
use \MailPoet\Settings\Hosts; use \MailPoet\Settings\Hosts;
use \MailPoet\Settings\Pages; use \MailPoet\Settings\Pages;
use \MailPoet\Settings\Charsets; use \MailPoet\Settings\Charsets;
@ -203,20 +204,26 @@ class Menu {
} }
function formEditor() { function formEditor() {
$data = array();
$form = array( $form = array(
'name' => __('My new form') 'name' => __('My new form')
); );
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
$id = (isset($_POST['id']) ? (int)$_POST['id'] : 0);
$form = Form::findOne($id); $form = Form::findOne($id);
if($form !== false) { if($form !== false) {
$segments = $form->segments();
$form = $form->asArray(); $form = $form->asArray();
$form['segments'] = array_map(function($segment) {
return $segment['id'];
}, $segments->findArray());
} }
$data['form'] = $form;
$data['segments'] = Segment::findArray(); $data = array(
'form' => $form,
'pages' => Pages::getAll(),
'segments' => Segment::findArray(),
'styles' => FormRenderer::getStyles($form)
);
echo $this->renderer->render('form/editor.html', $data); echo $this->renderer->render('form/editor.html', $data);
} }
} }

View File

@ -211,7 +211,7 @@ class Migrator {
$attributes = array( $attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,', 'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,', 'name varchar(90) NOT NULL,',
'body longtext,', 'data longtext,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',

View File

@ -57,6 +57,10 @@ class Form extends Model {
$form = self::findOne((int)$data['id']); $form = self::findOne((int)$data['id']);
} }
if(!empty($data['data'])) {
$data['data'] = json_encode($data['data']);
}
if($form === false) { if($form === false) {
$form = self::create(); $form = self::create();
$form->hydrate($data); $form->hydrate($data);

View File

@ -75,13 +75,72 @@ class Forms {
} }
} }
if($form === false) { if($form !== false && $form->id()) {
wp_send_json($form->getValidationErrors()); wp_send_json($form->id());
} else { } else {
wp_send_json(true); wp_send_json($form);
} }
} }
function save_editor($data = array()) {
$form_id = (isset($data['id']) ? (int)$data['id'] : 0);
$form_data = (isset($data['form']) ? $data['form'] : array());
if(empty($form_data)) {
// error
wp_send_json(false);
} else {
// check if the form is displayed as a widget (we'll display a different "saved!" message in this case)
$is_widget = false;
$widgets = get_option('widget_mailpoet_form');
if(!empty($widgets)) {
foreach($widgets as $widget) {
if(isset($widget['form']) && (int)$widget['form'] === $form_id) {
$is_widget = true;
break;
}
}
}
// check if the user gets to pick his own lists or if it's selected by the admin
$has_list_selection = false;
$blocks = (isset($form_data['body']) ? $form_data['body'] : array());
if(!empty($blocks)) {
foreach ($blocks as $i => $block) {
if($block['type'] === 'list') {
$has_list_selection = true;
if(!empty($block['params']['values'])) {
$list_selection = array_map(function($segment) {
return (int)$segment['id'];
}, $block['params']['values']);
}
break;
}
}
}
// check list selectio
if($has_list_selection === true) {
$form_data['lists_selected_by'] = 'user';
} else {
$form_data['lists_selected_by'] = 'admin';
}
}
$form = Form::createOrUpdate(array(
'id' => $form_id,
'data' => $form_data
));
// response
wp_send_json(array(
'result' => ($form !== false),
'is_widget' => $is_widget
));
}
function restore($id) { function restore($id) {
$result = false; $result = false;

View File

@ -34,28 +34,31 @@
<div> <div>
<!-- Form settings --> <!-- Form settings -->
<form id="mailpoet_form_settings" action="" method="POST"> <form id="mailpoet_form_settings" action="" method="POST">
<input
type="hidden"
id="mailpoet_form_id"
value="<%= form.id | default(0) %>"
/>
<div id="mailpoet_settings_list_selection"> <div id="mailpoet_settings_list_selection">
<!-- Form settings: list selection --> <!-- Form settings: list selection -->
<p> <p>
<strong><%= __('This form adds subscribers to these lists:') %></strong> <strong><%= __('This form adds subscribers to these segments:') %></strong>
</p> </p>
<select name="lists" data-placeholder="<%= __('Choose a list') %>" multiple> <select
id="mailpoet_form_segments"
name="segments"
data-placeholder="<%= __('Choose a list') %>"
multiple
>
<% for segment in segments %> <% for segment in segments %>
<option value="<%= segment.id %>" <option value="<%= segment.id %>"><%= segment.name %></option>
<% if segment.id in form.segments %>
selected="selected"
<% endif %>
><%= segment.name %></option>
<% endfor %> <% endfor %>
</select> </select>
<!-- error if user tries to save and has not selected a list -->
<p class="mailpoet_error" data-error="admin_no_list">
<%= __('You have to select at least 1 list') %>
</p>
</div> </div>
<div id="mailpoet_on_success"> <div id="mailpoet_on_success">
<!-- Form settings: after submit --> <!-- Form settings: after submit -->
<input type="hidden" name="on_success" value="message" />
<p> <p>
<label><strong><%= __('After submit...') %></strong></label> <label><strong><%= __('After submit...') %></strong></label>
<label> <label>
@ -63,6 +66,7 @@
type="radio" type="radio"
name="on_success" name="on_success"
value="message" value="message"
<% if(on_success == 'message') %>checked="checked"<% endif %>
/><%= __('Show message') %> /><%= __('Show message') %>
</label> </label>
&nbsp; &nbsp;
@ -71,6 +75,7 @@
type="radio" type="radio"
name="on_success" name="on_success"
value="page" value="page"
<% if(on_success == 'page') %>checked="checked"<% endif %>
/><%= __('Go to page') %> /><%= __('Go to page') %>
</label> </label>
</p> </p>
@ -93,10 +98,10 @@
> >
<select name="success_page"> <select name="success_page">
<% for page in pages %> <% for page in pages %>
<option value="<%= page.ID %>" <option value="<%= page.id %>"
<%- if form.data.settings.success_page != page.ID %> <%- if form.data.settings.success_page != page.id %>
<%=- ' selected="selected"' %> <%=- ' selected="selected"' %>
<%- endif %>><%= page.post_title %></option> <%- endif %>><%= page.title %></option>
<% endfor %> <% endfor %>
</select> </select>
</p> </p>
@ -173,8 +178,10 @@
</div> </div>
<%= javascript( <%= javascript(
'vendor.js',
'lib/prototype.min.js', 'lib/prototype.min.js',
'lib/scriptaculous.min.js', 'lib/scriptaculous.min.js',
'form_editor_lib.js',
'form_editor.js' 'form_editor.js'
)%> )%>
@ -309,20 +316,26 @@
// save change if necessary // save change if necessary
if(new_value !== '' && current_value !== new_value) { if(new_value !== '' && current_value !== new_value) {
console.log('TODO: form->save', { MailPoet.Ajax.post({
form: <%= form.id | default(0) %>, endpoint: 'forms',
form_name: new_value, action: 'save',
data: {
id: $('#mailpoet_form_id').val(),
name: new_value
}
}).done(function(response) {
MailPoet.Notice.success(
"<%= __('Form name successfully updated!') %>"
);
}); });
MailPoet.Notice.success("<%= __('Form name successfully updated!') %>");
} }
} }
} }
// on dom loaded // on dom loaded
$(function() { $(function() {
// load form // load form
WysijaForm.load(<%= form | json_encode | raw %>); WysijaForm.load(<%= form.data | raw %>);
// save form // save form
$('#mailpoet_form_save').on('click', function() { $('#mailpoet_form_save').on('click', function() {
@ -349,58 +362,35 @@
mailpoet_form_export(); mailpoet_form_export();
function mailpoet_form_save(callback) { function mailpoet_form_save(callback) {
console.log('TODO: form->save'); MailPoet.Ajax.post({
endpoint: 'forms',
// if there is a callback, call it! action: 'save_editor',
if(callback !== undefined) { data: {
callback(); id: $('#mailpoet_form_id').val(),
} form: WysijaForm.save()
/*mailpoet_post_json('form_save.php', {
form: <%= form.form %>,
data: WysijaForm.save()
}, function(response) {
if(response.result === false) {
var error = null;
if(response.error !== undefined) {
// display custom error message
error = $('.mailpoet_error[data-error="'+response.error+'"]');
}
if(error !== null) {
$(error).show();
} else {
// display unknown error message
MailPoet.Notice.error("<%= __('An unknown error occurred. Please try again or get in touch with our support.') %>", { scroll: true });
}
} else {
// if there is a callback, call it!
if(callback !== undefined) {
callback();
} else {
// otherwise display a success message
$success_message = str_replace(array(
'[link_widget]',
'[/link_widget]'
), array(
'<a href="'.admin_url('widgets.php').'" target="_blank">',
'</a>'
),
__('Saved! Add this form to [link_widget]a widget[/link_widget].')
);
?>
var success_message = "<?php echo addslashes($success_message); ?>";
if(response.is_widget === true) {
success_message = "<?php esc_html_e("Saved! The changes are already active in your widget."); ?>";
}
// display success message and scroll to it
MailPoet.Notice.hide(true);
MailPoet.Notice.success(success_message, { scroll: true, static: true });
}
} }
});*/ }).done(function(response) {
var message = null;
if(response.is_widget === true) {
message = "<%= __('Saved! The changes are already active in your widget.') %>";
} else {
message = "<%= __('Saved! Add this form to %1$sa widget%2$s.') | format("<a href='widgets.php' target='_blank'>", '</a>') | raw %>";
}
if(message !== null) {
MailPoet.Notice.hide();
MailPoet.Notice.success(message, {
scroll: true,
static: true
});
}
// if there is a callback, call it!
if(callback !== undefined) {
callback();
}
});
} }
// toolbar: on success toggle // toolbar: on success toggle
@ -505,15 +495,15 @@
}); });
// toolbar: list selection // toolbar: list selection
var selected_lists = <%= form.segments | json_encode | raw %>; var selected_segments = <%= form.segments | json_encode | raw %>;
var selected_lists_ids = []; var selected_segments_ids = [];
if(selected_lists !== null) { if(selected_segments !== null) {
selected_lists_ids = selected_lists.map(function(segment) { selected_segments_ids = selected_segments.map(function(segment) {
return parseInt(segment.id, 10); return parseInt(segment.id, 10);
}); });
} }
// enable select2 for list selection // enable select2 for list selection
$('select[name="lists"]').select2({ $('#mailpoet_form_segments').select2({
width:'100%', width:'100%',
templateResult: function(item) { templateResult: function(item) {
if(item.element && item.element.selected) { if(item.element && item.element.selected) {
@ -522,7 +512,7 @@
return item.text; return item.text;
} }
} }
}); }).select2('val', <%= form.segments | json_encode | raw %>);
// subscriber meta fields // subscriber meta fields
var meta_fields = [ var meta_fields = [

View File

@ -35,6 +35,10 @@ baseConfig = {
test: /\.jsx$/, test: /\.jsx$/,
loader: 'babel-loader' loader: 'babel-loader'
}, },
{
include: require.resolve('codemirror'),
loader: 'expose-loader?CodeMirror',
},
{ {
include: require.resolve('backbone'), include: require.resolve('backbone'),
loader: 'expose-loader?Backbone', loader: 'expose-loader?Backbone',
@ -79,8 +83,8 @@ config.push(_.extend({}, baseConfig, {
'settings/tabs.js' 'settings/tabs.js'
], ],
form_editor_lib: [ form_editor_lib: [
'select2', 'codemirror',
'codemirror' 'codemirror/mode/css/css'
], ],
newsletter_editor: [ newsletter_editor: [
'underscore', 'underscore',
@ -159,7 +163,6 @@ config.push(_.extend({}, baseConfig, {
'select2', 'select2',
'blob', 'blob',
'filesaver', 'filesaver',
'newsletter_editor/communicationsFix.js', 'newsletter_editor/communicationsFix.js',
'newsletter_editor/App', 'newsletter_editor/App',
'newsletter_editor/components/config.js', 'newsletter_editor/components/config.js',