Merge pull request #4968 from HabitRPG/paglias/emails-unsubscription-page

WIP Emails unsubscription page
This commit is contained in:
Matteo Pagliazzi 2015-04-08 19:12:28 +02:00
commit e58521f416
9 changed files with 127 additions and 21 deletions

View file

@ -7451,7 +7451,6 @@ process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}

View file

@ -98,6 +98,9 @@
"invitedQuest": "Invited to Quest",
"kickedGroup": "Kicked from group",
"remindersToLogin": "Reminders to check in to HabitRPG",
"unsubscribedSuccessfully": "Unsubscribed successfully!",
"unsubscribedTextUsers": "You have successfully unsubscribed from all HabitRPG emails. You can enable only the emails you want to receive from <a href=\"/#/options/settings/notifications\">the settings</a> (requires login).",
"unsubscribedTextOthers": "You won't receive any other email from HabitRPG.",
"unsubscribeAllEmails": "Check to Unsubscribe from Emails",
"unsubscribeAllEmailsText": "By checking this box, I certify that I understand that by unsubscribing from all emails, HabitRPG will never be able to notify me via email about important changes to the site or my account.",
"correctlyUnsubscribedEmailType": "Correctly unsubscribed from \"<%= emailType %>\" emails.",

View file

@ -7,6 +7,7 @@ var utils = require('../utils');
var nconf = require('nconf');
var request = require('request');
var User = require('../models/user').model;
var EmailUnsubscription = require('../models/emailUnsubscription').model;
var ga = require('./../utils').ga;
var i18n = require('./../i18n');
@ -109,9 +110,14 @@ 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){
// Clean previous email preferences
EmailUnsubscription.remove({email: savedUser.auth.local.email}, function(){
utils.txnEmail(savedUser, 'welcome');
});
cb.apply(cb, arguments);
});
}
}]
}, function(err, data) {
@ -178,9 +184,16 @@ api.loginSocial = function(req, res, next) {
};
user.auth[network] = prof;
user = new User(user);
user.save(cb);
user.save(function(err, savedUser){
// Clean previous email preferences
if(savedUser.auth.facebook.emails && savedUser.auth.facebook.emails[0] && savedUser.auth.facebook.emails[0].value){
EmailUnsubscription.remove({email: savedUser.auth.facebook.emails[0].value}, function(){
utils.txnEmail(savedUser, 'welcome');
});
}
cb.apply(cb, arguments);
});
utils.txnEmail(user, 'welcome');
ga.event('register', network).send();
}]
}, function(err, results){

View file

@ -12,6 +12,7 @@ var shared = require('../../../common');
var User = require('./../models/user').model;
var Group = require('./../models/group').model;
var Challenge = require('./../models/challenge').model;
var EmailUnsubscription = require('./../models/emailUnsubscription').model;
var isProd = nconf.get('NODE_ENV') === 'production';
var api = module.exports;
@ -623,10 +624,15 @@ var inviteByEmails = function(invites, group, req, res, next){
}
// TODO implement "users can only be invited once"
invite.canSend = true; // Requested by utils.txnEmail
utils.txnEmail(invite, ('invite-friend' + (group.type == 'guild' ? '-guild' : '')), variables);
// Check for the email address not to be unsubscribed
EmailUnsubscription.findOne({email: invite.email}, function(err, unsubscribed){
if(err) return cb(err);
if(unsubscribed) return cb();
cb();
utils.txnEmail(invite, ('invite-friend' + (group.type == 'guild' ? '-guild' : '')), variables);
cb();
});
});
}else{
cb();

View file

@ -0,0 +1,36 @@
var User = require('../models/user').model;
var EmailUnsubscription = require('../models/emailUnsubscription').model;
var utils = require('../utils');
var i18n = require('../../../common').i18n;
var api = module.exports = {};
api.unsubscribe = function(req, res, next){
if(!req.query.code) return res.json(500, {err: 'Missing unsubscription code.'});
var data = JSON.parse(utils.decrypt(req.query.code));
if(data._id){
User.update({_id: data._id}, {
$set: {'preferences.emailNotifications.unsubscribeFromAll': true}
}, {multi: false}, function(err, nAffected){
if(err) return next(err);
if(nAffected !== 1) return res.json(404, {err: 'User not found'});
res.send('<h1>' + i18n.t('unsubscribedSuccessfully', null, req.language) + '</h1>' + i18n.t('unsubscribedTextUsers', null, req.language));
});
}else{
EmailUnsubscription.findOne({email: data.email}, function(err, doc){
if(err) return next(err);
var okRes = '<h1>' + i18n.t('unsubscribedSuccessfully', null, req.language) + '</h1>' + i18n.t('unsubscribedTextOthers', null, req.language);
if(doc) return res.send(okRes);
EmailUnsubscription.create({email: data.email}, function(err, doc){
if(err) return next(err);
res.send(okRes);
})
});
}
};

View file

@ -0,0 +1,14 @@
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,
'default': shared.uuid
},
email: String
});
module.exports.schema = EmailUnsubscriptionSchema;
module.exports.model = mongoose.model('EmailUnsubscription', EmailUnsubscriptionSchema);

View file

@ -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;

View file

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

View file

@ -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,59 @@ 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'));
// Don't check canSend for non registered users as already checked before
return (mailingInfo.email && ((!mailingInfo._id || 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_UNSUB_URL
// otherwise we just add RECIPIENT_NAME and RECIPIENT_UNSUB_URL 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',
content: baseUrl + '/unsubscribe?code=' + 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',
content: baseUrl + '/unsubscribe?code=' + module.exports.encrypt(JSON.stringify({
_id: temporaryPersonalVariables[singlePersonalVariables.rcpt]._id,
email: singlePersonalVariables.rcpt
}))
}
)
});
}