diff --git a/package.json b/package.json index 3b48246b4e..f6c5bff675 100644 --- a/package.json +++ b/package.json @@ -70,9 +70,7 @@ "nodebb-theme-slick": "1.1.1", "nodebb-theme-vanilla": "6.0.26", "nodebb-widget-essentials": "3.0.4", - "nodemailer": "2.6.4", - "nodemailer-sendmail-transport": "1.0.0", - "nodemailer-smtp-transport": "^2.4.1", + "nodemailer": "4.1.0", "passport": "^0.3.0", "passport-local": "1.0.0", "postcss": "6.0.10", diff --git a/public/language/en-GB/admin/settings/email.json b/public/language/en-GB/admin/settings/email.json index 1e92c88490..97696e1f44 100644 --- a/public/language/en-GB/admin/settings/email.json +++ b/public/language/en-GB/admin/settings/email.json @@ -4,13 +4,21 @@ "address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.", "from": "From Name", "from-help": "The from name to display in the email.", - "gmail-routing": "Gmail Routing", - "gmail-routing-help1": "There have been reports of Gmail Routing not working on accounts with heightened security. In those scenarios, you will have to configure your GMail account to allow less secure apps.", - "gmail-routing-help2": "For more information about this workaround, please consult this NodeMailer article on the issue. An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. Browse available plugins here.", - "gmail-transport": "Route emails through a Gmail/Google Apps account", - "gmail-transport.username": "Username", - "gmail-transport.username-help": "Enter the full email address here, especially if you are using a Google Apps managed domain.", - "gmail-transport.password": "Password", + + "smtp-transport": "SMTP Transport", + "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", + "smtp-transport.service": "Select a service", + "smtp-transport.service-custom": "Custom Service", + "smtp-transport.service-help": "Select a service name above in order to use the known information about it. Alternatively, select 'Custom Service' and enter the details below.", + "smtp-transport.gmail-warning1": "There have been reports of the Gmail service not working on accounts with heightened security. In those scenarios, you will have to configure your GMail account to allow less secure apps.", + "smtp-transport.gmail-warning2": "For more information about this workaround, please consult this NodeMailer article on the issue. An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. Browse available plugins here.", + "smtp-transport.host": "SMTP Host", + "smtp-transport.port": "SMTP Port", + "smtp-transport.username": "Username", + "smtp-transport.username-help": "For the Gmail service, enter the full email address here, especially if you are using a Google Apps managed domain.", + "smtp-transport.password": "Password", + "template": "Edit Email Template", "template.select": "Select Email Template", "template.revert": "Revert to Original", diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js index d6a1983102..336abf34fa 100644 --- a/public/src/admin/settings/email.js +++ b/public/src/admin/settings/email.js @@ -9,11 +9,13 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { configureEmailTester(); configureEmailEditor(); handleDigestHourChange(); + handleSmtpServiceChange(); $(window).on('action:admin.settingsLoaded action:admin.settingsSaved', handleDigestHourChange); $(window).on('action:admin.settingsSaved', function () { socket.emit('admin.user.restartJobs'); }); + $('[id="email:smtpTransport:service"]').change(handleSmtpServiceChange); }; function configureEmailTester() { @@ -100,5 +102,10 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { }); } + function handleSmtpServiceChange() { + var isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp'; + $('[id="email:smtpTransport:custom-service"]')[isCustom ? 'slideDown' : 'slideUp'](isCustom); + } + return module; }); diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 97d9b17262..0410d4ea9e 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -1,9 +1,13 @@ 'use strict'; - var async = require('async'); var nconf = require('nconf'); +var fs = require('fs'); +var path = require('path'); + var meta = require('../../meta'); +var file = require('../../file'); +var emailer = require('../../emailer'); var settingsController = module.exports; @@ -22,44 +26,54 @@ settingsController.get = function (req, res, next) { function renderEmail(req, res, next) { - var fs = require('fs'); - var path = require('path'); - var file = require('../../file'); - var emailsPath = path.join(nconf.get('views_dir'), 'emails'); - async.waterfall([ - function (next) { - file.walk(emailsPath, next); - }, - function (emails, next) { - async.map(emails, function (email, next) { - var path = email.replace(emailsPath, '').substr(1).replace('.tpl', ''); + async.parallel({ + emails: function (cb) { + async.waterfall([ + function (next) { + file.walk(emailsPath, next); + }, + function (emails, next) { + // exclude .jst files + emails = emails.filter(function (email) { + return !email.endsWith('.jst'); + }); - async.waterfall([ - function (next) { - fs.readFile(email, next); - }, - function (original, next) { - var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString(); + async.map(emails, function (email, next) { + var path = email.replace(emailsPath, '').substr(1).replace('.tpl', ''); - next(null, { - path: path, - fullpath: email, - text: text, - original: original.toString(), - }); - }, - ], next); - }, next); - }, - function (emails) { - res.render('admin/settings/email', { - emails: emails, - sendable: emails.filter(function (email) { - return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1; - }), - }); + async.waterfall([ + function (next) { + fs.readFile(email, next); + }, + function (original, next) { + var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString(); + + next(null, { + path: path, + fullpath: email, + text: text, + original: original.toString(), + }); + }, + ], next); + }, next); + }, + ], cb); }, - ], next); + services: emailer.listServices, + }, function (err, results) { + if (err) { + return next(err); + } + + res.render('admin/settings/email', { + emails: results.emails, + sendable: results.emails.filter(function (email) { + return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1; + }), + services: results.services, + }); + }); } diff --git a/src/emailer.js b/src/emailer.js index bb9bd3d64d..d82228d712 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -5,8 +5,7 @@ var winston = require('winston'); var nconf = require('nconf'); var Benchpress = require('benchpressjs'); var nodemailer = require('nodemailer'); -var sendmailTransport = require('nodemailer-sendmail-transport'); -var smtpTransport = require('nodemailer-smtp-transport'); +var wellKnownServices = require('nodemailer/lib/well-known/services'); var htmlToText = require('html-to-text'); var url = require('url'); @@ -17,8 +16,12 @@ var translator = require('./translator'); var pubsub = require('./pubsub'); var transports = { - sendmail: nodemailer.createTransport(sendmailTransport()), - gmail: undefined, + sendmail: nodemailer.createTransport({ + sendmail: true, + newline: 'unix', + }), + smtp: undefined, + // gmail: undefined, }; var app; @@ -26,6 +29,11 @@ var fallbackTransport; var Emailer = module.exports; +Emailer.listServices = function (callback) { + var services = Object.keys(wellKnownServices); + setImmediate(callback, null, services); +}; + Emailer._defaultPayload = {}; Emailer.registerApp = function (expressApp) { @@ -47,17 +55,24 @@ Emailer.registerApp = function (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, + if (parseInt(meta.config['email:smtpTransport:enabled'], 10) === 1) { + var smtpOptions = { auth: { - user: meta.config['email:GmailTransport:user'], - pass: meta.config['email:GmailTransport:pass'], + user: meta.config['email:smtpTransport:user'], + pass: meta.config['email:smtpTransport:pass'], }, - })); - fallbackTransport = transports.gmail; + }; + + if (meta.config['email:smtpTransport:serice'] === 'nodebb-custom-smtp') { + smtpOptions.port = meta.config['email:smtpTransport:port']; + smtpOptions.host = meta.config['email:smtpTransport:host']; + smtpOptions.secure = true; + } else { + smtpOptions.service = meta.config['email:smtpTransport:service']; + } + + transports.smtp = nodemailer.createTransport(smtpOptions); + fallbackTransport = transports.smtp; } else { fallbackTransport = transports.sendmail; } diff --git a/src/views/admin/settings/email.tpl b/src/views/admin/settings/email.tpl index 36e84a0f23..50a3423ece 100644 --- a/src/views/admin/settings/email.tpl +++ b/src/views/admin/settings/email.tpl @@ -7,7 +7,7 @@

- + [[admin/settings/email:address-help]]


@@ -23,33 +23,57 @@
-
[[admin/settings/email:gmail-routing]]
+
[[admin/settings/email:smtp-transport]]

- [[admin/settings/email:gmail-routing-help1]] -

-

- [[admin/settings/email:gmail-routing-help2]] + [[admin/settings/email:smtp-transport-help]]

-
- - + + +

+ [[admin/settings/email:smtp-transport.service-help]] +
+ [[admin/settings/email:smtp-transport.gmail-warning1]] +
+ [[admin/settings/email:smtp-transport.gmail-warning2]] +

+
+ +
+ +

- [[admin/settings/email:gmail-transport.username-help]] + [[admin/settings/email:smtp-transport.username-help]]

- - + +