added view for RSS campaigns

This commit is contained in:
Andris Reinman 2016-05-03 12:36:06 +03:00
parent 09103ac58b
commit fd0e75da27
10 changed files with 430 additions and 134 deletions

View file

@ -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

View file

@ -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);
});
};

View file

@ -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) => {
}); });
}; };
processQuery({ if (parent) {
// only find normal campaigns at this point processQuery({
where: '`type`=?', // only find normal and RSS parent campaigns at this point
values: [1] where: '`parent`=?',
}); 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;

View file

@ -1,3 +1,3 @@
{ {
"schemaVersion": 7 "schemaVersion": 8
} }

View file

@ -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",

View file

@ -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;

View 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;

View file

@ -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>

View file

@ -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>

View file

@ -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,136 +53,200 @@
<dt>Email "subject line"</dt> <dt>Email "subject line"</dt>
<dd>{{subject}}</dd> <dd>{{subject}}</dd>
{{#unless isIdling}} {{#if isNormal}}
<dt>Delivered</dt>
<dd>{{delivered}}</dd>
<dt>Bounced</dt> {{#unless isIdling}}
<dd>{{bounced}}</dd> <dt>Delivered</dt>
<dd>{{delivered}}</dd>
<dt>Complaints</dt> <dt>Bounced</dt>
<dd>{{complained}}</dd> <dd>{{bounced}}</dd>
<dt>Unsubscribed</dt> <dt>Complaints</dt>
<dd>{{unsubscribed}}</dd> <dd>{{complained}}</dd>
<dt>Opened</dt> <dt>Unsubscribed</dt>
<dd> <dd>{{unsubscribed}}</dd>
<div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{openRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em; width: {{openRate}}%;"> <dt>Opened</dt>
{{openRate}}% <dd>
<div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{openRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em; width: {{openRate}}%;">
{{openRate}}%
</div>
</div> </div>
</div> </dd>
</dd>
<dt>Clicked</dt> <dt>Clicked</dt>
<dd> <dd>
<div class="progress"> <div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{clicksRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em; width: {{clicksRate}}%;"> <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{clicksRate}}" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em; width: {{clicksRate}}%;">
{{clicksRate}}% {{clicksRate}}%
</div>
</div> </div>
</div> </dd>
</dd>
{{/unless}} {{/unless}}
{{/if}}
</dl> </dl>
<div class="panel panel-default"> {{#if isNormal}}
<div class="panel-body">
{{#if isIdling}}
<form class="form-inline confirm-submit" data-confirm-message="Are you sure? This action would start sending messages to the selected list" method="post" action="/campaigns/send">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
<div class="pull-right"> <div class="panel panel-default">
<div class="form-group"> <div class="panel-body">
<p class="form-control-static">Delay sending</p> {{#if isIdling}}
</div> <form class="form-inline confirm-submit" data-confirm-message="Are you sure? This action would start sending messages to the selected list" method="post" action="/campaigns/send">
<div class="form-group"> <input type="hidden" name="_csrf" value="{{csrfToken}}">
<div class="input-group"> <input type="hidden" name="id" value="{{id}}" />
<input type="number" class="form-control" name="delay-hours" id="delay-hours" placeholder="0">
<div class="input-group-addon"> hours</div> <div class="pull-right">
<div class="form-group">
<p class="form-control-static">Delay sending</p>
</div>
<div class="form-group">
<div class="input-group">
<input type="number" class="form-control" name="delay-hours" id="delay-hours" placeholder="0">
<div class="input-group-addon"> hours</div>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="number" class="form-control" name="delay-minutes" id="delay-minutes" placeholder="0">
<div class="input-group-addon"> minutes</div>
</div>
</div> </div>
</div> </div>
<div class="form-group">
<div class="input-group"> <button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> Send to
<input type="number" class="form-control" name="delay-minutes" id="delay-minutes" placeholder="0"> {{#if segment}}
<div class="input-group-addon"> minutes</div> {{segment.subscribers}}
</div> {{else}}
{{list.subscribers}}
{{/if}} subscribers</button>
</form>
{{/if}}
{{#if isSending}}
<!-- Indicate that this page needs refreshing after 20s. -->
<div class="page-refresh" data-interval="20"></div>
{{#if isScheduled}}
<div class="pull-right">
<form class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would reset scheduling" method="post" action="/campaigns/reset">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Cancel</a>
</button>
</form>
</div> </div>
</div> <h4>Sending scheduled <span class="datestring text-info" data-date="{{scheduled}}" title="{{scheduled}}">{{scheduled}}</span></h4>
{{else}}
<div class="pull-right">
<form class="form-horizontal" method="post" action="/campaigns/pause">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> Send to <button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause</a>
{{#if segment}} </button>
{{segment.subscribers}} </form>
{{else}} </div>
{{list.subscribers}} <h4>Sending…</h4>
{{/if}} subscribers</button> {{/if}}
</form> {{/if}}
{{/if}}
{{#if isSending}} {{#if isPaused}}
<!-- Indicate that this page needs refreshing after 20s. -->
<div class="page-refresh" data-interval="20"></div>
{{#if isScheduled}}
<div class="pull-right"> <div class="pull-right">
<form class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would reset scheduling" method="post" action="/campaigns/reset"> <form class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/resume">
<input type="hidden" name="_csrf" value="{{csrfToken}}"> <input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" /> <input type="hidden" name="id" value="{{id}}" />
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Cancel</a> <button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Resume</a>
</button> </button>
</form> </form>
</div> </div>
<h4>Sending scheduled <span class="datestring text-info" data-date="{{scheduled}}" title="{{scheduled}}">{{scheduled}}</span></h4> <h4>Sending paused</h4>
{{/if}}
{{#if isFinished}}
<div class="pull-right">
<form id="continue-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/send">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
</form>
<form id="reset-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would reset all stats about current progress" method="post" action="/campaigns/reset">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
</form>
<button type="submit" form="continue-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Continue</a>
</button>
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Reset</a>
</button>
</div>
<h4>All messages sent! Hit "Continue" if you you want to send this campaign to new subscribers</h4>
{{/if}}
</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}} {{else}}
<div class="pull-right"> <div class="pull-right">
<form class="form-horizontal" method="post" action="/campaigns/pause"> <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="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" /> <input type="hidden" name="id" value="{{id}}" />
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause</a>
</button>
</form> </form>
</div>
<h4>Sending…</h4>
{{/if}}
{{/if}}
{{#if isPaused}} <button type="submit" form="activate-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Activate</a>
<div class="pull-right">
<form class="form-horizontal confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/resume">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Resume</a>
</button> </button>
</form> </div>
</div>
<h4>Sending paused</h4>
{{/if}}
{{#if isFinished}}
<div class="pull-right">
<form id="continue-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would resume sending messages to the selected list" method="post" action="/campaigns/send">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
</form>
<form id="reset-sending" class="confirm-submit" data-confirm-message="Are you sure? This action would reset all stats about current progress" method="post" action="/campaigns/reset">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="hidden" name="id" value="{{id}}" />
</form>
<button type="submit" form="continue-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Continue</a>
</button>
<button type="submit" form="reset-sending" class="btn btn-danger"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Reset</a>
</button>
</div>
<h4>All messages sent! Hit "Continue" if you you want to send this campaign to new subscribers</h4>
{{/if}}
Campaign status: <span class="label label-default">INACTIVE</span>
{{/if}}
</div>
</div> </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">
&nbsp;
</th>
</thead>
</table>
</div>
{{/if}}