Send welcome and unsubscribe confirmation emails

This commit is contained in:
Andris Reinman 2016-04-21 20:17:19 +03:00
parent 15ed41741e
commit 9e3b42e11c
12 changed files with 189 additions and 33 deletions

View file

@ -34,8 +34,6 @@ database="mailtrain"
charset="utf8mb4"
# enter path for mysql command line application
command="mysql"
# required database schema version
schema_version=1
[redis]
# enable to use Redis session cache or disable if Redis is not installed

View file

@ -12,7 +12,6 @@ let sender = require('./services/sender');
let importer = require('./services/importer'); // eslint-disable-line global-require
let verpServer = require('./services/verp-server'); // eslint-disable-line global-require
let testServer = require('./services/test-server'); // eslint-disable-line global-require
let settings = require('./lib/models/settings');
let port = config.www.port;
let host = config.www.host;
@ -34,20 +33,7 @@ let server = http.createServer(app);
* Listen on provided port, on all network interfaces.
*/
settings.list(['db_schema_version'], (err, configItems) => {
if (err) {
throw err;
}
let dbSchemaVersion = Number(configItems.dbSchemaVersion) || 0;
if (dbSchemaVersion < config.mysql.schema_version) {
log.error('Database', 'Database schema outdated. Run `npm run sql` to upgrade');
return process.exit(1);
}
server.listen(port, host);
});
server.listen(port, host);
server.on('error', err => {
if (err.syscall !== 'listen') {

View file

@ -182,6 +182,10 @@ module.exports.subscribe = (cid, optInIp, callback) => {
subscription = {};
}
subscription.cid = cid;
subscription.list = listId;
subscription.email = email;
let optInCountry = geoip.lookupCountry(optInIp) || null;
module.exports.insert(listId, {
email,
@ -200,11 +204,7 @@ module.exports.subscribe = (cid, optInIp, callback) => {
}
connection.query('DELETE FROM confirmations WHERE `cid`=? LIMIT 1', [cid], () => {
connection.release();
callback(null, {
list: listId,
cid,
email
});
callback(null, subscription);
});
});
});
@ -361,8 +361,8 @@ module.exports.get = (listId, cid, callback) => {
return callback(null, false);
}
let list = tools.convertKeys(rows[0]);
return callback(null, list);
let subscription = tools.convertKeys(rows[0]);
return callback(null, subscription);
});
});
};
@ -448,7 +448,7 @@ module.exports.unsubscribe = (listId, email, campaignId, callback) => {
return callback(err);
}
connection.query('SELECT id, status FROM `subscription__' + listId + '` WHERE `email`=?', [email], (err, rows) => {
connection.query('SELECT * FROM `subscription__' + listId + '` WHERE `email`=?', [email], (err, rows) => {
connection.release();
if (err) {
return callback(err);
@ -457,8 +457,13 @@ module.exports.unsubscribe = (listId, email, campaignId, callback) => {
return callback(null, false);
}
let id = rows[0].id;
module.exports.changeStatus(id, listId, campaignId, 2, callback);
let subscription = tools.convertKeys(rows[0]);
module.exports.changeStatus(subscription.id, listId, campaignId, 2, err => {
if (err) {
return callback(err);
}
return callback(null, subscription);
});
});
});
};

View file

