156 lines
4.1 KiB
JavaScript
156 lines
4.1 KiB
JavaScript
'use strict';
|
|
|
|
const _ = require('./translate')._;
|
|
const util = require('util');
|
|
const isemail = require('isemail');
|
|
const path = require('path');
|
|
|
|
const bluebird = require('bluebird');
|
|
|
|
const hasher = require('node-object-hash')();
|
|
const mjml = require('mjml');
|
|
const hbs = require('hbs');
|
|
const juice = require('juice');
|
|
|
|
const fsReadFile = bluebird.promisify(require('fs').readFile);
|
|
const jsdomEnv = bluebird.promisify(require('jsdom').env);
|
|
|
|
|
|
|
|
const templates = new Map();
|
|
|
|
async function getTemplate(template) {
|
|
if (!template) {
|
|
return false;
|
|
}
|
|
|
|
const key = (typeof template === 'object') ? hasher.hash(template) : template;
|
|
|
|
if (templates.has(key)) {
|
|
return templates.get(key);
|
|
}
|
|
|
|
let source;
|
|
if (typeof template === 'object') {
|
|
source = await mergeTemplateIntoLayout(template.template, template.layout);
|
|
} else {
|
|
source = await fsReadFile(path.join(__dirname, '..', 'views', template), 'utf-8');
|
|
}
|
|
|
|
if (template.type === 'mjml') {
|
|
const compiled = mjml.mjml2html(source);
|
|
|
|
if (compiled.errors.length) {
|
|
throw new Error(compiled.errors[0].message || compiled.errors[0]);
|
|
}
|
|
|
|
source = compiled.html;
|
|
}
|
|
|
|
const renderer = hbs.handlebars.compile(source);
|
|
templates.set(key, renderer);
|
|
|
|
return renderer;
|
|
}
|
|
|
|
|
|
async function mergeTemplateIntoLayout(template, layout) {
|
|
layout = layout || '{{{body}}}';
|
|
|
|
async function readFile(relPath) {
|
|
return await fsReadFile(path.join(__dirname, '..', 'views', relPath), 'utf-8');
|
|
}
|
|
|
|
// Please dont end your custom messages with .hbs ...
|
|
if (layout.endsWith('.hbs')) {
|
|
layout = await readFile(layout);
|
|
}
|
|
|
|
if (template.endsWith('.hbs')) {
|
|
template = await readFile(template);
|
|
}
|
|
|
|
const source = layout.replace(/\{\{\{body\}\}\}/g, template);
|
|
return source;
|
|
}
|
|
|
|
|
|
async function validateEmail(address, checkBlocked) {
|
|
let user = (address || '').toString().split('@').shift().toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
|
|
if (checkBlocked && blockedUsers.indexOf(user) >= 0) {
|
|
throw new new Error(util.format(_('Blocked email address "%s"'), address));
|
|
}
|
|
|
|
const result = await new Promise(resolve => {
|
|
const result = isemail.validate(address, {
|
|
checkDNS: true,
|
|
errorLevel: 1
|
|
}, resolve);
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
function validateEmailGetMessage(result, address) {
|
|
if (result !== 0) {
|
|
let message = util.format(_('Invalid email address "%s".'), address);
|
|
switch (result) {
|
|
case 5:
|
|
message += ' ' + _('MX record not found for domain');
|
|
break;
|
|
case 6:
|
|
message += ' ' + _('Address domain not found');
|
|
break;
|
|
case 12:
|
|
message += ' ' + _('Address domain name is required');
|
|
break;
|
|
}
|
|
return message;
|
|
}
|
|
}
|
|
|
|
async function prepareHtml(html) {
|
|
if (!(html || '').toString().trim()) {
|
|
return false;
|
|
}
|
|
|
|
const win = await jsdomEnv(false, false, {
|
|
html,
|
|
features: {
|
|
FetchExternalResources: false, // disables resource loading over HTTP / filesystem
|
|
ProcessExternalResources: false // do not execute JS within script blocks
|
|
}
|
|
});
|
|
|
|
const head = win.document.querySelector('head');
|
|
let hasCharsetTag = false;
|
|
const metaTags = win.document.querySelectorAll('meta');
|
|
if (metaTags) {
|
|
for (let i = 0; i < metaTags.length; i++) {
|
|
if (metaTags[i].hasAttribute('charset')) {
|
|
metaTags[i].setAttribute('charset', 'utf-8');
|
|
hasCharsetTag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!hasCharsetTag) {
|
|
const charsetTag = win.document.createElement('meta');
|
|
charsetTag.setAttribute('charset', 'utf-8');
|
|
head.appendChild(charsetTag);
|
|
}
|
|
const preparedHtml = '<!doctype html><html>' + win.document.documentElement.innerHTML + '</html>';
|
|
|
|
return juice(preparedHtml);
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
validateEmail,
|
|
validateEmailGetMessage,
|
|
mergeTemplateIntoLayout,
|
|
getTemplate,
|
|
prepareHtml
|
|
};
|
|
|