diff --git a/app.js b/app.js index d6b05349..36525f50 100644 --- a/app.js +++ b/app.js @@ -47,6 +47,8 @@ if (config.www.proxy) { // Do not expose software used app.disable('x-powered-by'); +hbs.registerPartials(__dirname + '/views/partials'); + /** * We need this helper to make sure that we consume flash messages only * when we are able to actually display these. Otherwise we might end up diff --git a/config/default.toml b/config/default.toml index 60780084..aee1c248 100644 --- a/config/default.toml +++ b/config/default.toml @@ -21,6 +21,12 @@ # Process title visible in monitoring logs and process listing title="mailtrain" +# Enabled HTML editors +editors=[ + ["summernote", "Summernote"], + ["codeeditor", "Code Editor"] +] + # If you start out as a root user (eg. if you want to use ports lower than 1000) # then you can downgrade the user once all services are up and running #user="nobody" diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js index e5b12e70..8d2a31ce 100644 --- a/lib/models/campaigns.js +++ b/lib/models/campaigns.js @@ -13,7 +13,7 @@ let log = require('npmlog'); let mailer = require('../mailer'); let humanize = require('humanize'); -let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'tracking_disabled']; +let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'tracking_disabled']; module.exports.list = (start, limit, callback) => { db.getConnection((err, connection) => { @@ -729,6 +729,8 @@ module.exports.create = (campaign, opts, callback) => { return callback(new Error('Selected template not found')); } + campaign.editorName = template.editorName; + campaign.editorData = template.editorData; campaign.html = template.html; campaign.text = template.text; diff --git a/lib/models/templates.js b/lib/models/templates.js index ea54cac3..6df4c6ad 100644 --- a/lib/models/templates.js +++ b/lib/models/templates.js @@ -3,7 +3,7 @@ let db = require('../db'); let tools = require('../tools'); -let allowedKeys = ['description', 'html', 'text']; +let allowedKeys = ['description', 'editor_name', 'editor_data', 'html', 'text']; module.exports.list = (start, limit, callback) => { db.getConnection((err, connection) => { diff --git a/meta.json b/meta.json index f0ac0c49..731dc8bf 100644 --- a/meta.json +++ b/meta.json @@ -1,3 +1,3 @@ { - "schemaVersion": 20 + "schemaVersion": 21 } diff --git a/routes/archive.js b/routes/archive.js index ff8fcab4..a4356462 100644 --- a/routes/archive.js +++ b/routes/archive.js @@ -62,8 +62,13 @@ router.get('/:campaign/:list/:subscription', passport.csrfProtection, (req, res, } let renderHtml = (html, renderTags) => { - res.render('archive/view', { - layout: 'archive/layout', + campaign.editorName = campaign.editorName || 'summernote'; + let sfx = ''; + if (campaign.editorName !== 'summernote' && campaign.editorName !== 'codeeditor') { + sfx = '-raw'; + } + res.render('archive/view' + sfx, { + layout: 'archive/layout' + sfx, message: renderTags ? tools.formatMessage(serviceUrl, campaign, list, subscription, html) : html, campaign, list, diff --git a/routes/campaigns.js b/routes/campaigns.js index 8a9b2e23..e5d715a9 100644 --- a/routes/campaigns.js +++ b/routes/campaigns.js @@ -1,5 +1,6 @@ 'use strict'; +let config = require('config'); let express = require('express'); let router = new express.Router(); let lists = require('../lib/models/lists'); @@ -115,7 +116,10 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) = return res.redirect('/campaigns/create?' + tools.queryParams(req.body)); } req.flash('success', 'Campaign “' + req.body.name + '” created'); - res.redirect('/campaigns/view/' + id); + res.redirect((req.body.type === 'rss') + ? '/campaigns/edit/' + id + : '/campaigns/edit/' + id + '?tab=template' + ); }); }); @@ -159,6 +163,8 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => { campaign.csrfToken = req.csrfToken(); campaign.listItems = listItems; campaign.useEditor = true; + campaign.editorName = campaign.editorName || 'summernote'; + campaign.editorConfig = config[campaign.editorName]; campaign.disableWysiwyg = configItems.disableWysiwyg; campaign.showGeneral = req.query.tab === 'general' || !req.query.tab; diff --git a/routes/templates.js b/routes/templates.js index 60df77ab..576d446a 100644 --- a/routes/templates.js +++ b/routes/templates.js @@ -1,5 +1,6 @@ 'use strict'; +let config = require('config'); let express = require('express'); let router = new express.Router(); let templates = require('../lib/models/templates'); @@ -66,6 +67,23 @@ router.get('/create', passport.csrfProtection, (req, res, next) => { data.text = data.text || rendererText(configItems); data.disableWysiwyg = configItems.disableWysiwyg; + data.editors = config.editors || [['summernote', 'Summernote']]; + data.editors = data.editors.map(ed => { + let editor = { + name: ed[0], + label: ed[1], + }; + if (config[editor.name] && config[editor.name].templates) { + editor.templates = config[editor.name].templates.map(tmpl => { + return { + name: tmpl[0], + label: tmpl[1], + } + }); + } + return editor; + }); + res.render('templates/create', data); }); }); @@ -79,7 +97,7 @@ router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) = return res.redirect('/templates/create?' + tools.queryParams(req.body)); } req.flash('success', 'Template created'); - res.redirect('/templates'); + res.redirect('/templates/edit/' + id); }); }); @@ -95,6 +113,8 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => { } template.csrfToken = req.csrfToken(); template.useEditor = true; + template.editorName = template.editorName || 'summernote'; + template.editorConfig = config[template.editorName]; template.disableWysiwyg = configItems.disableWysiwyg; res.render('templates/edit', template); }); diff --git a/setup/sql/upgrade-00021.sql b/setup/sql/upgrade-00021.sql new file mode 100644 index 00000000..ca3a85ec --- /dev/null +++ b/setup/sql/upgrade-00021.sql @@ -0,0 +1,16 @@ +# Header section +# Define incrementing schema version number +SET @schema_version = '21'; + +# Add fields editor_name, editor_data to templates +ALTER TABLE `templates` ADD COLUMN `editor_name` varchar(50) DEFAULT '' AFTER `description`; +ALTER TABLE `templates` ADD COLUMN `editor_data` longtext DEFAULT '' AFTER `editor_name`; + +# Add fields editor_name, editor_data to campaigns +ALTER TABLE `campaigns` ADD COLUMN `editor_name` varchar(50) DEFAULT '' AFTER `source_url`; +ALTER TABLE `campaigns` ADD COLUMN `editor_data` longtext DEFAULT '' AFTER `editor_name`; + +# Footer section +LOCK TABLES `settings` WRITE; +INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version; +UNLOCK TABLES; diff --git a/views/archive/layout-raw.hbs b/views/archive/layout-raw.hbs new file mode 100644 index 00000000..38e54992 --- /dev/null +++ b/views/archive/layout-raw.hbs @@ -0,0 +1 @@ +{{{body}}} diff --git a/views/archive/view-raw.hbs b/views/archive/view-raw.hbs new file mode 100644 index 00000000..2e4767a6 --- /dev/null +++ b/views/archive/view-raw.hbs @@ -0,0 +1 @@ +{{{message}}} diff --git a/views/campaigns/create-rss.hbs b/views/campaigns/create-rss.hbs index 7ca4b849..1dbb715d 100644 --- a/views/campaigns/create-rss.hbs +++ b/views/campaigns/create-rss.hbs @@ -58,7 +58,7 @@
- +
New entries from this RSS URL are sent out to list subscribers as email messages diff --git a/views/campaigns/edit-rss.hbs b/views/campaigns/edit-rss.hbs index 9786b811..466fb021 100644 --- a/views/campaigns/edit-rss.hbs +++ b/views/campaigns/edit-rss.hbs @@ -125,17 +125,15 @@
-
- -
- {{#if disableWysiwyg}} -
{{html}}
- - {{else}} - - {{/if}} - Use special merge tag [RSS_ENTRY] to mark the position for the RSS post content. Additionally you can use any valid merge tag as well. + {{#if disableWysiwyg}} + {{> codeeditor}} + {{else}} + {{> summernote}} + {{/if}} +
+
+ Use special merge tag [RSS_ENTRY] to mark the position for the RSS post content. Additionally you can use any valid merge tag as well.
diff --git a/views/campaigns/edit-triggered.hbs b/views/campaigns/edit-triggered.hbs index eb6e2712..b4e575db 100644 --- a/views/campaigns/edit-triggered.hbs +++ b/views/campaigns/edit-triggered.hbs @@ -167,24 +167,13 @@
-
- -
- {{#if disableWysiwyg}} -
{{html}}
- - {{else}} - - {{/if}} -
-
+ {{> plaintext}} -
- -
- -
-
+ {{#if disableWysiwyg}} + {{> codeeditor}} + {{else}} + {{> (lookup . 'editorName') }} + {{/if}} {{/if}} diff --git a/views/campaigns/edit.hbs b/views/campaigns/edit.hbs index 7ca0dcde..b4df6a0d 100644 --- a/views/campaigns/edit.hbs +++ b/views/campaigns/edit.hbs @@ -183,24 +183,13 @@ -
- -
- {{#if disableWysiwyg}} -
{{html}}
- - {{else}} - - {{/if}} -
-
+ {{> plaintext}} -
- -
- -
-
+ {{#if disableWysiwyg}} + {{> codeeditor}} + {{else}} + {{> (lookup . 'editorName') }} + {{/if}} {{/if}} diff --git a/views/partials/codeeditor.hbs b/views/partials/codeeditor.hbs new file mode 100644 index 00000000..68bd951e --- /dev/null +++ b/views/partials/codeeditor.hbs @@ -0,0 +1,7 @@ +
+ +
+
{{html}}
+ +
+
diff --git a/views/partials/editor-bridge.hbs b/views/partials/editor-bridge.hbs new file mode 100644 index 00000000..3d65659f --- /dev/null +++ b/views/partials/editor-bridge.hbs @@ -0,0 +1,80 @@ + + + + + diff --git a/views/partials/html-preview.hbs b/views/partials/html-preview.hbs new file mode 100644 index 00000000..938f2be4 --- /dev/null +++ b/views/partials/html-preview.hbs @@ -0,0 +1,14 @@ +
+
+ +
+
320x480px
+ +
+
+
diff --git a/views/partials/plaintext.hbs b/views/partials/plaintext.hbs new file mode 100644 index 00000000..ebb2fdb5 --- /dev/null +++ b/views/partials/plaintext.hbs @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/views/partials/summernote.hbs b/views/partials/summernote.hbs new file mode 100644 index 00000000..317a6507 --- /dev/null +++ b/views/partials/summernote.hbs @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/views/templates/create.hbs b/views/templates/create.hbs index bff536f5..71f567b7 100644 --- a/views/templates/create.hbs +++ b/views/templates/create.hbs @@ -19,72 +19,21 @@
-
- -
- -

- Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: [TAG_NAME] or [TAG_NAME/fallback] where fallback is an optional text value used - when TAG_NAME is empty. -

- -
    -
  • - [EMAIL] – email address of the subscriber -
  • -
  • - [FIRST_NAME] – first name of the subscriber -
  • -
  • - [LAST_NAME] – last name of the subscriber -
  • -
  • - [FULL_NAME] – first and last names of the subscriber joined -
  • -
  • - [LINK_UNSUBSCRIBE] – URL that points to the preferences page of the subscriber -
  • -
  • - [LINK_PREFERENCES] – URL that points to the unsubscribe page -
  • -
  • - [LINK_BROWSER] – URL to preview the message in a browser -
  • -
  • - [SUBSCRIPTION_ID] – Unique ID that identifies the recipient -
  • -
  • - [LIST_ID] – Unique ID that identifies the list used for this campaign -
  • -
  • - [CAMPAIGN_ID] – Unique ID that identifies current campaign -
  • -
-

- In addition to that any custom field can have its own merge tag. -

-
+ +
+
-
- -
- - {{#if disableWysiwyg}} -
{{html}}
- - {{else}} - - {{/if}} - -
-
- -
- -
- +
+
+ +
@@ -102,5 +51,42 @@
- + +
+ {{#each editors}} + {{#if templates}} +
+ +
+ +
+
+ {{/if}} + {{/each}} + +
+ + +
+ +
+ + +
+
+ + diff --git a/views/templates/edit.hbs b/views/templates/edit.hbs index 86b8ce37..019ee274 100644 --- a/views/templates/edit.hbs +++ b/views/templates/edit.hbs @@ -72,24 +72,13 @@
-
- -
- {{#if disableWysiwyg}} -
{{html}}
- - {{else}} - - {{/if}} -
-
+ {{> plaintext}} -
- -
- -
-
+ {{#if disableWysiwyg}} + {{> codeeditor}} + {{else}} + {{> (lookup . 'editorName') }} + {{/if}}