added view for RSS campaigns
This commit is contained in:
parent
09103ac58b
commit
fd0e75da27
10 changed files with 430 additions and 134 deletions
|
@ -32,7 +32,7 @@ user="mailtrain"
|
||||||
password="mailtrain"
|
password="mailtrain"
|
||||||
database="mailtrain"
|
database="mailtrain"
|
||||||
charset="utf8mb4"
|
charset="utf8mb4"
|
||||||
timezone="UTC"
|
timezone="local"
|
||||||
|
|
||||||
[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
|
||||||
|
|
26
lib/feed.js
26
lib/feed.js
|
@ -7,6 +7,7 @@ module.exports.fetch = (url, callback) => {
|
||||||
let req = request(url);
|
let req = request(url);
|
||||||
let feedparser = new FeedParser();
|
let feedparser = new FeedParser();
|
||||||
let returned = false;
|
let returned = false;
|
||||||
|
let entries = [];
|
||||||
|
|
||||||
req.setHeader('user-agent', 'Mailtrain');
|
req.setHeader('user-agent', 'Mailtrain');
|
||||||
req.setHeader('accept', 'text/html,application/xhtml+xml');
|
req.setHeader('accept', 'text/html,application/xhtml+xml');
|
||||||
|
@ -40,17 +41,24 @@ module.exports.fetch = (url, callback) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
feedparser.on('readable', () => {
|
feedparser.on('readable', () => {
|
||||||
// This is where the action is!
|
|
||||||
let meta = feedparser.meta;
|
|
||||||
let item;
|
let item;
|
||||||
|
|
||||||
while ((item = feedparser.read())) {
|
while ((item = feedparser.read())) {
|
||||||
//console.log(require('util').inspect(item, false, 22));
|
let entry = {
|
||||||
console.log(item.title);
|
title: item.title,
|
||||||
console.log(item.description || item.summary);
|
date: item.date || item.pubdate || item.pubDate || new Date(),
|
||||||
console.log('--------');
|
guid: item.guid || item.link,
|
||||||
|
link: item.link,
|
||||||
|
content: item.description || item.summary
|
||||||
|
};
|
||||||
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.fetch('https://andris9.wordpress.com/feed/', console.log);
|
feedparser.on('end', () => {
|
||||||
|
if (returned) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
returned = true;
|
||||||
|
callback(null, entries);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -7,6 +7,9 @@ let templates = require('./templates');
|
||||||
let segments = require('./segments');
|
let segments = require('./segments');
|
||||||
let subscriptions = require('./subscriptions');
|
let subscriptions = require('./subscriptions');
|
||||||
let shortid = require('shortid');
|
let shortid = require('shortid');
|
||||||
|
let isUrl = require('is-url');
|
||||||
|
let feed = require('../feed');
|
||||||
|
let log = require('npmlog');
|
||||||
|
|
||||||
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'];
|
||||||
|
|
||||||
|
@ -32,7 +35,7 @@ module.exports.list = (start, limit, callback) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.filter = (request, callback) => {
|
module.exports.filter = (request, parent, callback) => {
|
||||||
let columns = ['#', 'name', 'description', 'status', 'created'];
|
let columns = ['#', 'name', 'description', 'status', 'created'];
|
||||||
let processQuery = queryData => {
|
let processQuery = queryData => {
|
||||||
|
|
||||||
|
@ -107,11 +110,20 @@ module.exports.filter = (request, callback) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
processQuery({
|
processQuery({
|
||||||
// only find normal campaigns at this point
|
// only find normal and RSS parent campaigns at this point
|
||||||
where: '`type`=?',
|
where: '`parent`=?',
|
||||||
values: [1]
|
values: [parent]
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
processQuery({
|
||||||
|
// only find normal and RSS parent campaigns at this point
|
||||||
|
where: '`type` IN (?,?)',
|
||||||
|
values: [1, 2]
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.getByCid = (cid, callback) => {
|
module.exports.getByCid = (cid, callback) => {
|
||||||
|
@ -215,6 +227,10 @@ module.exports.create = (campaign, callback) => {
|
||||||
return callback(new Error('Campaign Name must be set'));
|
return callback(new Error('Campaign Name must be set'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (campaign.type === 2 && !campaign.sourceUrl || !isUrl(campaign.sourceUrl)) {
|
||||||
|
return callback(new Error('RSS URL must be set'));
|
||||||
|
}
|
||||||
|
|
||||||
lists.get(campaign.list, (err, list) => {
|
lists.get(campaign.list, (err, list) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
@ -223,10 +239,15 @@ module.exports.create = (campaign, callback) => {
|
||||||
return callback(new Error('Selected list not found'));
|
return callback(new Error('Selected list not found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = ['name'];
|
let keys = ['name', 'type'];
|
||||||
let values = [name];
|
let values = [name, campaign.type];
|
||||||
|
|
||||||
let create = () => {
|
if (campaign.type === 2) {
|
||||||
|
keys.push('status');
|
||||||
|
values.push(5); // inactive
|
||||||
|
}
|
||||||
|
|
||||||
|
let create = next => {
|
||||||
Object.keys(campaign).forEach(key => {
|
Object.keys(campaign).forEach(key => {
|
||||||
let value = typeof campaign[key] === 'number' ? campaign[key] : (campaign[key] || '').toString().trim();
|
let value = typeof campaign[key] === 'number' ? campaign[key] : (campaign[key] || '').toString().trim();
|
||||||
key = tools.toDbKey(key);
|
key = tools.toDbKey(key);
|
||||||
|
@ -242,19 +263,19 @@ module.exports.create = (campaign, callback) => {
|
||||||
|
|
||||||
db.getConnection((err, connection) => {
|
db.getConnection((err, connection) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = 'INSERT INTO campaigns (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')';
|
let query = 'INSERT INTO campaigns (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')';
|
||||||
connection.query(query, values, (err, result) => {
|
connection.query(query, values, (err, result) => {
|
||||||
connection.release();
|
connection.release();
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let campaignId = result && result.insertId || false;
|
let campaignId = result && result.insertId || false;
|
||||||
if (!campaignId) {
|
if (!campaignId) {
|
||||||
return callback(null, false);
|
return next(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are going to aqcuire a lot of log info, so we are putting
|
// we are going to aqcuire a lot of log info, so we are putting
|
||||||
|
@ -262,16 +283,55 @@ module.exports.create = (campaign, callback) => {
|
||||||
createCampaignTables(campaignId, err => {
|
createCampaignTables(campaignId, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
// FIXME: rollback
|
// FIXME: rollback
|
||||||
return callback(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
return callback(null, campaignId);
|
return next(null, campaignId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (campaign.type === 2) {
|
if (campaign.type === 2) {
|
||||||
create();
|
feed.fetch(campaign.sourceUrl, (err, entries) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
create((err, campaignId) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
if (!campaignId || !entries.length) {
|
||||||
|
return callback(null, campaignId);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// store references to already existing feed entries
|
||||||
|
// this is needed to detect new entries
|
||||||
|
let query = 'INSERT IGNORE INTO `rss` (`parent`,`guid`,`pubdate`) VALUES ' + entries.map(() => '(?,?,?)').join(',');
|
||||||
|
|
||||||
|
values = [];
|
||||||
|
entries.forEach(entry => {
|
||||||
|
values.push(campaignId, entry.guid, entry.date);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.query(query, values, err => {
|
||||||
|
connection.release();
|
||||||
|
if (err) {
|
||||||
|
// too late to report as failed
|
||||||
|
log.error('RSS', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, campaignId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
} else if (campaign.template) {
|
} else if (campaign.template) {
|
||||||
templates.get(campaign.template, (err, template) => {
|
templates.get(campaign.template, (err, template) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -284,10 +344,11 @@ module.exports.create = (campaign, callback) => {
|
||||||
keys = keys.concat(['html', 'text']);
|
keys = keys.concat(['html', 'text']);
|
||||||
values = values.concat([template.html, template.text]);
|
values = values.concat([template.html, template.text]);
|
||||||
|
|
||||||
create();
|
create(callback);
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
create();
|
return create(callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -343,11 +404,53 @@ module.exports.update = (id, updates, callback) => {
|
||||||
values.push(id);
|
values.push(id);
|
||||||
|
|
||||||
connection.query('UPDATE campaigns SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
|
connection.query('UPDATE campaigns SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
|
||||||
connection.release();
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
connection.release();
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
return callback(null, result && result.affectedRows || false);
|
let affected = result && result.affectedRows || false;
|
||||||
|
|
||||||
|
if (!affected) {
|
||||||
|
connection.release();
|
||||||
|
return callback(null, affected);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.query('SELECT `type`, `source_url` FROM campaigns WHERE id=? LIMIT 1', [id], (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
connection.release();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows || !rows[0] || rows[0].type !== 2) {
|
||||||
|
// if not RSS, then nothing to do here
|
||||||
|
connection.release();
|
||||||
|
return callback(null, affected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update seen rss entries to avoid sending old entries to subscribers
|
||||||
|
feed.fetch(rows[0].source_url, (err, entries) => {
|
||||||
|
if (err) {
|
||||||
|
connection.release();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = 'INSERT IGNORE INTO `rss` (`parent`,`guid`,`pubdate`) VALUES ' + entries.map(() => '(?,?,?)').join(',');
|
||||||
|
|
||||||
|
values = [];
|
||||||
|
entries.forEach(entry => {
|
||||||
|
values.push(id, entry.guid, entry.date);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.query(query, values, err => {
|
||||||
|
connection.release();
|
||||||
|
if (err) {
|
||||||
|
// too late to report as failed
|
||||||
|
log.error('RSS', err);
|
||||||
|
}
|
||||||
|
return callback(null, affected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -498,6 +601,60 @@ module.exports.reset = (id, callback) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.activate = (id, callback) => {
|
||||||
|
module.exports.get(id, false, (err, campaign) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (campaign.status !== 5) {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// campaigns marked as status=5 are paused
|
||||||
|
connection.query('UPDATE campaigns SET `status`=6, `status_change`=NOW() WHERE id=? LIMIT 1', [id], err => {
|
||||||
|
if (err) {
|
||||||
|
connection.release();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.inactivate = (id, callback) => {
|
||||||
|
module.exports.get(id, false, (err, campaign) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (campaign.status !== 6) {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// campaigns marked as status=6 are paused
|
||||||
|
connection.query('UPDATE campaigns SET `status`=5, `status_change`=NOW() WHERE id=? LIMIT 1', [id], err => {
|
||||||
|
if (err) {
|
||||||
|
connection.release();
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.getMail = (campaignId, listId, subscriptionId, callback) => {
|
module.exports.getMail = (campaignId, listId, subscriptionId, callback) => {
|
||||||
campaignId = Number(campaignId) || 0;
|
campaignId = Number(campaignId) || 0;
|
||||||
listId = Number(listId) || 0;
|
listId = Number(listId) || 0;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"schemaVersion": 7
|
"schemaVersion": 8
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
"hbs": "^4.0.0",
|
"hbs": "^4.0.0",
|
||||||
"html-to-text": "^2.1.0",
|
"html-to-text": "^2.1.0",
|
||||||
"humanize": "0.0.9",
|
"humanize": "0.0.9",
|
||||||
|
"is-url": "^1.2.1",
|
||||||
"isemail": "^2.1.0",
|
"isemail": "^2.1.0",
|
||||||
"moment-timezone": "^0.5.3",
|
"moment-timezone": "^0.5.3",
|
||||||
"morgan": "^1.7.0",
|
"morgan": "^1.7.0",
|
||||||
|
|
|
@ -196,7 +196,7 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/ajax', (req, res) => {
|
router.post('/ajax', (req, res) => {
|
||||||
campaigns.filter(req.body, (err, data, total, filteredTotal) => {
|
campaigns.filter(req.body, Number(req.query.parent) || false, (err, data, total, filteredTotal) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.json({
|
return res.json({
|
||||||
error: err.message || err,
|
error: err.message || err,
|
||||||
|
@ -217,6 +217,10 @@ router.post('/ajax', (req, res) => {
|
||||||
return 'Finished';
|
return 'Finished';
|
||||||
case 4:
|
case 4:
|
||||||
return 'Paused';
|
return 'Paused';
|
||||||
|
case 5:
|
||||||
|
return 'Inactive';
|
||||||
|
case 6:
|
||||||
|
return 'Active';
|
||||||
}
|
}
|
||||||
return 'Other';
|
return 'Other';
|
||||||
};
|
};
|
||||||
|
@ -256,6 +260,11 @@ router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
||||||
campaign.isSending = campaign.status === 2;
|
campaign.isSending = campaign.status === 2;
|
||||||
campaign.isFinished = campaign.status === 3;
|
campaign.isFinished = campaign.status === 3;
|
||||||
campaign.isPaused = campaign.status === 4;
|
campaign.isPaused = campaign.status === 4;
|
||||||
|
campaign.isInactive = campaign.status === 5;
|
||||||
|
campaign.isActive = campaign.status === 6;
|
||||||
|
|
||||||
|
campaign.isNormal = campaign.type === 1;
|
||||||
|
campaign.isRss = campaign.type === 2;
|
||||||
|
|
||||||
campaign.isScheduled = campaign.scheduled && campaign.scheduled > new Date();
|
campaign.isScheduled = campaign.scheduled && campaign.scheduled > new Date();
|
||||||
|
|
||||||
|
@ -341,4 +350,32 @@ router.post('/pause', passport.parseForm, passport.csrfProtection, (req, res) =>
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/activate', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
|
campaigns.activate(req.body.id, (err, reset) => {
|
||||||
|
if (err) {
|
||||||
|
req.flash('danger', err && err.message || err);
|
||||||
|
} else if (reset) {
|
||||||
|
req.flash('success', 'Sending activated');
|
||||||
|
} else {
|
||||||
|
req.flash('info', 'Could not activate sending');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/inactivate', passport.parseForm, passport.csrfProtection, (req, res) => {
|
||||||
|
campaigns.inactivate(req.body.id, (err, reset) => {
|
||||||
|
if (err) {
|
||||||
|
req.flash('danger', err && err.message || err);
|
||||||
|
} else if (reset) {
|
||||||
|
req.flash('success', 'Sending paused');
|
||||||
|
} else {
|
||||||
|
req.flash('info', 'Could not pause sending');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.redirect('/campaigns/view/' + encodeURIComponent(req.body.id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
24
setup/sql/upgrade-00008.sql
Normal file
24
setup/sql/upgrade-00008.sql
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Header section
|
||||||
|
# Define incrementing schema version number
|
||||||
|
SET @schema_version = '8';
|
||||||
|
|
||||||
|
# Create new table to store RSS entries for RSS campaigns
|
||||||
|
CREATE TABLE `rss` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`parent` int(11) unsigned NOT NULL,
|
||||||
|
`guid` varchar(255) NOT NULL DEFAULT '',
|
||||||
|
`pubdate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||||
|
`campaign` int(11) unsigned DEFAULT NULL,
|
||||||
|
`found` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `parent_2` (`parent`,`guid`),
|
||||||
|
KEY `parent` (`parent`),
|
||||||
|
CONSTRAINT `rss_ibfk_1` FOREIGN KEY (`parent`) REFERENCES `campaigns` (`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
ALTER TABLE `campaigns` ADD COLUMN `parent` int(11) unsigned DEFAULT NULL AFTER `type`;
|
||||||
|
CREATE INDEX parent_index ON `campaigns` (`parent`);
|
||||||
|
|
||||||
|
# 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;
|
|
@ -60,7 +60,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
||||||
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php">
|
<input type="url" class="form-control" name="source-url" id="source-url" value="{{sourceUrl}}" placeholder="http://example.com/rss.php" required>
|
||||||
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
<span class="help-block">New entries from this RSS URL are sent out to list subscribers as email messages</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,6 +39,11 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
|
{{#if isRss}}
|
||||||
|
<dt>Feed URL</dt>
|
||||||
|
<dd><a href="{{sourceUrl}}">{{sourceUrl}}</a></dd>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<dt>Email "from name"</dt>
|
<dt>Email "from name"</dt>
|
||||||
<dd>{{from}}</dd>
|
<dd>{{from}}</dd>
|
||||||
|
|
||||||
|
@ -48,6 +53,8 @@
|
||||||
<dt>Email "subject line"</dt>
|
<dt>Email "subject line"</dt>
|
||||||
<dd>{{subject}}</dd>
|
<dd>{{subject}}</dd>
|
||||||
|
|
||||||
|
{{#if isNormal}}
|
||||||
|
|
||||||
{{#unless isIdling}}
|
{{#unless isIdling}}
|
||||||
<dt>Delivered</dt>
|
<dt>Delivered</dt>
|
||||||
<dd>{{delivered}}</dd>
|
<dd>{{delivered}}</dd>
|
||||||
|
@ -80,8 +87,11 @@
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
{{#if isNormal}}
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{#if isIdling}}
|
{{#if isIdling}}
|
||||||
|
@ -181,3 +191,62 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isRss}}
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
{{#if isActive}}
|
||||||
|
<div class="pull-right">
|
||||||
|
<form id="inactivate-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would pause sending new entries in RSS feed as email messages to the selected list" method="post" action="/campaigns/inactivate">
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<button type="submit" form="inactivate-sending" class="btn btn-warning"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Campaign status: <span class="label label-primary">ACTIVE</span>
|
||||||
|
{{else}}
|
||||||
|
<div class="pull-right">
|
||||||
|
<form id="activate-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would start sending new entries in RSS feed as email messages to the selected list" method="post" action="/campaigns/activate">
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
<input type="hidden" name="id" value="{{id}}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<button type="submit" form="activate-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Activate</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Campaign status: <span class="label label-default">INACTIVE</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table data-topic-url="/campaigns" data-sort-column="4" data-sort-order="desc" class="table table-bordered table-hover data-table-ajax display nowrap" data-topic-args="parent={{id}}" width="100%" data-row-sort="0,1,0,1,1,0">
|
||||||
|
<thead>
|
||||||
|
<th class="col-md-1">
|
||||||
|
#
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Created
|
||||||
|
</th>
|
||||||
|
<th class="col-md-1">
|
||||||
|
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue