Added option to disable WYSIWYG editor

This commit is contained in:
Andris Reinman 2016-04-11 21:35:04 -07:00
parent 58cce00829
commit 02c8fb4dda
20 changed files with 147 additions and 64 deletions

10
app.js
View file

@ -9,7 +9,6 @@ let path = require('path');
let favicon = require('serve-favicon');
let logger = require('morgan');
let cookieParser = require('cookie-parser');
let csp = require('content-security-policy');
let session = require('express-session');
let RedisStore = require('connect-redis')(session);
let flash = require('connect-flash');
@ -51,7 +50,7 @@ app.disable('x-powered-by');
* in a situation where we consume a flash messages but then comes a redirect
* and the message is never displayed
*/
hbs.registerHelper('flash_messages', function () { // eslint-disable-line prefer-arrow-callback
hbs.registerHelper('flash_messages', function() { // eslint-disable-line prefer-arrow-callback
if (typeof this.flash !== 'function') { // eslint-disable-line no-invalid-this
return '';
}
@ -114,13 +113,6 @@ app.use(session({
}));
app.use(flash());
// Content-Security-Policy headers
let cspOptions = Object.create(csp.STARTER_OPTIONS);
cspOptions['style-src'] = '\'self\' \'unsafe-inline\' https://fonts.googleapis.com';
cspOptions['img-src'] = '\'self\' data:';
cspOptions['font-src'] = '\'self\' https://fonts.gstatic.com';
app.use(csp.getCSP(cspOptions));
app.use(bodyParser.urlencoded({
extended: true
}));

View file

@ -202,6 +202,7 @@ module.exports.subscribe = (cid, optInIp, callback) => {
connection.release();
callback(null, {
list: listId,
cid,
email
});
});

View file

@ -32,7 +32,6 @@
"config": "^1.20.0",
"connect-flash": "^0.1.1",
"connect-redis": "^3.0.2",
"content-security-policy": "^0.2.0",
"cookie-parser": "^1.4.1",
"csurf": "^1.8.3",
"csv-parse": "^1.0.4",

11
public/ace/ace.js Normal file

File diff suppressed because one or more lines are too long

1
public/ace/mode-html.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
ace.define("ace/theme/chrome",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-chrome",t.cssText='.ace-chrome .ace_gutter {background: #ebebeb;color: #333;overflow : hidden;}.ace-chrome .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-chrome {background-color: #FFFFFF;color: black;}.ace-chrome .ace_cursor {color: black;}.ace-chrome .ace_invisible {color: rgb(191, 191, 191);}.ace-chrome .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-chrome .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-chrome .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-chrome .ace_invalid {background-color: rgb(153, 0, 0);color: white;}.ace-chrome .ace_fold {}.ace-chrome .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-chrome .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-chrome .ace_support.ace_type,.ace-chrome .ace_support.ace_class.ace-chrome .ace_support.ace_other {color: rgb(109, 121, 222);}.ace-chrome .ace_variable.ace_parameter {font-style:italic;color:#FD971F;}.ace-chrome .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-chrome .ace_comment {color: #236e24;}.ace-chrome .ace_comment.ace_doc {color: #236e24;}.ace-chrome .ace_comment.ace_doc.ace_tag {color: #236e24;}.ace-chrome .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-chrome .ace_variable {color: rgb(49, 132, 149);}.ace-chrome .ace_xml-pe {color: rgb(104, 104, 91);}.ace-chrome .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-chrome .ace_heading {color: rgb(12, 7, 255);}.ace-chrome .ace_list {color:rgb(185, 6, 144);}.ace-chrome .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-chrome .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-chrome .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-chrome .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-chrome .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-chrome .ace_gutter-active-line {background-color : #dcdcdc;}.ace-chrome .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-chrome .ace_storage,.ace-chrome .ace_keyword,.ace-chrome .ace_meta.ace_tag {color: rgb(147, 15, 128);}.ace-chrome .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-chrome .ace_string {color: #1A1AA6;}.ace-chrome .ace_entity.ace_other.ace_attribute-name {color: #994409;}.ace-chrome .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})

File diff suppressed because one or more lines are too long

View file

@ -27,3 +27,7 @@ body {
div.jumbotron{
margin-top: -21px;
}
.code-editor {
height: 400px;
}

View file

@ -1,5 +1,6 @@
/* eslint-env browser */
/* globals $: false */
/* eslint no-invalid-this: 0, no-var: 0, prefer-arrow-callback: 0 */
/* globals $: false, ace: false */
'use strict';
@ -7,3 +8,16 @@ $('.summernote').summernote({
height: 400,
tabsize: 2
});
$('div.code-editor').each(function() {
var editor = ace.edit(this);
var textarea = document.querySelector('input[name=html]');
editor.setTheme('ace/theme/chrome');
editor.getSession().setMode('ace/mode/html');
editor.getSession().setUseWrapMode(true);
editor.getSession().setUseSoftTabs(true);
editor.getSession().on('change', function() {
textarea.value = editor.getSession().getValue();
});
textarea.value = editor.getSession().getValue();
});

View file

@ -130,40 +130,47 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
});
});
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
campaigns.get(req.params.id, false, (err, campaign) => {
if (err || !campaign) {
req.flash('danger', err && err.message || err || 'Could not find campaign with specified ID');
return res.redirect('/campaigns');
}
lists.quicklist((err, listItems) => {
settings.list(['disableWysiwyg'], (err, configItems) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/');
return next(err);
}
if (Number(campaign.list)) {
listItems.forEach(list => {
list.segments.forEach(segment => {
if (segment.id === campaign.segment) {
segment.selected = true;
lists.quicklist((err, listItems) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/');
}
if (Number(campaign.list)) {
listItems.forEach(list => {
list.segments.forEach(segment => {
if (segment.id === campaign.segment) {
segment.selected = true;
}
});
if (list.id === campaign.list && !campaign.segment) {
list.selected = true;
}
});
if (list.id === campaign.list && !campaign.segment) {
list.selected = true;
}
});
}
}
campaign.csrfToken = req.csrfToken();
campaign.listItems = listItems;
campaign.useEditor = true;
campaign.csrfToken = req.csrfToken();
campaign.listItems = listItems;
campaign.useEditor = true;
campaign.showGeneral = req.query.tab === 'general' || !req.query.tab;
campaign.showTemplate = req.query.tab === 'template';
campaign.disableWysiwyg = configItems.disableWysiwyg;
campaign.showGeneral = req.query.tab === 'general' || !req.query.tab;
campaign.showTemplate = req.query.tab === 'template';
res.render('campaigns/edit', campaign);
res.render('campaigns/edit', campaign);
});
});
});
});

View file

@ -11,7 +11,7 @@ let url = require('url');
let settings = require('../lib/models/settings');
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use'];
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg'];
router.all('/*', (req, res, next) => {
if (!req.user) {
@ -76,7 +76,7 @@ router.post('/update', passport.parseForm, passport.csrfProtection, (req, res) =
});
// checkboxs are not included in value listing if left unchecked
['smtp_log', 'smtp_self_signed', 'smtp_disable_auth', 'verp_use'].forEach(key => {
['smtp_log', 'smtp_self_signed', 'smtp_disable_auth', 'verp_use', 'disable_wysiwyg'].forEach(key => {
if (keys.indexOf(key) < 0) {
keys.push(key);
values.push('');

View file

@ -42,7 +42,8 @@ router.get('/subscribe/:cid', (req, res, next) => {
res.render('subscription/subscribed', {
title: list.name,
layout: 'subscription/layout',
homepage: configItems.defaultHomepage || configItems.serviceUrl
homepage: configItems.defaultHomepage || configItems.serviceUrl,
preferences: '/subscription/' + list.cid + '/manage/' + subscription.cid
});
});
});

View file

@ -47,7 +47,7 @@ router.get('/create', passport.csrfProtection, (req, res, next) => {
data.csrfToken = req.csrfToken();
data.useEditor = true;
settings.list(['defaultPostaddress', 'defaultSender'], (err, configItems) => {
settings.list(['defaultPostaddress', 'defaultSender', 'disableWysiwyg'], (err, configItems) => {
if (err) {
return next(err);
}
@ -64,6 +64,7 @@ router.get('/create', passport.csrfProtection, (req, res, next) => {
data.html = data.html || rendererHtml(configItems);
data.text = data.text || rendererText(configItems);
data.disableWysiwyg = configItems.disableWysiwyg;
res.render('templates/create', data);
});
});
@ -81,15 +82,21 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) =
});
});
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
templates.get(req.params.id, (err, template) => {
if (err || !template) {
req.flash('danger', err && err.message || err || 'Could not find template with specified ID');
return res.redirect('/templates');
}
template.csrfToken = req.csrfToken();
template.useEditor = true;
res.render('templates/edit', template);
settings.list(['disableWysiwyg'], (err, configItems) => {
if (err) {
return next(err);
}
template.csrfToken = req.csrfToken();
template.useEditor = true;
template.disableWysiwyg = configItems.disableWysiwyg;
res.render('templates/edit', template);
});
});
});

View file

@ -145,14 +145,19 @@
<div class="form-group">
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
<div class="col-sm-10">
<textarea class="form-control summernote" if="template-html" name="html" rows="8">{{html}}</textarea>
{{#if disableWysiwyg}}
<div class="code-editor" id="template-html">{{html}}</div>
<input type="hidden" name="html">
{{else}}
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
{{/if}}
</div>
</div>
<div class="form-group">
<label for="template-text" class="col-sm-2 control-label">Template content (plaintext)</label>
<div class="col-sm-10">
<textarea class="form-control" if="template-text" name="text" rows="10">{{text}}</textarea>
<textarea class="form-control" id="template-text" name="text" rows="10">{{text}}</textarea>
</div>
</div>
</fieldset>

View file

@ -1,19 +1,32 @@
<p>Hey [FIRST_NAME/Customer],</p>
<!doctype html>
<html>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.</p>
<head>
<meta charset="utf-8">
</head>
<p>Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus
libero lacus a est.</p>
<body>
<p>Cheers,
<br/> {{defaultSender}}
</p>
<p>
{{defaultPostaddress}}
<br/>
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">Preferences</a>
<span style="color: #444444;">&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
<span style="color: #444444;">&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">View this email in your browser</a>
</p>
<p>Hey [FIRST_NAME/Customer],</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est.</p>
<p>Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus libero lacus a est. Aenean at mollis ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius, leo a ullamcorper feugiat, ante purus sodales justo, a faucibus
libero lacus a est.</p>
<p>Cheers,
<br/> {{defaultSender}}
</p>
<p>
{{defaultPostaddress}}
<br/>
<a href="[LINK_PREFERENCES]" style="color: #666666; text-decoration: none;">Preferences</a>
<span style="color: #444444;">&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
<span style="color: #444444;">&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">View this email in your browser</a>
</p>
</body>
</html>

View file

@ -139,6 +139,7 @@
<script src="/javascript/tables.js"></script>
{{#if useEditor}}
<script src="/ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="/summernote/summernote.min.js"></script>
<script src="/javascript/editor.js"></script>
{{/if}}

View file

@ -36,6 +36,16 @@
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
<div class="checkbox">
<label>
<input type="checkbox" name="disable-wysiwyg" {{#if disableWysiwyg}} checked {{/if}}> Disable WYSIWYG editor
</label>
</div>
</div>
</div>
</fieldset>
<fieldset>
@ -204,7 +214,8 @@
</p>
<p class="text-info">
To get VERP working you need to set up a DNS MX record that points to your Mailtrain hostname. You must also ensure that Mailtrain VERP interface is available from port 25 of your server (port 25 usually requires root user privileges). This way if anyone tries to send email to <code>someuser@{{verpHostname}}</code> then the email should end up to this server.
To get VERP working you need to set up a DNS MX record that points to your Mailtrain hostname. You must also ensure that Mailtrain VERP interface is available from port 25 of your server (port 25 usually requires root user privileges). This way if anyone
tries to send email to <code>someuser@{{verpHostname}}</code> then the email should end up to this server.
</p>
<p class="text-warning">

View file

@ -6,6 +6,10 @@
<p>
<a class="btn btn-primary" href="{{homepage}}" role="button">
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> return to our website
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> continue to our website
</a>
or
<a class="btn btn-primary" href="{{preferences}}" role="button">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> manage you preferences
</a>
</p>

View file

@ -57,21 +57,26 @@
<div class="form-group">
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
<div class="col-sm-10">
<textarea class="form-control summernote" if="template-html" name="html" rows="8">{{html}}</textarea>
{{#if disableWysiwyg}}
<div class="code-editor" id="template-html">{{html}}</div>
<input type="hidden" name="html">
{{else}}
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
{{/if}}
</div>
</div>
<div class="form-group">
<label for="template-text" class="col-sm-2 control-label">Template content (plaintext)</label>
<div class="col-sm-10">
<textarea class="form-control" if="template-text" name="text" rows="10">{{text}}</textarea>
<textarea class="form-control" id="template-text" name="text" rows="10">{{text}}</textarea>
</div>
</div>
<div class="form-group">
<label for="template-description" class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<textarea class="form-control" if="template-description" name="description" rows="3" placeholder="Optional comments about this template">{{description}}</textarea>
<textarea class="form-control" id="template-description" name="description" rows="3" placeholder="Optional comments about this template">{{description}}</textarea>
<span class="help-block">HTML is allowed</span>
</div>
</div>

View file

@ -63,14 +63,19 @@
<div class="form-group">
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
<div class="col-sm-10">
<textarea class="form-control summernote" if="template-html" name="html" rows="8">{{html}}</textarea>
{{#if disableWysiwyg}}
<div class="code-editor" id="template-html">{{html}}</div>
<input type="hidden" name="html">
{{else}}
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
{{/if}}
</div>
</div>
<div class="form-group">
<label for="template-text" class="col-sm-2 control-label">Template content (plaintext)</label>
<div class="col-sm-10">
<textarea class="form-control" if="template-text" name="text" rows="10">{{text}}</textarea>
<textarea class="form-control" id="template-text" name="text" rows="10">{{text}}</textarea>
</div>
</div>