diff --git a/public/language/en-GB/admin/settings/email.json b/public/language/en-GB/admin/settings/email.json index 0310939cb3..c7a3628a7f 100644 --- a/public/language/en-GB/admin/settings/email.json +++ b/public/language/en-GB/admin/settings/email.json @@ -30,14 +30,20 @@ "smtp-transport.pool-help": "Pooling connections prevents NodeBB from creating a new connection for every email. This option only applies if SMTP Transport is enabled.", "smtp-transport.allow-self-signed": "Allow self-signed certificates", "smtp-transport.allow-self-signed-help": "Enabling this setting will allow you to use self-signed or invalid TLS certificates.", + "smtp-transport.test-success": "SMTP Test email sent successfully.", "template": "Edit Email Template", "template.select": "Select Email Template", "template.revert": "Revert to Original", + "test-smtp-settings": "Test SMTP Settings", "testing": "Email Testing", + "testing.success": "Test Email Sent.", "testing.select": "Select Email Template", "testing.send": "Send Test Email", - "testing.send-help": "The test email will be sent to the currently logged in user's email address.", + "testing.send-help-plugin": "\"%1\" will be used to send test emails.", + "testing.send-help-smtp": "SMTP transport is enabled and will be used to send emails.", + "testing.send-help-no-plugin": "No emailer plugin is installed to send emails, nodemailer will be used by default.", + "testing.send-help": "The test email will be sent to the currently logged in user's email address using the saved settings on this page. ", "subscriptions": "Email Digests", "subscriptions.disable": "Disable email digests", "subscriptions.hour": "Digest Hour", diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js index d514f2a482..83276844f4 100644 --- a/public/src/admin/settings/email.js +++ b/public/src/admin/settings/email.js @@ -6,6 +6,7 @@ define('admin/settings/email', ['ace/ace', 'alerts', 'admin/settings'], function let emailEditor; module.init = function () { + configureSmtpTester(); configureEmailTester(); configureEmailEditor(); handleDigestHourChange(); @@ -26,6 +27,29 @@ define('admin/settings/email', ['ace/ace', 'alerts', 'admin/settings'], function socket.emit('admin.user.restartJobs'); } + function configureSmtpTester() { + $('[data-action="email.smtp.test"]').on('click', function () { + const smtpOptions = {}; + $('[data-field^="email:smtp"]').each(function (index, el) { + const $el = $(el); + const key = $el.attr('data-field'); + if ($el.is(':checkbox')) { + smtpOptions[key] = $el.is(':checked'); + } else { + smtpOptions[key] = $el.val(); + } + }); + + socket.emit('admin.email.testSmtp', { smtp: smtpOptions }, function (err) { + if (err) { + console.error(err.message); + return alerts.error(err); + } + alerts.success('[[admin/settings/email:smtp-transport.test-success]]'); + }); + }); + } + function configureEmailTester() { $('button[data-action="email.test"]').off('click').on('click', function () { socket.emit('admin.email.test', { template: $('#test-email').val() }, function (err) { @@ -33,7 +57,7 @@ define('admin/settings/email', ['ace/ace', 'alerts', 'admin/settings'], function console.error(err.message); return alerts.error(err); } - alerts.success('Test Email Sent'); + alerts.success('[[admin/settings/email:testing.success]]'); }); return false; }); diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 184c6c0ed6..f27ecbae67 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -14,6 +14,7 @@ const api = require('../../api'); const pagination = require('../../pagination'); const helpers = require('../helpers'); const translator = require('../../translator'); +const plugins = require('../../plugins'); const settingsController = module.exports; @@ -114,9 +115,14 @@ settingsController.uploads = async (req, res) => { settingsController.email = async (req, res) => { const emails = await emailer.getTemplates(meta.config); + const hooks = plugins.loadedHooks['static:email.send']; + const emailerPlugin = hooks && hooks.length ? hooks[0].id : null; + const smtpEnabled = parseInt(meta.config['email:smtpTransport:enabled'], 10) === 1; res.render('admin/settings/email', { title: '[[admin/menu:settings/email]]', + emailerPlugin, + smtpEnabled, emails: emails, sendable: emails.filter(e => !e.path.includes('_plaintext') && !e.path.includes('partials')).map(tpl => tpl.path), services: emailer.listServices(), diff --git a/src/emailer.js b/src/emailer.js index ad7e762916..38d8867fa2 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -56,8 +56,7 @@ const smtpSettingsChanged = (config) => { const getHostname = () => { const configUrl = nconf.get('url'); - const parsed = url.parse(configUrl); - return parsed.hostname; + return new URL(configUrl).hostname; }; const buildCustomTemplates = async (config) => { @@ -120,51 +119,55 @@ Emailer.setupFallbackTransport = (config) => { winston.verbose('[emailer] Setting up fallback transport'); // Enable SMTP transport if enabled in ACP if (parseInt(config['email:smtpTransport:enabled'], 10) === 1) { - const smtpOptions = { - name: getHostname(), - pool: config['email:smtpTransport:pool'], - }; - - if (config['email:smtpTransport:user'] || config['email:smtpTransport:pass']) { - smtpOptions.auth = { - user: config['email:smtpTransport:user'], - pass: config['email:smtpTransport:pass'], - }; - } - - if (config['email:smtpTransport:service'] === 'nodebb-custom-smtp') { - smtpOptions.port = config['email:smtpTransport:port']; - smtpOptions.host = config['email:smtpTransport:host']; - - if (config['email:smtpTransport:security'] === 'NONE') { - smtpOptions.secure = false; - smtpOptions.requireTLS = false; - smtpOptions.ignoreTLS = true; - } else if (config['email:smtpTransport:security'] === 'STARTTLS') { - smtpOptions.secure = false; - smtpOptions.requireTLS = true; - smtpOptions.ignoreTLS = false; - } else { - // meta.config['email:smtpTransport:security'] === 'ENCRYPTED' or undefined - smtpOptions.secure = true; - smtpOptions.requireTLS = true; - smtpOptions.ignoreTLS = false; - } - } else { - smtpOptions.service = String(config['email:smtpTransport:service']); - } - if (config['email:smtpTransport:allow-self-signed']) { - smtpOptions.tls = { - rejectUnauthorized: false, - }; - } - Emailer.transports.smtp = nodemailer.createTransport(smtpOptions); + Emailer.transports.smtp = Emailer.createSmtpTransport(config); Emailer.fallbackTransport = Emailer.transports.smtp; } else { Emailer.fallbackTransport = Emailer.transports.sendmail; } }; +Emailer.createSmtpTransport = (config) => { + const smtpOptions = { + name: getHostname(), + pool: config['email:smtpTransport:pool'], + }; + + if (config['email:smtpTransport:user'] || config['email:smtpTransport:pass']) { + smtpOptions.auth = { + user: config['email:smtpTransport:user'], + pass: config['email:smtpTransport:pass'], + }; + } + + if (config['email:smtpTransport:service'] === 'nodebb-custom-smtp') { + smtpOptions.port = config['email:smtpTransport:port']; + smtpOptions.host = config['email:smtpTransport:host']; + + if (config['email:smtpTransport:security'] === 'NONE') { + smtpOptions.secure = false; + smtpOptions.requireTLS = false; + smtpOptions.ignoreTLS = true; + } else if (config['email:smtpTransport:security'] === 'STARTTLS') { + smtpOptions.secure = false; + smtpOptions.requireTLS = true; + smtpOptions.ignoreTLS = false; + } else { + // meta.config['email:smtpTransport:security'] === 'ENCRYPTED' or undefined + smtpOptions.secure = true; + smtpOptions.requireTLS = true; + smtpOptions.ignoreTLS = false; + } + } else { + smtpOptions.service = String(config['email:smtpTransport:service']); + } + if (config['email:smtpTransport:allow-self-signed']) { + smtpOptions.tls = { + rejectUnauthorized: false, + }; + } + return nodemailer.createTransport(smtpOptions); +}; + Emailer.registerApp = (expressApp) => { app = expressApp; diff --git a/src/socket.io/admin/email.js b/src/socket.io/admin/email.js index b2a160b1f3..9059520b2b 100644 --- a/src/socket.io/admin/email.js +++ b/src/socket.io/admin/email.js @@ -1,5 +1,6 @@ 'use strict'; +const nconf = require('nconf'); const winston = require('winston'); const meta = require('../../meta'); @@ -8,6 +9,7 @@ const userEmail = require('../../user/email'); const notifications = require('../../notifications'); const emailer = require('../../emailer'); const utils = require('../../utils'); +const user = require('../../user'); const Email = module.exports; @@ -72,3 +74,25 @@ Email.test = async function (socket, data) { throw err; } }; + +Email.testSmtp = async (socket, data) => { + try { + const smtp = emailer.createSmtpTransport(data.smtp); + const content = 'This is a test email sent from NodeBB to verify your SMTP settings are correct.'; + const { hostname } = new URL(nconf.get('url')); + const toEmail = await user.getUserField(socket.uid, 'email'); + await smtp.sendMail({ + to: toEmail, + subject: `[${meta.config.title}] SMTP Settings Test Email`, + html: content, + text: content, + from: { + name: meta.config['email:from_name'] || 'NodeBB', + address: meta.config['email:from'] || `no-reply@${hostname}`, + }, + }); + } catch (err) { + winston.error(err.stack); + throw err; + } +}; \ No newline at end of file diff --git a/src/views/admin/settings/email.tpl b/src/views/admin/settings/email.tpl index d39d3b8f72..5630d2aad5 100644 --- a/src/views/admin/settings/email.tpl +++ b/src/views/admin/settings/email.tpl @@ -167,10 +167,13 @@ [[admin/settings/email:smtp-transport.username-help]]
-
- [[admin/settings/email:testing.send-help]]
+ [[admin/settings/email:testing.send-help]]
+ {{{ if emailerPlugin }}}
+ [[admin/settings/email:testing.send-help-plugin, {emailerPlugin}]]
+ {{{ else }}}
+ {{{ if smtpEnabled }}}
+ [[admin/settings/email:testing.send-help-smtp]]
+ {{{ else }}}
+ [[admin/settings/email:testing.send-help-no-plugin]]
+ {{{ end }}}
+ {{{ end }}}