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"
|
||||
database="mailtrain"
|
||||
charset="utf8mb4"
|
||||
timezone="UTC"
|
||||
timezone="local"
|
||||
|
||||
[redis]
|
||||
# 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 feedparser = new FeedParser();
|
||||
let returned = false;
|
||||
let entries = [];
|
||||
|
||||
req.setHeader('user-agent', 'Mailtrain');
|
||||
req.setHeader('accept', 'text/html,application/xhtml+xml');
|
||||
|
@ -40,17 +41,24 @@ module.exports.fetch = (url, callback) => {
|
|||
});
|
||||
|
||||
feedparser.on('readable', () => {
|
||||
// This is where the action is!
|
||||
let meta = feedparser.meta;
|
||||
let item;
|
||||
|
||||
while ((item = feedparser.read())) {
|
||||
//console.log(require('util').inspect(item, false, 22));
|
||||
console.log(item.title);
|
||||
console.log(item.description || item.summary);
|
||||
console.log('--------');
|
||||
let entry = {
|
||||
title: item.title,
|
||||
date: item.date || item.pubdate || item.pubDate || new Date(),
|
||||
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 subscriptions = require('./subscriptions');
|
||||
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'];
|
||||
|
||||
|
@ -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 processQuery = queryData => {
|
||||
|
||||
|
@ -107,11 +110,20 @@ module.exports.filter = (request, callback) => {
|
|||
});
|
||||
};
|
||||
|
||||
processQuery({
|
||||
// only find normal campaigns at this point
|
||||
where: '`type`=?',
|
||||
values: [1]
|
||||
});
|
||||
if (parent) {
|
||||
processQuery({
|
||||
// only find normal and RSS parent campaigns at this point
|
||||
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) => {
|
||||
|
@ -215,6 +227,10 @@ module.exports.create = (campaign, callback) => {
|
|||
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) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
@ -223,10 +239,15 @@ module.exports.create = (campaign, callback) => {
|
|||
return callback(new Error('Selected list not found'));
|
||||
}
|
||||
|
||||
let keys = ['name'];
|
||||
let values = [name];
|
||||
let keys = ['name', 'type'];
|
||||
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 => {
|
||||
let value = typeof campaign[key] === 'number' ? campaign[key] : (campaign[key] || '').toString().trim();
|
||||
key = tools.toDbKey(key);
|
||||
|
@ -242,19 +263,19 @@ module.exports.create = (campaign, callback) => {
|
|||
|
||||
db.getConnection((err, connection) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
return next(err);
|
||||
}
|
||||
|
||||
let query = 'INSERT INTO campaigns (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')';
|
||||
connection.query(query, values, (err, result) => {
|
||||
connection.release();
|
||||
if (err) {
|
||||
return callback(err);
|
||||
return next(err);
|
||||
}
|
||||
|
||||
let campaignId = result && result.insertId || false;
|
||||
if (!campaignId) {
|
||||
return callback(null, false);
|
||||
return next(null, false);
|
||||
}
|
||||
|
||||
// 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 => {
|
||||
if (err) {
|
||||
// FIXME: rollback
|
||||
return callback(err);
|
||||
return next(err);
|
||||
}
|
||||
return callback(null, campaignId);
|
||||
return next(null, campaignId);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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) {
|
||||
templates.get(campaign.template, (err, template) => {
|
||||
if (err) {
|
||||
|
@ -284,10 +344,11 @@ module.exports.create = (campaign, callback) => {
|
|||
keys = keys.concat(['html', 'text']);
|
||||
values = values.concat([template.html, template.text]);
|
||||
|
||||
create();
|
||||
create(callback);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
create();
|
||||
return create(callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -343,11 +404,53 @@ module.exports.update = (id, updates, callback) => {
|
|||
values.push(id);
|
||||
|
||||
connection.query('UPDATE campaigns SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
|
||||
connection.release();
|
||||
if (err) {
|
||||
connection.release();
|
||||
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) => {
|
||||
campaignId = Number(campaignId) || 0;
|
||||
listId = Number(listId) || 0;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"schemaVersion": 7
|
||||
"schemaVersion": 8
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"hbs": "^4.0.0",
|
||||
"html-to-text": "^2.1.0",
|
||||
"humanize": "0.0.9",
|
||||
"is-url": "^1.2.1",
|
||||
"isemail": "^2.1.0",
|
||||
"moment-timezone": "^0.5.3",
|
||||
"morgan": "^1.7.0",
|
||||
|
|
|
@ -196,7 +196,7 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (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) {
|
||||
return res.json({
|
||||
error: err.message || err,
|
||||
|
@ -217,6 +217,10 @@ router.post('/ajax', (req, res) => {
|
|||
return 'Finished';
|
||||
case 4:
|
||||
return 'Paused';
|
||||
case 5:
|
||||
return 'Inactive';
|
||||
case 6:
|
||||
return 'Active';
|
||||
}
|
||||
return 'Other';
|
||||
};
|
||||
|
@ -256,6 +260,11 @@ router.get('/view/:id', passport.csrfProtection, (req, res) => {
|
|||
campaign.isSending = campaign.status === 2;
|
||||
campaign.isFinished = campaign.status === 3;
|
||||
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();
|
||||
|
||||
|
@ -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;
|
||||
|
|
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">
|
||||
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
<div class="form-group">
|
||||
<label for="template" class="col-sm-2 control-label">RSS Feed Url</label>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
{{/if}}
|
||||
</dd>
|
||||
|
||||
{{#if isRss}}
|
||||
<dt>Feed URL</dt>
|
||||
<dd><a href="{{sourceUrl}}">{{sourceUrl}}</a></dd>
|
||||
{{/if}}
|
||||
|
||||
<dt>Email "from name"</dt>
|
||||
<dd>{{from}}</dd>
|
||||
|
||||
|
@ -48,136 +53,200 @@
|
|||
<dt>Email "subject line"</dt>
|
||||
<dd>{{subject}}</dd>
|
||||
|
||||
{{#unless isIdling}}
|
||||
<dt>Delivered</dt>
|
||||
<dd>{{delivered}}</dd>
|
||||
{{#if isNormal}}
|
||||
|
||||
<dt>Bounced</dt>
|
||||
<dd>{{bounced}}</dd>
|
||||
{{#unless isIdling}}
|
||||
<dt>Delivered</dt>
|
||||
<dd>{{delivered}}</dd>
|
||||
|
||||
<dt>Complaints</dt>
|
||||
<dd>{{complained}}</dd>
|
||||
<dt>Bounced</dt>
|
||||
<dd>{{bounced}}</dd>
|
||||
|
||||
<dt>Unsubscribed</dt>
|
||||
<dd>{{unsubscribed}}</dd>
|
||||
<dt>Complaints</dt>
|
||||
<dd>{{complained}}</dd>
|
||||
|
||||
<dt>Opened</dt>
|
||||
<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}}%
|
||||
<dt>Unsubscribed</dt>
|
||||
<dd>{{unsubscribed}}</dd>
|
||||
|
||||
<dt>Opened</dt>
|
||||
<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>
|
||||
</dd>
|
||||
</dd>
|
||||
|
||||
<dt>Clicked</dt>
|
||||
<dd>
|
||||
<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}}%;">
|
||||
{{clicksRate}}%
|
||||
<dt>Clicked</dt>
|
||||
<dd>
|
||||
<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}}%;">
|
||||
{{clicksRate}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dd>
|
||||
</dd>
|
||||
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
</dl>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<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}}" />
|
||||
{{#if isNormal}}
|
||||
|
||||
<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 class="panel panel-default">
|
||||
<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="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 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>
|
||||
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> Send to
|
||||
{{#if segment}}
|
||||
{{segment.subscribers}}
|
||||
{{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>
|
||||
<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
|
||||
{{#if segment}}
|
||||
{{segment.subscribers}}
|
||||
{{else}}
|
||||
{{list.subscribers}}
|
||||
{{/if}} subscribers</button>
|
||||
</form>
|
||||
{{/if}}
|
||||
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause</a>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<h4>Sending…</h4>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if isSending}}
|
||||
<!-- Indicate that this page needs refreshing after 20s. -->
|
||||
<div class="page-refresh" data-interval="20"></div>
|
||||
{{#if isScheduled}}
|
||||
{{#if isPaused}}
|
||||
<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="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>
|
||||
</form>
|
||||
</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}}
|
||||
<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="id" value="{{id}}" />
|
||||
|
||||
<button type="submit" class="btn btn-info"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span> Pause</a>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<h4>Sending…</h4>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if isPaused}}
|
||||
<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 type="submit" form="activate-sending" class="btn btn-info"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Activate</a>
|
||||
</button>
|
||||
</form>
|
||||
</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}}
|
||||
</div>
|
||||
|
||||
Campaign status: <span class="label label-default">INACTIVE</span>
|
||||
{{/if}}
|
||||
</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">
|
||||
|
||||
</th>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue