diff --git a/bin/dbwriter b/bin/dbwriter index f50e577de773aebaf3592c25c5a8aabb3c5cdca1..8c2981b70c46383eeeb13187376169ad34f50e6a 100755 --- a/bin/dbwriter +++ b/bin/dbwriter @@ -19,7 +19,20 @@ config = require('../lib/configuration.js'), heartbeat = require('../lib/heartbeat.js'), metrics = require('../lib/metrics.js'), logger = require('../lib/logging.js').logger, -shutdown = require('../lib/shutdown'); +shutdown = require('../lib/shutdown'), +cachify = require('connect-cachify'), +assets = require('../lib/static_resources').all; + +// dbwriter needs cachify to properly render emails with cachified URLs, +// but it serves no cachified content, so it's not necc to register it as +// a handler of any sort. +cachify.setup( + assets(config.get('supported_languages')), + { + prefix: config.get('cachify_prefix'), + production: config.get('use_minified_resources'), + root: path.join(__dirname, "..", "resources", "static") + }); var app = undefined; diff --git a/lib/email.js b/lib/email.js index 13d24ffbb45147cbf63c130d6dc1d111d9e5b897..31615a2fbe8061e0d72e11ded137b2edcc06e2bb 100644 --- a/lib/email.js +++ b/lib/email.js @@ -28,35 +28,41 @@ if (smtp_params && smtp_params.host) { } } -const TEMPLATE_PATH = path.join(__dirname, "static", "email_templates"); +const TEMPLATE_PATH = path.join(__dirname, "..", "resources", "email_templates"); -// the underbar decorator to allow getext to extract strings +// the underbar decorator to allow getext to extract strings function _(str) { return str; } // a map of all the different emails we send const templates = { "new": { landing: 'verify_email_address', - subject: _("Confirm email address for Persona"), + subject: _("Confirm email address for Persona"), template: fs.readFileSync(path.join(TEMPLATE_PATH, 'new.ejs')), + templateHTML: fs.readFileSync(path.join(TEMPLATE_PATH, 'new.html.ejs')) }, "reset": { landing: 'reset_password', subject: _("Reset Persona password"), template: fs.readFileSync(path.join(TEMPLATE_PATH, 'reset.ejs')), + templateHTML: fs.readFileSync(path.join(TEMPLATE_PATH, 'reset.html.ejs')) }, "confirm": { landing: 'confirm', subject: _("Confirm email address for Persona"), template: fs.readFileSync(path.join(TEMPLATE_PATH, 'confirm.ejs')), + templateHTML: fs.readFileSync(path.join(TEMPLATE_PATH, 'confirm.html.ejs')) } }; // now turn file contents into compiled templates Object.keys(templates).forEach(function(type) { templates[type].template = ejs.compile(templates[type].template.toString()); + if (templates[type].templateHTML) { + templates[type].templateHTML = ejs.compile(templates[type].templateHTML.toString()); + } }); - + var interceptor = undefined; @@ -94,19 +100,27 @@ function doSend(email_type, email, site, secret, langContext) { // log verification email to console separated by whitespace. console.log("\nVERIFICATION URL:\n" + public_url + "\n"); } else { - emailer.send_mail({ + var templateArgs = { + link: public_url, + site: site, + gettext: GETTEXT, + format: format + }; + + var mailOpts = { // XXX: Ideally this would be a live email address and a response to these email // addresses would go into a ticketing system (lloyd/skinny) sender: "Persona <no-reply@persona.org>", to: email, subject: GETTEXT(email_params.subject), - body: email_params.template({ - link: public_url, - site: site, - gettext: GETTEXT, - format: format - }) - }, function(err, success){ + text: email_params.template(templateArgs) + }; + + if (email_params.templateHTML) { + mailOpts.html = email_params.templateHTML(templateArgs); + } + + emailer.send_mail(mailOpts, function(err, success) { if (!success) { logger.error("error sending email to: " + email + " - " + err); } diff --git a/package.json b/package.json index 1e45de6881495042a873d00681c892f3c587fa08..5c2099d18d055a32e5e8549b2b7902f9b16d3481 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,12 @@ "ejs": "0.4.3", "etagify": "0.0.2", "express": "2.5.0", - "gobbledygook": "0.0.3", + "gobbledygook": "0.0.3", "mustache": "0.3.1-dev", "jwcrypto": "0.3.2", "mysql": "0.9.5", "node-statsd": "https://github.com/downloads/lloyd/node-statsd/0509f85.tgz", - "nodemailer": "0.1.24", + "nodemailer": "0.3.21", "mkdirp": "0.3.0", "optimist": "0.2.8", "postprocess": "0.2.4", diff --git a/lib/static/email_templates/new.ejs b/resources/email_templates/confirm.ejs similarity index 100% rename from lib/static/email_templates/new.ejs rename to resources/email_templates/confirm.ejs diff --git a/resources/email_templates/confirm.html.ejs b/resources/email_templates/confirm.html.ejs new file mode 100644 index 0000000000000000000000000000000000000000..4e6529f29846faccef7b95b12acdb34d8640c545 --- /dev/null +++ b/resources/email_templates/confirm.html.ejs @@ -0,0 +1,93 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + #outlook a{ + padding:0; + } + body{ + background: url("<%= cachify('/common/i/grain.png') %>") #76868f; + background: url("<%= cachify('/pages/i/marketplace-header.png') %>") top repeat-x, url("<%= cachify('/common/i/grain.png') %>") #76868f; + margin:0; + padding:0; + width:100% !important; + -webkit-text-size-adjust:none; + font-family: Helvetica, Arial, sans-serif; + font-size: 14px; + color: #424f5a; + } + img{ + border:0; + height:auto; + line-height:100%; + outline:none; + text-decoration:none; + } + a{ + color: #3b9bda; + text-decoration: none; + } + a:hover{ + text-decoration: underline; + } + p{ + margin: 0 0 15px; + line-height: 1.5; + } + + h2{ + font-size: 18px; + margin: 20px 0 15px; + } + + p:last-child{ + margin-bottom: 0; + } + table td{ + border-collapse:collapse; + } + + #content{ + border-radius: 3px; + padding: 30px; + } + </style> + </head> + <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" style="-webkit-text-size-adjust: none;margin: 0;padding: 0;background-color: #76868f;width: 100% !important;"> + <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" style="margin: 0;padding: 0;height: 100% !important;width: 100% !important;"> + <td align="left" valign="top" style="border-collapse: collapse;"> + <center> + <table border="0" cellpadding="0" cellspacing="0" width="600"> + <tr class="spacer"> + <td height="30px"></td> + </tr> + <tr> + <td align="left" valign="top" style="border-collapse: collapse;"> + <img src="<%= cachify('/pages/i/persona-logo-wordmark.png') %>" alt="Mozilla Persona" /> + </td> + </tr> + <tr class="spacer"> + <td height="30px"></td> + </tr> + </table><!-- #header --> + <table id="content" border="0" cellpadding="0" cellspacing="0" width="600" style="background-color: #fff;"> + <tr> + <td align="left" valign="top" class="email-content" style="border-collapse: collapse;"> + <p><%= gettext('Thank you for using Persona.') %></p> + + <p><a href="<%= link %>" style="color: #3b9bda; font-weight: bold;"><%= format(gettext('Click to confirm this email address and automatically sign in to %s'), [site]) %>.</a></p> + + <p><%= gettext('Note: If you are NOT trying to sign into this website, please ignore this email.') %></p> + + <p><%= gettext('Thank you,') %><br /> + <%= gettext('The Persona team') %></p> + </td> + </tr> + </table><!-- #content --> + </center> + </td> + </tr> + </table><!-- #container table --> + </body> +</html> diff --git a/lib/static/email_templates/confirm.ejs b/resources/email_templates/new.ejs similarity index 100% rename from lib/static/email_templates/confirm.ejs rename to resources/email_templates/new.ejs diff --git a/resources/email_templates/new.html.ejs b/resources/email_templates/new.html.ejs new file mode 100644 index 0000000000000000000000000000000000000000..9efd4bd295a24c9344e978b7197c47848c7a22d8 --- /dev/null +++ b/resources/email_templates/new.html.ejs @@ -0,0 +1,93 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + #outlook a{ + padding:0; + } + body{ + background: url("<%= cachify('/common/i/grain.png') %>") #76868f; + background: url("<%= cachify('/pages/i/marketplace-header.png') %>") top repeat-x, url("<%= cachify('/common/i/grain.png') %>") #76868f; + margin:0; + padding:0; + width:100% !important; + -webkit-text-size-adjust:none; + font-family: Helvetica, Arial, sans-serif; + font-size: 14px; + color: #424f5a; + } + img{ + border:0; + height:auto; + line-height:100%; + outline:none; + text-decoration:none; + } + a{ + color: #3b9bda; + text-decoration: none; + } + a:hover{ + text-decoration: underline; + } + p{ + margin: 0 0 15px; + line-height: 1.5; + } + + h2{ + font-size: 18px; + margin: 20px 0 15px; + } + + p:last-child{ + margin-bottom: 0; + } + table td{ + border-collapse:collapse; + } + + #content{ + border-radius: 3px; + padding: 30px; + } + </style> + </head> + <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" style="-webkit-text-size-adjust: none;margin: 0;padding: 0;background-color: #76868f;width: 100% !important;"> + <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" style="margin: 0;padding: 0;height: 100% !important;width: 100% !important;"> + <td align="left" valign="top" style="border-collapse: collapse;"> + <center> + <table border="0" cellpadding="0" cellspacing="0" width="600"> + <tr class="spacer"> + <td height="30px"></td> + </tr> + <tr> + <td align="left" valign="top" style="border-collapse: collapse;"> + <img src="<%= cachify('/pages/i/persona-logo-wordmark.png') %>" alt="Mozilla Persona" /> + </td> + </tr> + <tr class="spacer"> + <td height="30px"></td> + </tr> + </table><!-- #header --> + <table id="content" border="0" cellpadding="0" cellspacing="0" width="600" style="background-color: #fff;"> + <tr> + <td align="left" valign="top" class="email-content" style="border-collapse: collapse;"> + <p><%= gettext('Thank you for using Persona.') %></p> + + <p><a href="<%= link %>" style="color: #3b9bda; font-weight: bold;"><%= format(gettext('Click to confirm this email address and automatically sign in to %s'), [site]) %>.</a></p> + + <p><%= gettext('Note: If you are NOT trying to sign into this website, please ignore this email.') %></p> + + <p><%= gettext('Thank you,') %><br /> + <%= gettext('The Persona team') %></p> + </td> + </tr> + </table><!-- #content --> + </center> + </td> + </tr> + </table><!-- #container table --> + </body> +</html> diff --git a/lib/static/email_templates/reset.ejs b/resources/email_templates/reset.ejs similarity index 100% rename from lib/static/email_templates/reset.ejs rename to resources/email_templates/reset.ejs diff --git a/resources/email_templates/reset.html.ejs b/resources/email_templates/reset.html.ejs new file mode 100644 index 0000000000000000000000000000000000000000..1766a14f7fbd4ddc29db34e1e73d75427745eaff --- /dev/null +++ b/resources/email_templates/reset.html.ejs @@ -0,0 +1,93 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + #outlook a{ + padding:0; + } + body{ + background: url("<%= cachify('/common/i/grain.png') %>") #76868f; + background: url("<%= cachify('/pages/i/marketplace-header.png') %>") top repeat-x, url("<%= cachify('/common/i/grain.png') %>") #76868f; + margin:0; + padding:0; + width:100% !important; + -webkit-text-size-adjust:none; + font-family: Helvetica, Arial, sans-serif; + font-size: 14px; + color: #424f5a; + } + img{ + border:0; + height:auto; + line-height:100%; + outline:none; + text-decoration:none; + } + a{ + color: #3b9bda; + text-decoration: none; + } + a:hover{ + text-decoration: underline; + } + p{ + margin: 0 0 15px; + line-height: 1.5; + } + + h2{ + font-size: 18px; + margin: 20px 0 15px; + } + + p:last-child{ + margin-bottom: 0; + } + table td{ + border-collapse:collapse; + } + + #content{ + border-radius: 3px; + padding: 30px; + } + </style> + </head> + <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" style="-webkit-text-size-adjust: none;margin: 0;padding: 0;background-color: #76868f;width: 100% !important;"> + <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" style="margin: 0;padding: 0;height: 100% !important;width: 100% !important;"> + <td align="left" valign="top" style="border-collapse: collapse;"> + <center> + <table border="0" cellpadding="0" cellspacing="0" width="600"> + <tr class="spacer"> + <td height="30px"></td> + </tr> + <tr> + <td align="left" valign="top" style="border-collapse: collapse;"> + <img src="<%= cachify('/pages/i/persona-logo-wordmark.png') %>" alt="Mozilla Persona" /> + </td> + </tr> + <tr class="spacer"> + <td height="30px"></td> + </tr> + </table><!-- #header --> + <table id="content" border="0" cellpadding="0" cellspacing="0" width="600" style="background-color: #fff;"> + <tr> + <td align="left" valign="top" class="email-content" style="border-collapse: collapse;"> + <p><%= gettext('Forgot your password for Persona? It happens to the best of us.') %></p> + + <p><a href="<%= link %>" style="color: #3b9bda; font-weight: bold;"><%= gettext('Click to reset your password:') %> <%= link %></a></p> + + <p><%= gettext('Note: If you did NOT ask to reset your password, please ignore this email.') %></p> + + <p><%= gettext('Thank you,') %><br /> + <%= gettext('The Persona team') %></p> + </td> + </tr> + </table><!-- #content --> + </center> + </td> + </tr> + </table><!-- #container table --> + </body> +</html>