Blame view

node_modules/nodemailer/lib/template-sender.js 3.9 KB
f7563de62   Palak Handa   first commit
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 '&amp;';
                          case '<':
                              return '&lt;';
                          case '>':
                              return '&gt;';
                          case '"':
                              return '&quot;';
                          case '\'':
                              return '&#039;';
                          default:
                              return char;
                      }
                  });
              }
              return value;
          }
          return match;
      });
  }