@ -6,7 +6,7 @@
"main": "index.js",
"scripts": {
"test": "grunt",
"start": "node index.js",
"start": "node setup/sql/sql.js && node index.js",
"sql": "node setup/sql/sql.js",
"sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql",
"sqldrop": "node setup/sql/drop.js"

View file

@ -35,7 +35,7 @@ router.get('/subscribe/:cid', (req, res, next) => {
return next(err);
}
settings.list(['defaultHomepage', 'serviceUrl', 'pgpPrivateKey'], (err, configItems) => {
settings.list(['defaultHomepage', 'serviceUrl', 'pgpPrivateKey', 'defaultAddress', 'defaultFrom'], (err, configItems) => {
if (err) {
return next(err);
}
@ -47,6 +47,45 @@ router.get('/subscribe/:cid', (req, res, next) => {
preferences: '/subscription/' + list.cid + '/manage/' + subscription.cid,
hasPubkey: !!configItems.pgpPrivateKey
});
fields.list(list.id, (err, fieldList) => {
if (err) {
return log.error('Fields', err);
}
let encryptionKeys = [];
fields.getRow(fieldList, subscription).forEach(field => {
if (field.type === 'gpg' && field.value) {
encryptionKeys.push(field.value.trim());
}
});
mailer.sendMail({
from: {
name: configItems.defaultFrom,
address: configItems.defaultAddress
},
to: {
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
address: subscription.email
},
subject: list.name + ': Subscription Confirmed',
encryptionKeys
}, {
html: 'emails/subscription-confirmed-html.hbs',
text: 'emails/subscription-confirmed-text.hbs',
data: {
title: list.name,
contactAddress: configItems.defaultAddress,
preferencesUrl: urllib.resolve(configItems.serviceUrl, '/subscription/' + list.cid + '/manage/' + subscription.cid),
unsubscribeUrl: urllib.resolve(configItems.serviceUrl, '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid)
}
}, err => {
if (err) {
log.error('Subscription', err.stack);
}
});
});
});
});
});
@ -362,12 +401,56 @@ router.post('/:lcid/unsubscribe', passport.parseForm, passport.csrfProtection, (
let email = req.body.email;
subscriptions.unsubscribe(list.id, email, req.body.campaign, err => {
subscriptions.unsubscribe(list.id, email, req.body.campaign, (err, subscription) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/subscription/' + encodeURIComponent(req.params.lcid) + '/unsubscribe/' + encodeURIComponent(req.body.cid) + '?' + tools.queryParams(req.body));
}
res.redirect('/subscription/' + req.params.lcid + '/unsubscribe-notice');
fields.list(list.id, (err, fieldList) => {
if (err) {
return log.error('Fields', err);
}
let encryptionKeys = [];
fields.getRow(fieldList, subscription).forEach(field => {
if (field.type === 'gpg' && field.value) {
encryptionKeys.push(field.value.trim());
}
});
settings.list(['defaultHomepage', 'defaultFrom', 'defaultAddress', 'serviceUrl'], (err, configItems) => {
if (err) {
return next(err);
}
mailer.sendMail({
from: {
name: configItems.defaultFrom,
address: configItems.defaultAddress
},
to: {
name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '),
address: subscription.email
},
subject: list.name + ': Subscription Confirmed',
encryptionKeys
}, {
html: 'emails/unsubscribe-confirmed-html.hbs',
text: 'emails/unsubscribe-confirmed-text.hbs',
data: {
title: list.name,
contactAddress: configItems.defaultAddress,
subscribeUrl: urllib.resolve(configItems.serviceUrl, '/subscription/' + list.cid + '?cid=' + subscription.cid)
}
}, err => {
if (err) {
log.error('Subscription', err.stack);
}
});
});
});
});
});
});

View file

@ -13,5 +13,5 @@ respawn limit 10 0
script
cd /opt/mailtrain
exec node index.js >> /var/log/mailtrain.log 2>&1
exec npm start >> /var/log/mailtrain.log 2>&1
end script

View file

@ -178,6 +178,6 @@ runUpdates(err => {
log.error('sql', err);
process.exit(1);
}
log.info('sql', 'Database update completed');
log.info('sql', 'Database check completed');
process.exit(0);
});

View file

@ -0,0 +1,30 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}: Subscription Confirmed</title>
</head>
<body>
<h1>{{title}}</h1>
<h2>Your subscription to our list has been confirmed.</h2>
<p>
If you want to modify your subscription then you can:
</p>
<p>
<a href="{{preferencesUrl}}">manage your preferences</a>
<span style="color: #444444;"> or </span>
<a href="{{unsubscribeUrl}}">unsubscribe here</a>
</p>
<p>For questions about this list, please contact:
<br/>{{contactAddress}}</p>
</body>
</html>

View file

@ -0,0 +1,16 @@
{{{title}}}
Subscription Confirmed
======================
Your subscription to our list has been confirmed.
If you want to modify your subscription then you can:
manage your preferences: {{preferencesUrl}}
- or -
unsubscribe here: {{unsubscribeUrl}}
For questions about this list, please contact:
{{{contactAddress}}}

View file

@ -0,0 +1,26 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}: You are now unsubscribed</title>
</head>
<body>
<h1>{{title}}</h1>
<h2>We have removed your email address from our list.</h2>
<p>
If you unsubscribed by mistake, you can re-subscribe at:
</p>
<p><a href="{{subscribeUrl}}" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Subscribe</a></p>
<p>For questions about this list, please contact:
<br/>{{contactAddress}}</p>
</body>
</html>

View file

@ -0,0 +1,12 @@
{{{title}}}
You are now unsubscribed
========================
We have removed your email address from our list.
If you unsubscribed by mistake, you can re-subscribe at:
Subscribe: {{subscribeUrl}}
For questions about this list, please contact:
{{{contactAddress}}}

View file

@ -10,6 +10,6 @@
</a>
or
<a class="btn btn-primary" href="{{preferences}}" role="button">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> manage you preferences
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> manage your preferences
</a>
</p>