Send welcome and unsubscribe confirmation emails
This commit is contained in:
parent
15ed41741e
commit
9e3b42e11c
12 changed files with 189 additions and 33 deletions
|
@ -34,8 +34,6 @@ database="mailtrain"
|
||||||
charset="utf8mb4"
|
charset="utf8mb4"
|
||||||
# enter path for mysql command line application
|
# enter path for mysql command line application
|
||||||
command="mysql"
|
command="mysql"
|
||||||
# required database schema version
|
|
||||||
schema_version=1
|
|
||||||
|
|
||||||
[redis]
|
[redis]
|
||||||
# enable to use Redis session cache or disable if Redis is not installed
|
# enable to use Redis session cache or disable if Redis is not installed
|
||||||
|
|
16
index.js
16
index.js
|
@ -12,7 +12,6 @@ let sender = require('./services/sender');
|
||||||
let importer = require('./services/importer'); // eslint-disable-line global-require
|
let importer = require('./services/importer'); // eslint-disable-line global-require
|
||||||
let verpServer = require('./services/verp-server'); // 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 testServer = require('./services/test-server'); // eslint-disable-line global-require
|
||||||
let settings = require('./lib/models/settings');
|
|
||||||
|
|
||||||
let port = config.www.port;
|
let port = config.www.port;
|
||||||
let host = config.www.host;
|
let host = config.www.host;
|
||||||
|
@ -34,20 +33,7 @@ let server = http.createServer(app);
|
||||||
* Listen on provided port, on all network interfaces.
|
* Listen on provided port, on all network interfaces.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
settings.list(['db_schema_version'], (err, configItems) => {
|
server.listen(port, host);
|
||||||
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.on('error', err => {
|
server.on('error', err => {
|
||||||
if (err.syscall !== 'listen') {
|
if (err.syscall !== 'listen') {
|
||||||
|
|
|
@ -182,6 +182,10 @@ module.exports.subscribe = (cid, optInIp, callback) => {
|
||||||
subscription = {};
|
subscription = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subscription.cid = cid;
|
||||||
|
subscription.list = listId;
|
||||||
|
subscription.email = email;
|
||||||
|
|
||||||
let optInCountry = geoip.lookupCountry(optInIp) || null;
|
let optInCountry = geoip.lookupCountry(optInIp) || null;
|
||||||
module.exports.insert(listId, {
|
module.exports.insert(listId, {
|
||||||
email,
|
email,
|
||||||
|
@ -200,11 +204,7 @@ module.exports.subscribe = (cid, optInIp, callback) => {
|
||||||
}
|
}
|
||||||
connection.query('DELETE FROM confirmations WHERE `cid`=? LIMIT 1', [cid], () => {
|
connection.query('DELETE FROM confirmations WHERE `cid`=? LIMIT 1', [cid], () => {
|
||||||
connection.release();
|
connection.release();
|
||||||
callback(null, {
|
callback(null, subscription);
|
||||||
list: listId,
|
|
||||||
cid,
|
|
||||||
email
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -361,8 +361,8 @@ module.exports.get = (listId, cid, callback) => {
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = tools.convertKeys(rows[0]);
|
let subscription = tools.convertKeys(rows[0]);
|
||||||
return callback(null, list);
|
return callback(null, subscription);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -448,7 +448,7 @@ module.exports.unsubscribe = (listId, email, campaignId, callback) => {
|
||||||
return callback(err);
|
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();
|
connection.release();
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
@ -457,8 +457,13 @@ module.exports.unsubscribe = (listId, email, campaignId, callback) => {
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = rows[0].id;
|
let subscription = tools.convertKeys(rows[0]);
|
||||||
module.exports.changeStatus(id, listId, campaignId, 2, callback);
|
module.exports.changeStatus(subscription.id, listId, campaignId, 2, err => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(null, subscription);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "grunt",
|
"test": "grunt",
|
||||||
"start": "node index.js",
|
"start": "node setup/sql/sql.js && node index.js",
|
||||||
"sql": "node setup/sql/sql.js",
|
"sql": "node setup/sql/sql.js",
|
||||||
"sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql",
|
"sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql",
|
||||||
"sqldrop": "node setup/sql/drop.js"
|
"sqldrop": "node setup/sql/drop.js"
|
||||||
|
|
|
@ -35,7 +35,7 @@ router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.list(['defaultHomepage', 'serviceUrl', 'pgpPrivateKey'], (err, configItems) => {
|
settings.list(['defaultHomepage', 'serviceUrl', 'pgpPrivateKey', 'defaultAddress', 'defaultFrom'], (err, configItems) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,45 @@ router.get('/subscribe/:cid', (req, res, next) => {
|
||||||
preferences: '/subscription/' + list.cid + '/manage/' + subscription.cid,
|
preferences: '/subscription/' + list.cid + '/manage/' + subscription.cid,
|
||||||
hasPubkey: !!configItems.pgpPrivateKey
|
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;
|
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) {
|
if (err) {
|
||||||
req.flash('danger', err.message || err);
|
req.flash('danger', err.message || err);
|
||||||
return res.redirect('/subscription/' + encodeURIComponent(req.params.lcid) + '/unsubscribe/' + encodeURIComponent(req.body.cid) + '?' + tools.queryParams(req.body));
|
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');
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,5 +13,5 @@ respawn limit 10 0
|
||||||
|
|
||||||
script
|
script
|
||||||
cd /opt/mailtrain
|
cd /opt/mailtrain
|
||||||
exec node index.js >> /var/log/mailtrain.log 2>&1
|
exec npm start >> /var/log/mailtrain.log 2>&1
|
||||||
end script
|
end script
|
||||||
|
|
|
@ -178,6 +178,6 @@ runUpdates(err => {
|
||||||
log.error('sql', err);
|
log.error('sql', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
log.info('sql', 'Database update completed');
|
log.info('sql', 'Database check completed');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
30
views/emails/subscription-confirmed-html.hbs
Normal file
30
views/emails/subscription-confirmed-html.hbs
Normal 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>
|
16
views/emails/subscription-confirmed-text.hbs
Normal file
16
views/emails/subscription-confirmed-text.hbs
Normal 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}}}
|
26
views/emails/unsubscribe-confirmed-html.hbs
Normal file
26
views/emails/unsubscribe-confirmed-html.hbs
Normal 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>
|
12
views/emails/unsubscribe-confirmed-text.hbs
Normal file
12
views/emails/unsubscribe-confirmed-text.hbs
Normal 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}}}
|
|
@ -10,6 +10,6 @@
|
||||||
</a>
|
</a>
|
||||||
or
|
or
|
||||||
<a class="btn btn-primary" href="{{preferences}}" role="button">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue