From 71108ee935f58701ecadceb3bd43de26cd428eb9 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 3 Apr 2015 13:26:20 +0200 Subject: [PATCH] feat(email): WIP start implementing unsubscription page and related features --- common/dist/scripts/habitrpg-shared.js | 1 - website/src/controllers/auth.js | 12 ++++-- website/src/controllers/unsubscription.js | 33 ++++++++++++++++ website/src/models/emailUnsubscription.js | 1 + website/src/routes/unsubscription.js | 8 ++++ website/src/server.js | 1 + website/src/utils.js | 46 +++++++++++++++++------ 7 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 website/src/controllers/unsubscription.js create mode 100644 website/src/routes/unsubscription.js diff --git a/common/dist/scripts/habitrpg-shared.js b/common/dist/scripts/habitrpg-shared.js index e0c5bc83ce..d81443b13c 100644 --- a/common/dist/scripts/habitrpg-shared.js +++ b/common/dist/scripts/habitrpg-shared.js @@ -7437,7 +7437,6 @@ process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues -process.versions = {}; function noop() {} diff --git a/website/src/controllers/auth.js b/website/src/controllers/auth.js index 25681d9474..0add5f5aee 100644 --- a/website/src/controllers/auth.js +++ b/website/src/controllers/auth.js @@ -109,9 +109,11 @@ api.registerUser = function(req, res, next) { newUser.preferences = newUser.preferences || {}; newUser.preferences.language = req.language; // User language detected from browser, not saved var user = new User(newUser); - utils.txnEmail(user, 'welcome'); ga.event('register', 'Local').send(); - user.save(cb); + user.save(function(err, savedUser){ + utils.txnEmail(savedUser, 'welcome'); + cb(err, savedUser); + }); } }] }, function(err, data) { @@ -178,9 +180,11 @@ api.loginSocial = function(req, res, next) { }; user.auth[network] = prof; user = new User(user); - user.save(cb); + user.save(function(err, savedUser){ + utils.txnEmail(savedUser, 'welcome'); + cb(err, savedUser); + }); - utils.txnEmail(user, 'welcome'); ga.event('register', network).send(); }] }, function(err, results){ diff --git a/website/src/controllers/unsubscription.js b/website/src/controllers/unsubscription.js new file mode 100644 index 0000000000..fde41c3cee --- /dev/null +++ b/website/src/controllers/unsubscription.js @@ -0,0 +1,33 @@ +var User = require('../models/user'); +var EmailUnsubscription = require('../models/emailUnsubscription'); +var utils = require('../utils'); + +var api = module.exports = {}; + +api.unsubscribe = function(req, res, next){ + if(!req.query.code) return next(new Error('Missing unsubscription code.')); + + var data = JSON.parse(utils.decrypt(req.query.code)); + + if(data._id){ + User.update({_id: data._id}, { + $set: {} + }, {multi: false}, function(err, nAffected){ + if(err) return next(err); + if(nAffected !== 1) return next(new Error('User not found')); + + res.send('Unsubscribed!'); + }); + }else{ + EmailUnsubscription.findOne({email: data.email}, function(err, res){ + if(err) return next(err); + if(res) return next(new Error('Email address already unsubscribed')); + + EmailUnsubscription.create({email: data.email}, function(err, res){ + if(err) return next(err); + + res.send('Unsubscribed!'); + }) + }); + } +}; \ No newline at end of file diff --git a/website/src/models/emailUnsubscription.js b/website/src/models/emailUnsubscription.js index cfc6b02502..144417f3fc 100644 --- a/website/src/models/emailUnsubscription.js +++ b/website/src/models/emailUnsubscription.js @@ -1,6 +1,7 @@ var mongoose = require("mongoose"); var shared = require('../../../common'); +// A collection used to store mailing list unsubscription for non registered email addresses var EmailUnsubscriptionSchema = new mongoose.Schema({ _id: { type: String, diff --git a/website/src/routes/unsubscription.js b/website/src/routes/unsubscription.js new file mode 100644 index 0000000000..efd06f9197 --- /dev/null +++ b/website/src/routes/unsubscription.js @@ -0,0 +1,8 @@ +var express = require('express'); +var router = new express.Router(); +var i18n = require('../i18n'); +var unsubscription = require('../controllers/unsubscription'); + +router.get('/unsubscribe', i18n.getUserLanguage, unsubscription.unsubscribe); + +module.exports = router; \ No newline at end of file diff --git a/website/src/server.js b/website/src/server.js index 1c3484d776..1447823a4d 100644 --- a/website/src/server.js +++ b/website/src/server.js @@ -127,6 +127,7 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) { app.use(require('./routes/payments').middleware); app.use(require('./routes/auth').middleware); app.use(require('./routes/coupon').middleware); + app.use(require('./routes/unsubscription').middleware); var v2 = express(); app.use('/api/v2', v2); app.use('/api/v1', require('./routes/apiv1').middleware); diff --git a/website/src/utils.js b/website/src/utils.js index 6349ca5d3e..0cfa22b5c7 100644 --- a/website/src/utils.js +++ b/website/src/utils.js @@ -44,6 +44,10 @@ function getUserInfo(user, fields) { } } + if(fields.indexOf('_id') != -1){ + info._id = user._id; + } + if(fields.indexOf('canSend') != -1){ info.canSend = user.preferences.emailNotifications.unsubscribeFromAll !== true; } @@ -62,37 +66,55 @@ module.exports.txnEmail = function(mailingInfoArray, emailType, variables, perso // It's important to pass at least a user with its `preferences` as we need to check if he unsubscribed mailingInfoArray = mailingInfoArray.map(function(mailingInfo){ - return mailingInfo._id ? getUserInfo(mailingInfo, ['email', 'name', 'canSend']) : mailingInfo; + return mailingInfo._id ? getUserInfo(mailingInfo, ['_id', 'email', 'name', 'canSend']) : mailingInfo; }).filter(function(mailingInfo){ // Always send reset-password emails return (mailingInfo.email && (mailingInfo.canSend || emailType === 'reset-password')); }); // Personal variables are personal to each email recipient, if they are missing - // we manually create a structure for them with RECIPIENT_NAME - // otherwise we just add RECIPIENT_NAME to the existing personal variables + // we manually create a structure for them with RECIPIENT_NAME and RECIPIENT_ID + // otherwise we just add RECIPIENT_NAME and RECIPIENT_ID to the existing personal variables if(!personalVariables || personalVariables.length === 0){ personalVariables = mailingInfoArray.map(function(mailingInfo){ return { rcpt: mailingInfo.email, - vars: [{ - name: 'RECIPIENT_NAME', - content: mailingInfo.name - }] + vars: [ + { + name: 'RECIPIENT_NAME', + content: mailingInfo.name + }, + { + name: 'RECIPIENT_UNSUB_URL_PARAM', + content: module.exports.encrypt(JSON.stringify({_id: mailingInfo._id, email: mailingInfo.email})) + } + ] } }); }else{ var temporaryPersonalVariables = {}; mailingInfoArray.forEach(function(mailingInfo){ - temporaryPersonalVariables[mailingInfo.email] = mailingInfo.name; + temporaryPersonalVariables[mailingInfo.email] = { + name: mailingInfo.name, + _id: mailingInfo._id + } }); personalVariables.forEach(function(singlePersonalVariables){ - singlePersonalVariables.vars.push({ - name: 'RECIPIENT_NAME', - content: temporaryPersonalVariables[singlePersonalVariables.rcpt] - }); + singlePersonalVariables.vars.push( + { + name: 'RECIPIENT_NAME', + content: temporaryPersonalVariables[singlePersonalVariables.rcpt].name + }, + { + name: 'RECIPIENT_UNSUB_URL_PARAM', + content: module.exports.encrypt(JSON.stringify({ + _id: temporaryPersonalVariables[singlePersonalVariables.rcpt]._id, + email: singlePersonalVariables.rcpt + })) + } + ) }); }