Commit b91c26c9 authored by wu-lee's avatar wu-lee

Merge branch 'email-templates'

* email-templates:
  serve.js - log the hostname too
  serve.js - set 'trust proxy' to true
  serve.js - log the status code into the metadata correctly
  serve.js - move email template code into lib/email-templates.js
  serve.js - load lib/helpers.js into handlebars used for email
  lib/helpers.js - add a parags helper, for use in email templates.
  metalsmith.js - exclude the email-templates directory
  serve.js,email.js - also send an ack email to registrant
  serve.js - allow beautified notification emails
parents 3de6ba0e 4d84a433
......@@ -24,12 +24,25 @@ module.exports = {
tls: true,
},
registration: {
// REQUIRED field!
// REQUIRED fields!
// Where replies to notifications/acknowledgements go
from: "Registrations <registrations@example.com>",
// REQUIRED field!
// Where notifications go (acknowledgements go to the
// registrant).
to: "Registrations <registrations@example.com>",
subject: "Someone has registered!",
}
// The subject line for notifications
notification: "Someone has registered!",
// The subject line for acknowledgements
acknowledgement: "Thanks for registering!",
},
templates: {
// Note: recommended place for templates is in src/email-templates/.
// notification: 'src/email-templates/notification-template.hbs',
// acknowledgement: 'src/email-templates/acknowledgement-template.hbs',
// Theme config file included in template context under 'config' key.
// Also, request under 'request'
},
},
webhook: {
path: '/example-path',
......
'use strict';
/** Defines functions which map registration data into HTML email
* content for user acknowledgements and admin notifications.
*/
const handlebars = require('handlebars');
const fs = require('fs');
const handlebarsHelpers = require('./helpers.js');
const config = require('./config.js');
const maillog = require('./logging.js').loggers.mail;
// Register the helpers - we use the same ones as supplied to the
// theme, out of laziness.
for(var name in handlebarsHelpers) {
handlebars.registerHelper(name, handlebarsHelpers[name]);
}
function defineTemplateLoader(file, name, defaultTemplate) {
if (!file) {
maillog.warn(`no configured ${name} template file, using default`);
return defaultTemplate;
}
maillog.info(`${name} will be loaded from the file: ${file}`);
return (context) => {
if (fs.existsSync(file)) {
maillog.info(`loading ${name} from file: ${file}`);
const content = fs.readFileSync(file, 'utf8');
return handlebars.compile(content)(context);
}
else {
maillog.warn(`using default as no ${name} found at ${file}`);
return defaultTemplate(context);
}
};
}
// Define functions which returns the email templates. These are
// re-loaded on each send so that they can be defined by files in the
// source (with inline handlebars markup). (These templates can be
// excluded from appearing in the output wiki by various means.) The
// one required field in submitted data is an 'email' field with a
// recipient address. All fields, plus parts of the config, are
// supplied to the templates.
var notificationTemplate, acknowledgementTemplate;
if (config.email && config.email.templates) {
notificationTemplate = config.email.templates.notification;
acknowledgementTemplate = config.email.templates.acknowledgement;
}
module.exports = {
notification: defineTemplateLoader(
notificationTemplate,
'email notification template',
(context) =>
`<pre>${JSON.stringify(context.request.body, null, 2)}</pre>`
),
acknowledgement: defineTemplateLoader(
acknowledgementTemplate,
'email acknowledgement template',
(context) => 'Thanks! We shall be in touch.'
),
};
......@@ -4,7 +4,15 @@ const toText = require('html2plaintext');
module.exports = (serverConfig = {}) => {
async function send(config, html) {
/** Sends a notification email.
*
* @param config - the emailjs config
* @param html - the HTML message (will have a text version added)
* @param json - optional JSON attachment (the registration data)
* @returns a promise which can be used to obtain the success
* or failure of this (potentially slow) operation.
*/
async function send(config, html, json) {
if (!config.from)
throw new Error("must send with a from: parameter");
if (!config.to)
......@@ -12,12 +20,17 @@ module.exports = (serverConfig = {}) => {
var html = String(html);
const server = email.server.connect(serverConfig);
const attachments = [{data: html, alternative: true}];
if (json)
attachments.push({
data: JSON.stringify(json),
name: 'form-data.json',
type: 'application/json'
});
const message = {
...config,
text: toText(html),
attachment: [
{data: html, alternative: true},
],
attachment: attachments
};
// Send the message and await completion. Return
......
......@@ -34,6 +34,14 @@ module.exports = {
return new Handlebars.SafeString(link);
},
/** Html escapes text, but converts adjacent newlines into paragraph tags */
parags: function() {
var [opts, text] = getopts(arguments);
text = Handlebars.Utils.escapeExpression(text);
text = text.replace(/\n\s*\n/mg, '<p>');
return new Handlebars.SafeString(text);
},
// Ghost emulations
asset: function(path) {
return '/'+path;
......
......@@ -134,6 +134,7 @@ module.exports = () => {
.ignore([
'**/*~', // Emacs backup files
'.*', // dotfiles
'email-templates', // email templates directory
])
.clean(true) // clean destination before building
.use(metalsmithDebug())
......
'use strict';
const express = require('express');
const fs = require('fs');
const emailTemplates = require('./lib/email-templates.js');
const metalsmith = require('./metalsmith.js');
const email = require('./lib/email.js');
const git = require('./lib/git.js')({});
......@@ -39,7 +40,6 @@ var fileOptions = {
}
};
async function build() {
buildlog.info('starting build...')
metalsmith().build(function(err) {
......@@ -49,17 +49,19 @@ async function build() {
buildlog.info('completed build.')
}
app.set('trust proxy', true); // Assume we're behind a proxy
app.use(express.static('/', expressOptions));
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.all('*', (req, res, next) => {
next();
httplog.info(`${req.method} ${req.url} ${req.ip} ${res.statusCode}`, {
httplog.info(`${req.method} ${req.url} ${req.hostname} ${req.ip} ${res.statusCode}`, {
method: req.method,
url: req.url,
ip: req.ip,
code: res.code,
host: req.hostname,
code: res.statusCode,
});
})
app.get('/', (req, res) => {
......@@ -118,13 +120,52 @@ app.post('/registration', (req, res) => {
submitlog.info("error emailing registration notification, ",
err);
}
submitlog.info('emailing registration notification: ', req.body);
// Send notification of registration to admins
const sendConfig = config.email.registration || {};
send(sendConfig,
`<pre>${JSON.stringify(req.body, null, 2)}</pre>`)
.then(ok, error);
const templateContext = {
config: {theme: config.theme},
request: req
};
submitlog.info(`emailing registration notification to ${sendConfig.to}`, req.body);
const notificationConfig = {
from: sendConfig.from,
to: sendConfig.to,
subject: sendConfig.notification,
};
try {
const html = emailTemplates.notification(templateContext);
send(notificationConfig,
html,
req.body)
.then(ok, error);
}
catch(e) {
maillog.error(`failed to send notification of registration to ${notificationConfig.to}: `, e);
}
if (req.body.email) {
// Send acknowledgement email to registrant
const acknowledgementConfig = {
from: sendConfig.from,
to: req.body.email,
subject: sendConfig.acknowledgement,
};
try {
const html = emailTemplates.acknowledgement(templateContext);
submitlog.info(`emailing registration acknowledgement to ${acknowledgementConfig.to}: `);
send(acknowledgementConfig,
html)
.then(ok, error);
}
catch(e) {
maillog.error(`failed to send registration acknowledgement to ${acknowledgementConfig.to} `, e);
}
}
else {
maillog.error(`cannot send registration acknowledgement, no email set in registration`);
}
res.redirect('/home');
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment