src/support/mailer/base.js
"use strict";
/**
* @fileOverview Base class for mailer types.
*/
const co = require('co'),
path = require('path'),
marked = require('marked');
const waigo = global.waigo,
_ = waigo._,
logger = waigo.load('support/logger'),
Q = waigo.load('support/promise'),
errors = waigo.load('support/errors'),
NodeMailer = waigo.load('support/mailer/engines/nodeMailer').NodeMailer,
viewObjects = waigo.load('support/viewObjects');
const EmailTemplate = require('email-templates').EmailTemplate;
const MailerError = errors.define('MailerError');
/**
* Base mailer class.
*/
class Mailer {
constructor (App, config, typeString) {
this.App = App;
this.logger = logger.create(`Mailer-${typeString}`);
this.config = config;
}
* _init (transport) {
this._nodeMailer = new NodeMailer(
this.logger.create('NodeMailer'),
this.config, transport
);
}
_renderEmailTemplate (templateName, templateVars) {
this.logger.debug('Rendering template ' + templateName);
let templatePath =
path.dirname( waigo.getPath(`emails/${templateName}/html.pug`) );
let emailTemplate = new EmailTemplate(templatePath);
return new Q((resolve, reject) => {
emailTemplate.render(templateVars, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
* _renderBodyMarkdown (template, templateVars) {
var compiled = _.template(template, {
interpolate: /{{([\s\S]+?)}}/img
});
return marked(compiled(templateVars || {}));
}
* _renderBodyTemplate (templateName, templateVars) {
var body = yield this._renderEmailTemplate(templateName, templateVars);
return body.html;
}
* _renderBody (mailOptions, templateVars) {
let content;
if (mailOptions.bodyTemplate) {
content = yield this._renderBodyTemplate(mailOptions.bodyTemplate, templateVars);
} else {
content = yield this._renderBodyMarkdown(mailOptions.body, templateVars);
}
var templateVars = _.extend({}, this.App.templateVars, templateVars, {
content: content
});
var body = yield this._renderEmailTemplate('_layout', templateVars);
return body.html;
}
* _renderSubject (mailOptions, templateVars) {
var compiled = _.template(mailOptions.subject, {
interpolate: /{{([\s\S]+?)}}/img
});
return compiled(templateVars || {});
}
* _prepareMailOptions (mailOptions) {
mailOptions = _.extend({
from: this.config.from,
subject: null,
body: null,
bodyTemplate: null,
templateVars: {},
ctx: {},
allowEmpty: false
}, mailOptions);
if (_.isEmpty(mailOptions.to)) {
throw new MailerError('Recipients must be set');
}
// it not allowed to send empty email
if (!mailOptions.allowEmpty) {
if (_.isEmpty(mailOptions.subject) ||
(_.isEmpty(mailOptions.body) && _.isEmpty(mailOptions.bodyTemplate)) ) {
throw new MailerError('Subject and body/template must be set');
}
}
if (!_.isArray(mailOptions.to)) {
mailOptions.to = [mailOptions.to];
}
// templateVars common to all recipients
mailOptions.templateVars = _.extend({}, this.App.templateVars, mailOptions.ctx.templateVars,
yield viewObjects.toViewObjectYieldable(mailOptions.templateVars, mailOptions.ctx)
);
return mailOptions;
}
* _send (mailOptions) {
let self = this;
mailOptions = yield self._prepareMailOptions(mailOptions);
return yield _.map(mailOptions.to, (recipient) => {
return co.wrap(function*() {
// email address
var email = _.get(recipient, 'emailAddress', recipient);
self.logger.debug('Email ' + email + ': ' + mailOptions.subject);
// user-specific templateVars
var userLocals = _.extend({}, mailOptions.templateVars,
yield viewObjects.toViewObjectYieldable({
recipient: recipient
}, mailOptions.ctx)
);
// render body
var body = yield self._renderBody(mailOptions, userLocals);
var subject = yield self._renderSubject(mailOptions, userLocals);
// setup actual options
var sendOptions = _.extend({
from: mailOptions.from,
}, {
to: email,
subject: subject,
html: body
});
self.logger.debug('Content', sendOptions.html);
// send
var ret = yield self._nodeMailer.send(sendOptions);
// record
self.App.emit('record', 'email', recipient, {
subject: sendOptions.subject
});
return ret;
})();
});
}
* render (mailOptions) {
mailOptions = yield this._prepareMailOptions(mailOptions);
var recipient = mailOptions.to.pop();
var email = _.get(recipient, 'emails.0.email', recipient);
this.logger.debug('Render email ' + email + ': ' + mailOptions.subject);
// user-specific templateVars
var userLocals = _.extend({}, mailOptions.templateVars,
yield viewObjects.toViewObjectYieldable({
recipient: recipient
}, mailOptions.ctx)
);
return {
body: yield this._renderBody(mailOptions, userLocals),
subject: yield this._renderSubject(mailOptions, userLocals),
}
}
}
module.exports = Mailer;