mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
feat(email): WIP start implementing unsubscription page and related features
This commit is contained in:
1
common/dist/scripts/habitrpg-shared.js
vendored
1
common/dist/scripts/habitrpg-shared.js
vendored
@@ -7437,7 +7437,6 @@ process.browser = true;
|
|||||||
process.env = {};
|
process.env = {};
|
||||||
process.argv = [];
|
process.argv = [];
|
||||||
process.version = ''; // empty string to avoid regexp issues
|
process.version = ''; // empty string to avoid regexp issues
|
||||||
process.versions = {};
|
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
|
|||||||
@@ -109,9 +109,11 @@ api.registerUser = function(req, res, next) {
|
|||||||
newUser.preferences = newUser.preferences || {};
|
newUser.preferences = newUser.preferences || {};
|
||||||
newUser.preferences.language = req.language; // User language detected from browser, not saved
|
newUser.preferences.language = req.language; // User language detected from browser, not saved
|
||||||
var user = new User(newUser);
|
var user = new User(newUser);
|
||||||
utils.txnEmail(user, 'welcome');
|
|
||||||
ga.event('register', 'Local').send();
|
ga.event('register', 'Local').send();
|
||||||
user.save(cb);
|
user.save(function(err, savedUser){
|
||||||
|
utils.txnEmail(savedUser, 'welcome');
|
||||||
|
cb(err, savedUser);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}, function(err, data) {
|
}, function(err, data) {
|
||||||
@@ -178,9 +180,11 @@ api.loginSocial = function(req, res, next) {
|
|||||||
};
|
};
|
||||||
user.auth[network] = prof;
|
user.auth[network] = prof;
|
||||||
user = new User(user);
|
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();
|
ga.event('register', network).send();
|
||||||
}]
|
}]
|
||||||
}, function(err, results){
|
}, function(err, results){
|
||||||
|
|||||||
33
website/src/controllers/unsubscription.js
Normal file
33
website/src/controllers/unsubscription.js
Normal file
@@ -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!');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
var mongoose = require("mongoose");
|
var mongoose = require("mongoose");
|
||||||
var shared = require('../../../common');
|
var shared = require('../../../common');
|
||||||
|
|
||||||
|
// A collection used to store mailing list unsubscription for non registered email addresses
|
||||||
var EmailUnsubscriptionSchema = new mongoose.Schema({
|
var EmailUnsubscriptionSchema = new mongoose.Schema({
|
||||||
_id: {
|
_id: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
8
website/src/routes/unsubscription.js
Normal file
8
website/src/routes/unsubscription.js
Normal 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;
|
||||||
@@ -127,6 +127,7 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
|||||||
app.use(require('./routes/payments').middleware);
|
app.use(require('./routes/payments').middleware);
|
||||||
app.use(require('./routes/auth').middleware);
|
app.use(require('./routes/auth').middleware);
|
||||||
app.use(require('./routes/coupon').middleware);
|
app.use(require('./routes/coupon').middleware);
|
||||||
|
app.use(require('./routes/unsubscription').middleware);
|
||||||
var v2 = express();
|
var v2 = express();
|
||||||
app.use('/api/v2', v2);
|
app.use('/api/v2', v2);
|
||||||
app.use('/api/v1', require('./routes/apiv1').middleware);
|
app.use('/api/v1', require('./routes/apiv1').middleware);
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ function getUserInfo(user, fields) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fields.indexOf('_id') != -1){
|
||||||
|
info._id = user._id;
|
||||||
|
}
|
||||||
|
|
||||||
if(fields.indexOf('canSend') != -1){
|
if(fields.indexOf('canSend') != -1){
|
||||||
info.canSend = user.preferences.emailNotifications.unsubscribeFromAll !== true;
|
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
|
// 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){
|
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){
|
}).filter(function(mailingInfo){
|
||||||
// Always send reset-password emails
|
// Always send reset-password emails
|
||||||
return (mailingInfo.email && (mailingInfo.canSend || emailType === 'reset-password'));
|
return (mailingInfo.email && (mailingInfo.canSend || emailType === 'reset-password'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Personal variables are personal to each email recipient, if they are missing
|
// Personal variables are personal to each email recipient, if they are missing
|
||||||
// we manually create a structure for them with RECIPIENT_NAME
|
// we manually create a structure for them with RECIPIENT_NAME and RECIPIENT_ID
|
||||||
// otherwise we just add RECIPIENT_NAME to the existing personal variables
|
// otherwise we just add RECIPIENT_NAME and RECIPIENT_ID to the existing personal variables
|
||||||
if(!personalVariables || personalVariables.length === 0){
|
if(!personalVariables || personalVariables.length === 0){
|
||||||
personalVariables = mailingInfoArray.map(function(mailingInfo){
|
personalVariables = mailingInfoArray.map(function(mailingInfo){
|
||||||
return {
|
return {
|
||||||
rcpt: mailingInfo.email,
|
rcpt: mailingInfo.email,
|
||||||
vars: [{
|
vars: [
|
||||||
name: 'RECIPIENT_NAME',
|
{
|
||||||
content: mailingInfo.name
|
name: 'RECIPIENT_NAME',
|
||||||
}]
|
content: mailingInfo.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RECIPIENT_UNSUB_URL_PARAM',
|
||||||
|
content: module.exports.encrypt(JSON.stringify({_id: mailingInfo._id, email: mailingInfo.email}))
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
var temporaryPersonalVariables = {};
|
var temporaryPersonalVariables = {};
|
||||||
|
|
||||||
mailingInfoArray.forEach(function(mailingInfo){
|
mailingInfoArray.forEach(function(mailingInfo){
|
||||||
temporaryPersonalVariables[mailingInfo.email] = mailingInfo.name;
|
temporaryPersonalVariables[mailingInfo.email] = {
|
||||||
|
name: mailingInfo.name,
|
||||||
|
_id: mailingInfo._id
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
personalVariables.forEach(function(singlePersonalVariables){
|
personalVariables.forEach(function(singlePersonalVariables){
|
||||||
singlePersonalVariables.vars.push({
|
singlePersonalVariables.vars.push(
|
||||||
name: 'RECIPIENT_NAME',
|
{
|
||||||
content: temporaryPersonalVariables[singlePersonalVariables.rcpt]
|
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
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user