diff --git a/src/emailer.js b/src/emailer.js index bad8f22650..38d68102c1 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -23,146 +23,146 @@ var transports = { var app; var fallbackTransport; -(function (Emailer) { - Emailer.registerApp = function (expressApp) { - app = expressApp; - - // Enable Gmail transport if enabled in ACP - if (parseInt(meta.config['email:GmailTransport:enabled'], 10) === 1) { - transports.gmail = nodemailer.createTransport(smtpTransport({ - host: 'smtp.gmail.com', - port: 465, - secure: true, - auth: { - user: meta.config['email:GmailTransport:user'], - pass: meta.config['email:GmailTransport:pass'], - }, - })); - fallbackTransport = transports.gmail; - } else { - fallbackTransport = transports.sendmail; - } +var Emailer = module.exports; - return Emailer; - }; - Emailer.send = function (template, uid, params, callback) { - callback = callback || function () {}; - if (!app) { - winston.warn('[emailer] App not ready!'); - return callback(); - } +Emailer.registerApp = function (expressApp) { + app = expressApp; - async.waterfall([ - function (next) { - async.parallel({ - email: async.apply(User.getUserField, uid, 'email'), - settings: async.apply(User.getSettings, uid), - }, next); - }, - function (results, next) { - if (!results.email) { - winston.warn('uid : ' + uid + ' has no email, not sending.'); - return next(); - } - params.uid = uid; - Emailer.sendToEmail(template, results.email, results.settings.userLang, params, next); - }, - ], callback); - }; - - Emailer.sendToEmail = function (template, email, language, params, callback) { - callback = callback || function () {}; - - var lang = language || meta.config.defaultLang || 'en-GB'; - - async.waterfall([ - function (next) { - async.parallel({ - html: function (next) { - renderAndTranslate('emails/' + template, params, lang, next); - }, - subject: function (next) { - translator.translate(params.subject, lang, function (translated) { - next(null, translated); - }); - }, - }, next); - }, - function (results, next) { - var data = { - _raw: params, - to: email, - from: meta.config['email:from'] || 'no-reply@' + getHostname(), - from_name: meta.config['email:from_name'] || 'NodeBB', - subject: results.subject, - html: results.html, - plaintext: htmlToText.fromString(results.html, { - ignoreImage: true, - }), - template: template, - uid: params.uid, - pid: params.pid, - fromUid: params.fromUid, - }; - Plugins.fireHook('filter:email.modify', data, next); - }, - function (data, next) { - if (Plugins.hasListeners('filter:email.send')) { - Plugins.fireHook('filter:email.send', data, next); - } else { - Emailer.sendViaFallback(data, next); - } + // Enable Gmail transport if enabled in ACP + if (parseInt(meta.config['email:GmailTransport:enabled'], 10) === 1) { + transports.gmail = nodemailer.createTransport(smtpTransport({ + host: 'smtp.gmail.com', + port: 465, + secure: true, + auth: { + user: meta.config['email:GmailTransport:user'], + pass: meta.config['email:GmailTransport:pass'], }, - ], function (err) { - if (err && err.code === 'ENOENT') { - callback(new Error('[[error:sendmail-not-found]]')); - } else { - callback(err); - } - }); - }; + })); + fallbackTransport = transports.gmail; + } else { + fallbackTransport = transports.sendmail; + } - Emailer.sendViaFallback = function (data, callback) { - // Some minor alterations to the data to conform to nodemailer standard - data.text = data.plaintext; - delete data.plaintext; + return Emailer; +}; - // NodeMailer uses a combined "from" - data.from = data.from_name + '<' + data.from + '>'; - delete data.from_name; +Emailer.send = function (template, uid, params, callback) { + callback = callback || function () {}; + if (!app) { + winston.warn('[emailer] App not ready!'); + return callback(); + } - winston.verbose('[emailer] Sending email to uid ' + data.uid + ' (' + data.to + ')'); - fallbackTransport.sendMail(data, function (err) { - if (err) { - winston.error(err); + async.waterfall([ + function (next) { + async.parallel({ + email: async.apply(User.getUserField, uid, 'email'), + settings: async.apply(User.getSettings, uid), + }, next); + }, + function (results, next) { + if (!results.email) { + winston.warn('uid : ' + uid + ' has no email, not sending.'); + return next(); } - callback(); - }); - }; + params.uid = uid; + Emailer.sendToEmail(template, results.email, results.settings.userLang, params, next); + }, + ], callback); +}; - function render(tpl, params, next) { - if (meta.config['email:custom:' + tpl.replace('emails/', '')]) { - var text = templates.parse(meta.config['email:custom:' + tpl.replace('emails/', '')], params); - next(null, text); +Emailer.sendToEmail = function (template, email, language, params, callback) { + callback = callback || function () {}; + + var lang = language || meta.config.defaultLang || 'en-GB'; + + async.waterfall([ + function (next) { + async.parallel({ + html: function (next) { + renderAndTranslate('emails/' + template, params, lang, next); + }, + subject: function (next) { + translator.translate(params.subject, lang, function (translated) { + next(null, translated); + }); + }, + }, next); + }, + function (results, next) { + var data = { + _raw: params, + to: email, + from: meta.config['email:from'] || 'no-reply@' + getHostname(), + from_name: meta.config['email:from_name'] || 'NodeBB', + subject: results.subject, + html: results.html, + plaintext: htmlToText.fromString(results.html, { + ignoreImage: true, + }), + template: template, + uid: params.uid, + pid: params.pid, + fromUid: params.fromUid, + }; + Plugins.fireHook('filter:email.modify', data, next); + }, + function (data, next) { + if (Plugins.hasListeners('filter:email.send')) { + Plugins.fireHook('filter:email.send', data, next); + } else { + Emailer.sendViaFallback(data, next); + } + }, + ], function (err) { + if (err && err.code === 'ENOENT') { + callback(new Error('[[error:sendmail-not-found]]')); } else { - app.render(tpl, params, next); + callback(err); } - } + }); +}; - function renderAndTranslate(tpl, params, lang, callback) { - render(tpl, params, function (err, html) { - translator.translate(html, lang, function (translated) { - callback(err, translated); - }); - }); - } +Emailer.sendViaFallback = function (data, callback) { + // Some minor alterations to the data to conform to nodemailer standard + data.text = data.plaintext; + delete data.plaintext; - function getHostname() { - var configUrl = nconf.get('url'); - var parsed = url.parse(configUrl); + // NodeMailer uses a combined "from" + data.from = data.from_name + '<' + data.from + '>'; + delete data.from_name; + + winston.verbose('[emailer] Sending email to uid ' + data.uid + ' (' + data.to + ')'); + fallbackTransport.sendMail(data, function (err) { + if (err) { + winston.error(err); + } + callback(); + }); +}; - return parsed.hostname; +function render(tpl, params, next) { + if (meta.config['email:custom:' + tpl.replace('emails/', '')]) { + var text = templates.parse(meta.config['email:custom:' + tpl.replace('emails/', '')], params); + next(null, text); + } else { + app.render(tpl, params, next); } -}(module.exports)); +} + +function renderAndTranslate(tpl, params, lang, callback) { + render(tpl, params, function (err, html) { + translator.translate(html, lang, function (translated) { + callback(err, translated); + }); + }); +} + +function getHostname() { + var configUrl = nconf.get('url'); + var parsed = url.parse(configUrl); + return parsed.hostname; +} diff --git a/src/events.js b/src/events.js index c7a8b26638..48307b868d 100644 --- a/src/events.js +++ b/src/events.js @@ -9,94 +9,95 @@ var batch = require('./batch'); var user = require('./user'); var utils = require('./utils'); -(function (events) { - events.log = function (data, callback) { - callback = callback || function () {}; - - async.waterfall([ - function (next) { - db.incrObjectField('global', 'nextEid', next); - }, - function (eid, next) { - data.timestamp = Date.now(); - data.eid = eid; - - async.parallel([ - function (next) { - db.sortedSetAdd('events:time', data.timestamp, eid, next); - }, - function (next) { - db.setObject('event:' + eid, data, next); - }, - ], next); - }, - ], function (err) { - callback(err); - }); - }; - - events.getEvents = function (start, stop, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRange('events:time', start, stop, next); - }, - function (eids, next) { - var keys = eids.map(function (eid) { - return 'event:' + eid; - }); - db.getObjects(keys, next); - }, - function (eventsData, next) { - eventsData = eventsData.filter(Boolean); - addUserData(eventsData, 'uid', 'user', next); - }, - function (eventsData, next) { - addUserData(eventsData, 'targetUid', 'targetUser', next); - }, - function (eventsData, next) { - eventsData.forEach(function (event) { - Object.keys(event).forEach(function (key) { - if (typeof event[key] === 'string') { - event[key] = validator.escape(String(event[key] || '')); - } - }); - var e = utils.merge(event); - e.eid = undefined; - e.uid = undefined; - e.type = undefined; - e.ip = undefined; - e.user = undefined; - event.jsonString = JSON.stringify(e, null, 4); - event.timestampISO = new Date(parseInt(event.timestamp, 10)).toUTCString(); +var events = module.exports; + +events.log = function (data, callback) { + callback = callback || function () {}; + + async.waterfall([ + function (next) { + db.incrObjectField('global', 'nextEid', next); + }, + function (eid, next) { + data.timestamp = Date.now(); + data.eid = eid; + + async.parallel([ + function (next) { + db.sortedSetAdd('events:time', data.timestamp, eid, next); + }, + function (next) { + db.setObject('event:' + eid, data, next); + }, + ], next); + }, + ], function (err) { + callback(err); + }); +}; + +events.getEvents = function (start, stop, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange('events:time', start, stop, next); + }, + function (eids, next) { + var keys = eids.map(function (eid) { + return 'event:' + eid; + }); + db.getObjects(keys, next); + }, + function (eventsData, next) { + eventsData = eventsData.filter(Boolean); + addUserData(eventsData, 'uid', 'user', next); + }, + function (eventsData, next) { + addUserData(eventsData, 'targetUid', 'targetUser', next); + }, + function (eventsData, next) { + eventsData.forEach(function (event) { + Object.keys(event).forEach(function (key) { + if (typeof event[key] === 'string') { + event[key] = validator.escape(String(event[key] || '')); + } }); - next(null, eventsData); - }, - ], callback); - }; - - function addUserData(eventsData, field, objectName, callback) { - var uids = eventsData.map(function (event) { - return event && event[field]; - }).filter(function (uid, index, array) { - return uid && array.indexOf(uid) === index; - }); - - if (!uids.length) { - return callback(null, eventsData); - } - - async.parallel({ - isAdmin: function (next) { - user.isAdministrator(uids, next); - }, - userData: function (next) { - user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); - }, - }, function (err, results) { - if (err) { - return callback(err); - } + var e = utils.merge(event); + e.eid = undefined; + e.uid = undefined; + e.type = undefined; + e.ip = undefined; + e.user = undefined; + event.jsonString = JSON.stringify(e, null, 4); + event.timestampISO = new Date(parseInt(event.timestamp, 10)).toUTCString(); + }); + next(null, eventsData); + }, + ], callback); +}; + +function addUserData(eventsData, field, objectName, callback) { + var uids = eventsData.map(function (event) { + return event && event[field]; + }).filter(function (uid, index, array) { + return uid && array.indexOf(uid) === index; + }); + + if (!uids.length) { + return callback(null, eventsData); + } + async.waterfall([ + function (next) { + async.parallel({ + isAdmin: function (next) { + user.isAdministrator(uids, next); + }, + userData: function (next) { + user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); + }, + }, next); + }, + function (results, next) { var userData = results.userData; var map = {}; @@ -110,30 +111,30 @@ var utils = require('./utils'); event[objectName] = map[event[field]]; } }); - callback(null, eventsData); - }); - } - - events.deleteEvents = function (eids, callback) { - callback = callback || function () {}; - async.parallel([ - function (next) { - var keys = eids.map(function (eid) { - return 'event:' + eid; - }); - db.deleteAll(keys, next); - }, - function (next) { - db.sortedSetRemove('events:time', eids, next); - }, - ], callback); - }; - - events.deleteAll = function (callback) { - callback = callback || function () {}; - - batch.processSortedSet('events:time', function (eids, next) { - events.deleteEvents(eids, next); - }, { alwaysStartAt: 0 }, callback); - }; -}(module.exports)); + next(null, eventsData); + }, + ], callback); +} + +events.deleteEvents = function (eids, callback) { + callback = callback || function () {}; + async.parallel([ + function (next) { + var keys = eids.map(function (eid) { + return 'event:' + eid; + }); + db.deleteAll(keys, next); + }, + function (next) { + db.sortedSetRemove('events:time', eids, next); + }, + ], callback); +}; + +events.deleteAll = function (callback) { + callback = callback || function () {}; + + batch.processSortedSet('events:time', function (eids, next) { + events.deleteEvents(eids, next); + }, { alwaysStartAt: 0 }, callback); +};