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:
Sai Prashanth Chandramouli 2016-07-28 09:17:40 -07:00 коммит произвёл Vlad Filippov
Родитель f91a3279e2
Коммит 5c9f6718fd
11 изменённых файлов: 959 добавлений и 422 удалений

Просмотреть файл

@ -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
})

1249
npm-shrinkwrap.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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',