Blame view
node_modules/nodemailer/lib/template-sender.js
3.9 KB
f7563de62
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
'use strict'; var shared = require('nodemailer-shared'); module.exports = templateSender; // expose for testing module.exports.render = render; /** * Create template based e-mail sender * * @param {Object} transport Nodemailer transport object to use for actual sending * @param {Object} templates Either an object with template strings or an external renderer * @param {Object} [defaults] Default fields set for every mail sent using this sender * @return {Function} Template based sender */ function templateSender(transport, templates, defaults) { templates = templates || {}; defaults = defaults || {}; // built in renderer var defaultRenderer = function (context, callback) { var rendered = {}; Object.keys(templates).forEach(function (key) { rendered[key] = render(templates[key], { escapeHtml: key === 'html' }, context); }); callback(null, rendered); }; // actual renderer var renderer = (typeof templates.render === 'function' ? templates.render.bind(templates) : defaultRenderer); return function (fields, context, callback) { var promise; if (!callback && typeof Promise === 'function') { promise = new Promise(function (resolve, reject) { callback = shared.callbackPromise(resolve, reject); }); } // render data renderer(context, function (err, rendered) { if (err) { return callback(err); } var mailData = mix(defaults, fields, rendered); setImmediate(function () { transport.sendMail(mailData, callback); }); }); return promise; }; } /** * Merges multiple objects into one. Assumes single level, except 'headers' */ function mix( /* obj1, obj2, ..., objN */ ) { var args = Array.prototype.slice.call(arguments); var result = {}; args.forEach(function (arg) { Object.keys(arg || {}).forEach(function (key) { if (key === 'headers') { if (!result.headers) { result.headers = {}; } Object.keys(arg[key]).forEach(function (hKey) { if (!(hKey in result.headers)) { result.headers[hKey] = arg[key][hKey]; } }); } else if (!(key in result)) { result[key] = arg[key]; } }); }); return result; } /** * Renders a template string using provided context. Values are marked as {{key}} in the template. * * @param {String} str Template string * @param {Object} options Render options. options.escapeHtml=true escapes html specific characters * @param {Object} context Key-value pairs for the template, eg {name: 'User Name'} * @return {String} Rendered template */ function render(str, options, context) { str = (str || '').toString(); context = context || {}; options = options || {}; var re = /\{\{[ ]*([^{}\s]+)[ ]*\}\}/g; return str.replace(re, function (match, key) { var value; if (context.hasOwnProperty(key)) { value = context[key].toString(); if (options.escapeHtml) { value = value.replace(/["'&<>]/g, function (char) { switch (char) { case '&': return '&'; case '<': return '<'; case '>': return '>'; case '"': return '"'; case '\'': return '''; default: return char; } }); } return value; } return match; }); } |