Пример #1
0
var sendEmail = module.exports.sendEmail = function(templateModule, templateId, recipient, data, opts, callback) {
    data = data || {};
    opts = opts || {};
    callback = callback || function(err) {
        if (err && err.code === 400) {
            log().error({'err': err}, 'Failed to deliver due to validation error');
        }
    };

    var validator = new Validator();
    validator.check(templateModule, {'code': 400, 'msg': 'Must specify a template module'}).notEmpty();
    validator.check(templateId, {'code': 400, 'msg': 'Must specify a template id'}).notEmpty();
    validator.check(null, {'code': 400, 'msg': 'Must specify a user when sending an email'}).isObject(recipient);

    // Only validate the user email if it was a valid object
    if (recipient) {
        validator.check(recipient.email, {'code': 400, 'msg': 'User must have a valid email address to receive email'}).isEmail();
    }

    if (validator.hasErrors()) {
        return callback(validator.getFirstError());
    }

    log().trace({
        'templateModule': templateModule,
        'templateId': templateId,
        'recipient': recipient,
        'data': data,
        'opts': opts
    }, 'Preparing template for mail to be sent.');

    var metaTemplate = _getTemplate(templateModule, templateId, 'meta.json');
    var htmlTemplate = _getTemplate(templateModule, templateId, 'html');
    var txtTemplate = _getTemplate(templateModule, templateId, 'txt');
    var sharedLogic = _getTemplate(templateModule, templateId, 'shared');

    // Verify the user templates have enough data to send an email
    if (!metaTemplate) {
        var noMetaTemplateErr = {'code': 500, 'msg': 'No email metadata template existed for user'};
        log().error({
            'err': new Error(noMetaTemplateErr.msg),
            'templateModule': templateModule,
            'templateId': templateId,
            'recipient': {
                'id': recipient.id,
                'locale': recipient.locale
            }
        }, noMetaTemplateErr.msg);
        return callback(noMetaTemplateErr);
    } else if (!htmlTemplate && !txtTemplate) {
        var noContentTemplateErr = {'code': 500, 'msg': 'No email content (text or html) template existed for user'};
        log().error({
            'err': new Error(noContentTemplateErr.msg),
            'templateModule': templateModule,
            'templateId': templateId,
            'recipient': {
                'id': recipient.id,
                'locale': recipient.locale
            }
        }, noContentTemplateErr.msg);
        return callback(noContentTemplateErr);
    }

    var renderedTemplates = {};
    var templateCtx = _.extend({}, data, {
        'recipient': recipient,
        'shared': sharedLogic,
        'instance': {
            'name': TenantsConfig.getValue(recipient.tenant.alias, 'instance', 'instanceName'),
            'URL': TenantsConfig.getValue(recipient.tenant.alias, 'instance', 'instanceURL')
        },
        'hostingOrganization': {
            'name': TenantsConfig.getValue(recipient.tenant.alias, 'instance', 'hostingOrganization'),
            'URL': TenantsConfig.getValue(recipient.tenant.alias, 'instance', 'hostingOrganizationURL')
        }
    });
    var metaContent = null;
    var htmlContent = null;
    var txtContent = null;

    var metaRendered = UIAPI.renderTemplate(metaTemplate, templateCtx, recipient.locale);

    try {
        // Try and parse the meta template into JSON
        metaContent = JSON.parse(metaRendered);
    } catch (metaErr) {
        log().error({
            'err': metaErr,
            'templateModule': templateModule,
            'templateId': templateId,
            'rendered': metaRendered,
            'recipient': {
                'id': recipient.id,
                'locale': recipient.locale
            }
        }, 'Error parsing email metadata template for recipient');
        return callback({'code': 500, 'msg': 'Error parsing email metadata template for recipient'});
    }

    // Try and render the html template
    if (htmlTemplate) {
        try {
            htmlContent = UIAPI.renderTemplate(htmlTemplate, templateCtx, recipient.locale);
        } catch (htmlErr) {
            log().warn({
                'err': htmlErr,
                'templateModule': templateModule,
                'templateId': templateId,
                'recipient': {
                    'id': recipient.id,
                    'email': recipient.email,
                    'locale': recipient.locale
                }
            }, 'Failed to parse email html template for recipient');
        }
    }

    // Try and render the text template
    if (txtTemplate) {
        try {
            txtContent = UIAPI.renderTemplate(txtTemplate, templateCtx, recipient.locale);
        } catch (txtErr) {
            log().warn({
                'err': txtErr,
                'templateModule': templateModule,
                'templateId': templateId,
                'recipient': {
                    'id': recipient.id,
                    'locale': recipient.locale
                }
            }, 'Failed to parse email html template for user');
        }
    }

    if (htmlContent || txtContent) {
        // If one of HTML or TXT templates managed to render, we will send the email with the content we have
        renderedTemplates['meta.json'] = metaContent;
        renderedTemplates['html'] = htmlContent;
        renderedTemplates['txt'] = txtContent;
    } else {
        return callback({'code': 500, 'msg': 'Could not parse a suitable content template for user'});
    }

    // If the `from` headers aren't set, we generate an intelligent `from` header based on the tenant host
    var tenant = TenantsAPI.getTenant(recipient.tenant.alias);
    var fromName = EmailConfig.getValue(tenant.alias, 'general', 'fromName') || tenant.displayName;
    fromName = fromName.replace('${tenant}', tenant.displayName);
    var fromAddr = EmailConfig.getValue(tenant.alias, 'general', 'fromAddress') || util.format('noreply@%s', tenant.host);
    var from = util.format('"%s" <%s>', fromName, fromAddr);

    // Build the email object that will be sent through nodemailer. The 'from' property can be overridden by
    // the meta.json, then we further override that with some hard values
    var emailInfo = _.extend({'from': from}, renderedTemplates['meta.json'], {
        'to': recipient.email
    });

    if (renderedTemplates['txt']) {
        emailInfo.text = renderedTemplates['txt'];
    }

    if (renderedTemplates['html']) {
        emailInfo.html = renderedTemplates['html'];
    }

    // Ensure the hash is set and is a valid hex string
    opts.hash = _generateMessageHash(emailInfo, opts);

    // Set the Message-Id header based on the message hash. We apply the
    // tenant host as the FQDN as it improves the spam score by providing
    // a source location of the message. We also add the userid of the user
    // we sent the message to, so we can determine what user a message was
    // sent to in Sendgrid
    emailInfo.messageId = util.format('%s.%s@%s', opts.hash, recipient.id.replace(/:/g, '-'), tenant.host);

    // If we're not sending out HTML, we can send out the email now
    if (!emailInfo.html) {
        return _sendEmail(emailInfo, opts, callback);
    }

    // If we're sending HTML, we should inline all the CSS
    _inlineCSS(emailInfo.html, function(err, inlinedHtml) {
        if (err) {
            log().error({'err': err, 'emailInfo': emailInfo}, 'Unable to inline CSS');
            return callback(err);
        }

        // Process the HTML such that we add line breaks before each html attribute to try and keep
        // line length below 998 characters
        emailInfo.html = _.chain(inlinedHtml.split('\n'))
            .map(function(line) {
                return line.replace(/<[^\/][^>]+>/g, function(match) {
                    return match.replace(/\s+[a-zA-Z0-9_-]+="[^"]+"/g, function(match) {
                        return util.format('\n%s', match);
                    });
                });
            })
            .value()
            .join('\n');

        return _sendEmail(emailInfo, opts, callback);
    });
};
Пример #2
0
var _renderTemplate = function(templateContent, data, locale) {
    return UIAPI.renderTemplate(templateContent, data, locale);
};