feat(location): add location data to emails (#180) r=vladikoff,shane-tomlinson
* feat(location): add location data to emails * fix(templates): Ditch the style elements, move styles to elements themselves. Also - update the write-to-disk script to add location information. Also - add if conditionals around the ip and timestamp
This commit is contained in:
Родитель
f91a3279e2
Коммит
5c9f6718fd
51
mailer.js
51
mailer.js
|
@ -5,6 +5,10 @@
|
|||
var qs = require('querystring')
|
||||
var P = require('bluebird')
|
||||
var nodemailer = require('nodemailer')
|
||||
var moment = require('moment-timezone')
|
||||
|
||||
var DEFAULT_LOCALE = 'en'
|
||||
var DEFAULT_TIMEZONE = 'Etc/UTC'
|
||||
|
||||
module.exports = function (log) {
|
||||
function extend(target, source) {
|
||||
|
@ -26,6 +30,19 @@ module.exports = function (log) {
|
|||
return 'href="' + url + '" style="color: #0095dd; text-decoration: none; font-family: sans-serif;"'
|
||||
}
|
||||
|
||||
function constructLocalTimeString (timeZone, locale) {
|
||||
// if no timeZone is passed, use DEFAULT_TIMEZONE
|
||||
moment.tz.setDefault(DEFAULT_TIMEZONE)
|
||||
// if no locale is passed, use DEFAULT_LOCALE
|
||||
locale = locale || DEFAULT_LOCALE
|
||||
moment.locale(locale)
|
||||
var time = moment()
|
||||
if (timeZone) {
|
||||
time = time.tz(timeZone)
|
||||
}
|
||||
// return a locale-specific time
|
||||
return time.format('LTS (z) dddd, ll')
|
||||
}
|
||||
|
||||
function Mailer(translator, templates, config, sender) {
|
||||
var options = {
|
||||
|
@ -95,6 +112,25 @@ module.exports = function (log) {
|
|||
return parts.join(', ')
|
||||
}
|
||||
|
||||
Mailer.prototype._constructLocationString = function (message) {
|
||||
var translator = this.translator(message.acceptLanguage)
|
||||
var location = message.location
|
||||
// construct the location string from the location object
|
||||
if (location) {
|
||||
if (location.city) {
|
||||
return translator.format(translator.gettext('%(city)s, %(country)s (estimated)'), location)
|
||||
} else {
|
||||
return translator.format(translator.gettext('%(country)s (estimated)'), location)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
Mailer.prototype._constructLocalTimeString = function (timeZone, acceptLanguage) {
|
||||
var translator = this.translator(acceptLanguage)
|
||||
return constructLocalTimeString(timeZone, translator.language)
|
||||
}
|
||||
|
||||
Mailer.prototype.localize = function (message) {
|
||||
var translator = this.translator(message.acceptLanguage)
|
||||
|
||||
|
@ -218,12 +254,15 @@ module.exports = function (log) {
|
|||
templateValues: {
|
||||
device: this._formatUserAgentInfo(message),
|
||||
email: message.email,
|
||||
ip: message.ip,
|
||||
link: link,
|
||||
location: this._constructLocationString(message),
|
||||
oneClickLink: oneClickLink,
|
||||
passwordChangeLink: this.createPasswordChangeLink(message.email),
|
||||
passwordChangeLinkAttributes: this._passwordChangeLinkAttributes(message.email),
|
||||
supportLinkAttributes: linkAttributes(this.supportUrl),
|
||||
supportUrl: this.supportUrl
|
||||
supportUrl: this.supportUrl,
|
||||
timestamp: this._constructLocalTimeString(message.timeZone, message.acceptLanguage)
|
||||
},
|
||||
uid: message.uid
|
||||
})
|
||||
|
@ -326,12 +365,6 @@ module.exports = function (log) {
|
|||
log.trace({ op: 'mailer.newDeviceLoginEmail', email: message.email, uid: message.uid })
|
||||
var link = this.createPasswordChangeLink(message.email)
|
||||
|
||||
// Make a human-readable timestamp string.
|
||||
// For now it's always in UTC.
|
||||
// Future iterations can localize this better.
|
||||
var timestamp = new Date(message.timestamp || Date.now())
|
||||
var timestampStr = timestamp.toISOString().substr(0, 16).replace('T', ' ') + ' UTC'
|
||||
|
||||
return this.send({
|
||||
acceptLanguage: message.acceptLanguage,
|
||||
email: message.email,
|
||||
|
@ -342,11 +375,13 @@ module.exports = function (log) {
|
|||
template: 'newDeviceLoginEmail',
|
||||
templateValues: {
|
||||
device: this._formatUserAgentInfo(message),
|
||||
ip: message.ip,
|
||||
location: this._constructLocationString(message),
|
||||
passwordChangeLinkAttributes: this._passwordChangeLinkAttributes(message.email),
|
||||
resetLink: link,
|
||||
supportLinkAttributes: this._supportLinkAttributes(),
|
||||
supportUrl: this.supportUrl,
|
||||
timestamp: timestampStr
|
||||
timestamp: this._constructLocalTimeString(message.timeZone, message.acceptLanguage)
|
||||
},
|
||||
uid: message.uid
|
||||
})
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -25,6 +25,7 @@
|
|||
"handlebars": "1.3.0",
|
||||
"i18n-abide": "0.0.23",
|
||||
"jed": "0.5.4",
|
||||
"moment-timezone": "0.5.4",
|
||||
"mozlog": "2.0.5",
|
||||
"nodemailer": "0.7.1",
|
||||
"po2json": "0.4.1",
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<% extends "partials/base/base_text_only.html" %>
|
||||
|
||||
<% block heading %>{{t "New sign-in to Firefox"}}<% endblock %>
|
||||
<% block text_primary %>{{{ device }}}<br/>{{ timestamp }}<% endblock %>
|
||||
<% block text_primary %>{{{ device }}}<br/>
|
||||
{{#if ip }}<span style="display:block;margin:2px 0;">{{t "IP address: %(ip)s" }}</span>{{/if}}
|
||||
{{#if location }}<span style="display:block;margin:2px 0;">{{ location }}</span>{{/if}}
|
||||
{{#if timestamp }}<span style="display:block;margin:2px 0;">{{ timestamp }}</span>{{/if}}
|
||||
<% endblock %>
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
<tr style="page-break-before: always">
|
||||
<td valign="top">
|
||||
<h1 style="font-family: sans-serif; font-weight: normal; margin: 0 0 24px 0; text-align: center;"><% block verify_title %>{{t "New sign-in to Firefox"}}<% endblock %></h1>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 24px 0; text-align: center;"><% block verify_content %>{{t "For added security, please confirm this sign-in to begin syncing with %(device)s." }}<% endblock %></p>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 24px 0; text-align: center;"><% block verify_content %>{{t "For added security, please confirm this sign-in to begin syncing with this device:"}}<br/><br/>
|
||||
{{{ device }}}
|
||||
{{#if ip }}<span style="display:block;margin:2px 0;">{{t "IP address: %(ip)s" }}</span>{{/if}}
|
||||
{{#if location }}<span style="display:block;margin:2px 0;">{{ location }}</span>{{/if}}
|
||||
{{#if timestamp }}<span style="display:block;margin:2px 0;">{{ timestamp }}</span>{{/if}}
|
||||
<% endblock %></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* postVerifyEmail
|
||||
* recoveryEmail
|
||||
* suspiciousLocationEmail
|
||||
* unlockEmail
|
||||
* verificationReminderEmail:first
|
||||
* verificationReminderEmail:second
|
||||
* verifyEmail
|
||||
|
@ -82,14 +81,20 @@ function sendMail(mailer, messageToSend) {
|
|||
var messageSubType = parts[1]
|
||||
|
||||
var message = {
|
||||
acceptLanguage: 'en',
|
||||
acceptLanguage: 'en;q=0.8,en-US;q=0.5,en;q=0.3"',
|
||||
code: 'ae35999f861ffc81d594034eb4560af8',
|
||||
email: 'testuser@testuser.com',
|
||||
ip: '10.246.67.38',
|
||||
location: {
|
||||
city: 'Madrid',
|
||||
country: 'Spain'
|
||||
},
|
||||
locations: [],
|
||||
redirectTo: 'https://redirect.com/',
|
||||
resume: 'eyJjYW1wYWlnbiI6bnVsbCwiZW50cnlwb2ludCI6bnVsbCwiZmxvd0lkIjoiM2Q1ODZiNzY4Mzc2NGJhOWFiNzhkMzMxMTdjZDU4Y2RmYjk3Mzk5MWU5NTk0NjgxODBlMDUyMmY2MThhNmEyMSIsInJlc2V0UGFzc3dvcmRDb25maXJtIjp0cnVlLCJ1bmlxdWVVc2VySWQiOiI1ODNkOGFlYS00NzU3LTRiZTQtYWJlNC0wZWQ2NWZhY2Y2YWQiLCJ1dG1DYW1wYWlnbiI6bnVsbCwidXRtQ29udGVudCI6bnVsbCwidXRtTWVkaXVtIjpudWxsLCJ1dG1Tb3VyY2UiOm51bGwsInV0bVRlcm0iOm51bGx9',
|
||||
service: 'sync',
|
||||
token: '47b22cd271963448cf36da95cccfcfb342b5693d66f58aa635f9a95579431002',
|
||||
timeZone: 'Europe/Madrid',
|
||||
type: messageSubType,
|
||||
uaBrowser: 'Firefox',
|
||||
uaBrowserVersion: '47',
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
<tr style="page-break-before: always">
|
||||
<td valign="top">
|
||||
<h1 style="font-family: sans-serif; font-weight: normal; margin: 0 0 24px 0; text-align: center;">{{t "New sign-in to Firefox"}}</h1>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 12px 0; text-align: center;">{{{ device }}}<br/>{{ timestamp }}</p>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 12px 0; text-align: center;">{{{ device }}}<br/>
|
||||
{{#if ip }}<span style="display:block;margin:2px 0;">{{t "IP address: %(ip)s" }}</span>{{/if}}
|
||||
{{#if location }}<span style="display:block;margin:2px 0;">{{ location }}</span>{{/if}}
|
||||
{{#if timestamp }}<span style="display:block;margin:2px 0;">{{ timestamp }}</span>{{/if}}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
{{t "New sign-in to Firefox" }}
|
||||
|
||||
{{ device }}
|
||||
{{ timestamp }}
|
||||
{{#if ip}}{{t "IP address: %(ip)s" }}{{/if}}
|
||||
{{#if location}}{{ location }}{{/if}}
|
||||
{{#if timestamp}}{{ timestamp }}{{/if}}
|
||||
|
||||
{{t "This is an automated email; if you didn't add a new device to your Firefox Account, you should change your password immediately at %(resetLink)s."}}
|
||||
{{t "For more information, please visit %(supportUrl)s"}}
|
||||
|
|
|
@ -20,7 +20,12 @@
|
|||
<tr style="page-break-before: always">
|
||||
<td valign="top">
|
||||
<h1 style="font-family: sans-serif; font-weight: normal; margin: 0 0 24px 0; text-align: center;">{{t "New sign-in to Firefox"}}</h1>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 24px 0; text-align: center;">{{t "For added security, please confirm this sign-in to begin syncing with %(device)s." }}</p>
|
||||
<p class="primary" style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 24px 0; text-align: center;">{{t "For added security, please confirm this sign-in to begin syncing with this device:"}}<br/><br/>
|
||||
{{{ device }}}
|
||||
{{#if ip }}<span style="display:block;margin:2px 0;">{{t "IP address: %(ip)s" }}</span>{{/if}}
|
||||
{{#if location }}<span style="display:block;margin:2px 0;">{{ location }}</span>{{/if}}
|
||||
{{#if timestamp }}<span style="display:block;margin:2px 0;">{{ timestamp }}</span>{{/if}}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
{{t "New sign-in to Firefox"}}
|
||||
{{t "For added security, please confirm this sign-in to begin syncing with %(device)s." }}
|
||||
{{t "For added security, please confirm this sign-in to begin syncing with this device:" }}
|
||||
{{ device }}
|
||||
{{#if ip}}{{t "IP address: %(ip)s" }}{{/if}}
|
||||
{{#if location}}{{ location }}{{/if}}
|
||||
{{#if timestamp}}{{ timestamp }}{{/if}}
|
||||
{{t "Confirm sign-in"}} {{{link}}}
|
||||
|
||||
{{t "If you suspect that someone is trying to gain access to your account, please change your password." }} {{{ passwordChangeLink }}}
|
||||
|
|
|
@ -62,6 +62,11 @@ var typesContainIOSStoreLinks = [
|
|||
'postVerifyEmail'
|
||||
]
|
||||
|
||||
var typesContainLocationData = [
|
||||
'newDeviceLoginEmail',
|
||||
'verifyLoginEmail'
|
||||
]
|
||||
|
||||
function includes(haystack, needle) {
|
||||
return (haystack.indexOf(needle) > -1)
|
||||
}
|
||||
|
@ -182,6 +187,36 @@ P.all(
|
|||
)
|
||||
}
|
||||
|
||||
if (includes(typesContainLocationData, type)) {
|
||||
var location = {
|
||||
city: 'Mountain View',
|
||||
country: 'USA'
|
||||
}
|
||||
|
||||
message = {
|
||||
device: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0',
|
||||
email: 'a@b.com',
|
||||
ip: '219.129.234.194',
|
||||
location: location,
|
||||
timeZone: 'America/Los_Angeles'
|
||||
}
|
||||
|
||||
test(
|
||||
'Location and ip data is present in email template output for ' + type,
|
||||
function (t) {
|
||||
mailer.mailer.sendMail = function (emailConfig) {
|
||||
t.ok(includes(emailConfig.html, location.city + ', ' + location.country))
|
||||
t.ok(includes(emailConfig.html, message.ip))
|
||||
|
||||
t.ok(includes(emailConfig.text, location.city + ', ' + location.country))
|
||||
t.ok(includes(emailConfig.text, message.ip))
|
||||
t.end()
|
||||
}
|
||||
mailer[type](message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (type === 'verifyLoginEmail') {
|
||||
test(
|
||||
'test verify token email',
|
||||
|
|
Загрузка…
Ссылка в новой задаче