This commit is contained in:
Andris Reinman 2016-09-08 14:39:41 +03:00
parent 6c34091634
commit 95379f731f
17 changed files with 187 additions and 99 deletions

View file

@ -1,5 +1,11 @@
# Changelog
## 1.18.0 2016-09-08
* Updated installation script to bundle ZoneMTA as the default sending engine
* Added new option to disable clicked and opened tracking
* Store remote IP for subscription confirmations
## 1.17.0 2016-08-29
* Added new custom field for JSON data that is rendered using Handlebars when included in an email

View file

@ -13,7 +13,7 @@ let log = require('npmlog');
let mailer = require('../mailer');
let caches = require('../caches');
let allowedKeys = ['description', 'from', 'address', 'subject', 'template', 'source_url', 'list', 'segment', 'html', 'text'];
let allowedKeys = ['description', 'from', 'address', 'subject', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'tracking_disabled'];
module.exports.list = (start, limit, callback) => {
db.getConnection((err, connection) => {
@ -417,6 +417,8 @@ module.exports.create = (campaign, opts, callback) => {
campaign = tools.convertKeys(campaign);
let name = (campaign.name || '').toString().trim();
campaign.trackingDisabled = campaign.trackingDisabled ? 1 : 0;
opts = opts || {};
if (/^\d+:\d+$/.test(campaign.list)) {
@ -631,6 +633,8 @@ module.exports.update = (id, updates, callback) => {
let campaign = tools.convertKeys(updates);
let name = (campaign.name || '').toString().trim();
campaign.trackingDisabled = campaign.trackingDisabled ? 1 : 0;
if (!name) {
return callback(new Error('Campaign Name must be set'));
}

View file

@ -39,9 +39,11 @@ module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, li
if (err) {
return callback(err);
}
if(!data){
if (!data || data.campaign.trackingDisabled) {
return callback(null, false);
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
@ -154,6 +156,10 @@ module.exports.countOpen = (remoteIp, campaignCid, listCid, subscriptionCid, cal
return callback(err);
}
if (!data || data.campaign.trackingDisabled) {
return callback(null, false);
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
@ -260,6 +266,10 @@ module.exports.add = (url, campaignId, callback) => {
};
module.exports.updateLinks = (campaign, list, subscription, serviceUrl, message, callback) => {
if (campaign.trackingDisabled) {
// tracking is disabled, do not modify the message
return setImmediate(() => callback(null, message));
}
let re = /(<a[^>]* href\s*=[\s"']*)(http[^"'>\s]+)/gi;
let urls = new Set();
(message || '').replace(re, (match, prefix, url) => {
@ -272,7 +282,7 @@ module.exports.updateLinks = (campaign, list, subscription, serviceUrl, message,
// insert tracking image
let inserted = false;
let imgUrl = urllib.resolve(serviceUrl, util.format('/links/%s/%s/%s', campaign.cid, list.cid, encodeURIComponent(subscription.cid)));
let img = '<img src="' + imgUrl + '" width="1" height="1" alt="Tracking Image">';
let img = '<img src="' + imgUrl + '" width="1" height="1" alt="mt">';
message = message.replace(/<\/body\b/i, match => {
inserted = true;
return img + match;

View file

@ -172,7 +172,7 @@ module.exports.filter = (listId, request, columns, segmentId, callback) => {
};
module.exports.addConfirmation = (list, email, data, callback) => {
module.exports.addConfirmation = (list, email, optInIp, data, callback) => {
let cid = shortid.generate();
tools.validateEmail(email, false, err => {
@ -185,8 +185,8 @@ module.exports.addConfirmation = (list, email, data, callback) => {
return callback(err);
}
let query = 'INSERT INTO confirmations (cid, list, email, data) VALUES (?,?,?,?)';
connection.query(query, [cid, list.id, email, JSON.stringify(data || {})], (err, result) => {
let query = 'INSERT INTO confirmations (cid, list, email, opt_in_ip, data) VALUES (?,?,?,?,?)';
connection.query(query, [cid, list.id, email, optInIp, JSON.stringify(data || {})], (err, result) => {
connection.release();
if (err) {
return callback(err);

View file

@ -1,3 +1,3 @@
{
"schemaVersion": 17
"schemaVersion": 18
}

View file

@ -1,7 +1,7 @@
{
"name": "mailtrain",
"private": true,
"version": "1.17.0",
"version": "1.18.0",
"description": "Self hosted email newsletter app",
"main": "index.js",
"scripts": {

View file

@ -122,7 +122,7 @@ router.post('/subscribe/:listId', (req, res) => {
}
if (/^(yes|true|1)$/i.test(input.REQUIRE_CONFIRMATION)) {
subscriptions.addConfirmation(list, input.EMAIL, subscription, (err, cid) => {
subscriptions.addConfirmation(list, input.EMAIL, req.ip, subscription, (err, cid) => {
if (err) {
log.error('API', err);
res.status(500);

View file

@ -233,7 +233,6 @@ router.get('/edit/:id', passport.csrfProtection, (req, res, next) => {
req.flash('danger', err.message || err);
return res.redirect('/');
}
campaign.mergeTags = mergeTags;
res.render(view, campaign);
});

View file

@ -234,7 +234,7 @@ router.post('/:cid/subscribe', passport.parseForm, passport.csrfProtection, (req
});
data = tools.convertKeys(data);
subscriptions.addConfirmation(list, email, data, (err, confirmCid) => {
subscriptions.addConfirmation(list, email, req.ip, data, (err, confirmCid) => {
if (!err && !confirmCid) {
err = new Error('Could not store confirmation data');
}

View file

@ -0,0 +1,12 @@
# Header section
# Define incrementing schema version number
SET @schema_version = '18';
# Add template field for group elements
ALTER TABLE `campaigns` ADD COLUMN `tracking_disabled` tinyint(4) unsigned NOT NULL DEFAULT '0' AFTER `status`;
ALTER TABLE `confirmations` ADD COLUMN `opt_in_ip` varchar(100) DEFAULT NULL AFTER `email`;
# Footer section
LOCK TABLES `settings` WRITE;
INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
UNLOCK TABLES;

View file

@ -81,6 +81,14 @@
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
<hr />
<div class="form-group">

View file

@ -100,6 +100,15 @@
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
<hr />
<div class="form-group">

View file

@ -100,6 +100,15 @@
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
<hr />
<div class="form-group">

View file

@ -153,6 +153,15 @@
<input type="email" class="form-control" name="address" id="address" value="{{address}}" placeholder="This is the address people will send replies to" required>
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
</fieldset>
<hr />

View file

@ -99,6 +99,15 @@
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
</fieldset>
</div>
<div role="tabpanel" class="tab-pane {{#if showTemplate}}active{{/if}}" id="template">

View file

@ -98,6 +98,14 @@
<input type="text" class="form-control" name="subject" id="subject" value="{{subject}}" placeholder="Keep it relevant and non-spammy" required>
</div>
</div>
<div class="col-sm-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" name="tracking-disabled" value="1" {{#if trackingDisabled}} checked {{/if}}> Disable clicked/opened tracking
</label>
</div>
</div>
</fieldset>
</div>
<div role="tabpanel" class="tab-pane {{#if showTemplate}}active{{/if}}" id="template">

View file

@ -145,6 +145,8 @@
</div>
</dd>
{{#unless trackingDisabled}}
<dt>Opened <a href="/campaigns/opened/{{id}}" title="List subscribers who opened this message"><span class="glyphicon glyphicon-zoom-in" aria-hidden="true"></span></a></dt>
<dd>
<div class="progress">
@ -165,6 +167,7 @@
{{/unless}}
{{/unless}}
{{/unless}}
</dl>
{{#if isNormal}}
@ -321,6 +324,7 @@
</div>
{{#if links}}
{{#unless trackingDisabled}}
<div role="tabpanel" class="tab-pane {{#if showLinks}}active{{/if}}" id="links">
<p></p>
@ -404,6 +408,7 @@
</div>
</div>
{{/unless}}
{{/if}}
</div>