Added option to disable WYSIWYG editor
This commit is contained in:
parent
58cce00829
commit
02c8fb4dda
20 changed files with 147 additions and 64 deletions
10
app.js
10
app.js
|
@ -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
|
||||
}));
|
||||
|
|
|
@ -202,6 +202,7 @@ module.exports.subscribe = (cid, optInIp, callback) => {
|
|||
connection.release();
|
||||
callback(null, {
|
||||
list: listId,
|
||||
cid,
|
||||
email
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
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
1
public/ace/mode-html.js
Normal file
File diff suppressed because one or more lines are too long
1
public/ace/theme-chrome.js
Normal file
1
public/ace/theme-chrome.js
Normal 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)})
|
1
public/ace/worker-html.js
Normal file
1
public/ace/worker-html.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -27,3 +27,7 @@ body {
|
|||
div.jumbotron{
|
||||
margin-top: -21px;
|
||||
}
|
||||
|
||||
.code-editor {
|
||||
height: 400px;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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('');
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;"> | </span>
|
||||
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
|
||||
<span style="color: #444444;"> | </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;"> | </span>
|
||||
<a href="[LINK_UNSUBSCRIBE]" style="color: #666666; text-decoration: none;">Unsubscribe</a>
|
||||
<span style="color: #444444;"> | </span>
|
||||
<a href="[LINK_BROWSER]" style="color: #666666; text-decoration: none;">View this email in your browser</a>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in a new issue