307 lines
11 KiB
JavaScript
307 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
const express = require('express');
|
|
const passport = require('../lib/passport');
|
|
const router = new express.Router();
|
|
const _ = require('../lib/translate')._;
|
|
const reportTemplates = require('../lib/models/report-templates');
|
|
const tools = require('../lib/tools');
|
|
const util = require('util');
|
|
const htmlescape = require('escape-html');
|
|
const striptags = require('striptags');
|
|
|
|
const allowedMimeTypes = {
|
|
'text/html': 'HTML',
|
|
'text/csv': 'CSV'
|
|
};
|
|
|
|
router.all('/*', (req, res, next) => {
|
|
if (!req.user) {
|
|
req.flash('danger', _('Need to be logged in to access restricted content'));
|
|
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
|
}
|
|
res.setSelectedMenu('reports');
|
|
next();
|
|
});
|
|
|
|
router.get('/', (req, res) => {
|
|
res.render('report-templates/report-templates', {
|
|
title: _('Report Templates')
|
|
});
|
|
});
|
|
|
|
router.post('/ajax', (req, res) => {
|
|
reportTemplates.filter(req.body, (err, data, total, filteredTotal) => {
|
|
if (err) {
|
|
return res.json({
|
|
error: err.message || err,
|
|
data: []
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
draw: req.body.draw,
|
|
recordsTotal: total,
|
|
recordsFiltered: filteredTotal,
|
|
data: data.map((row, i) => [
|
|
(Number(req.body.start) || 0) + 1 + i,
|
|
htmlescape(row.name || ''),
|
|
htmlescape(striptags(row.description) || ''),
|
|
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>',
|
|
'<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/report-templates/edit/' + row.id + '"> ' + _('Edit') + '</a>']
|
|
)
|
|
});
|
|
});
|
|
});
|
|
|
|
router.get('/create', passport.csrfProtection, (req, res) => {
|
|
const data = req.query;
|
|
const wizard = req.query['type'] || '';
|
|
|
|
if (wizard == 'subscribers-all') {
|
|
if (!('description' in data)) data.description = 'Generates a campaign report listing all subscribers along with their statistics.';
|
|
|
|
if (!('mimeType' in data)) data.mimeType = 'text/html';
|
|
|
|
if (!('userFields' in data)) data.userFields =
|
|
'[\n' +
|
|
' {\n' +
|
|
' "id": "campaign",\n' +
|
|
' "name": "Campaign",\n' +
|
|
' "type": "campaign",\n' +
|
|
' "minOccurences": 1,\n' +
|
|
' "maxOccurences": 1\n' +
|
|
' }\n' +
|
|
']';
|
|
|
|
if (!('js' in data)) data.js =
|
|
'campaigns.results(inputs.campaign, ["*"], "", (err, results) => {\n' +
|
|
' if (err) {\n' +
|
|
' return callback(err);\n' +
|
|
' }\n' +
|
|
'\n' +
|
|
' const data = {\n' +
|
|
' results: results\n' +
|
|
' };\n' +
|
|
'\n' +
|
|
' return callback(null, data);\n' +
|
|
'});';
|
|
|
|
if (!('hbs' in data)) data.hbs =
|
|
'<h2>{{title}}</h2>\n' +
|
|
'\n' +
|
|
'<div class="table-responsive">\n' +
|
|
' <table class="table table-bordered table-hover data-table display nowrap" width="100%" data-row-sort="1,1" data-paging="false">\n' +
|
|
' <thead>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}Email{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}Tracker Count{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' </thead>\n' +
|
|
' {{#if results}}\n' +
|
|
' <tbody>\n' +
|
|
' {{#each results}}\n' +
|
|
' <tr>\n' +
|
|
' <th scope="row">\n' +
|
|
' {{email}}\n' +
|
|
' </th>\n' +
|
|
' <td style="width: 20%;">\n' +
|
|
' {{tracker_count}}\n' +
|
|
' </td>\n' +
|
|
' </tr>\n' +
|
|
' {{/each}}\n' +
|
|
' </tbody>\n' +
|
|
' {{/if}}\n' +
|
|
' </table>\n' +
|
|
'</div>';
|
|
|
|
} else if (wizard == 'subscribers-grouped') {
|
|
if (!('description' in data)) data.description = 'Generates a campaign report with results are aggregated by some "Country" custom field.';
|
|
|
|
if (!('mimeType' in data)) data.mimeType = 'text/html';
|
|
|
|
if (!('userFields' in data)) data.userFields =
|
|
'[\n' +
|
|
' {\n' +
|
|
' "id": "campaign",\n' +
|
|
' "name": "Campaign",\n' +
|
|
' "type": "campaign",\n' +
|
|
' "minOccurences": 1,\n' +
|
|
' "maxOccurences": 1\n' +
|
|
' }\n' +
|
|
']';
|
|
|
|
if (!('js' in data)) data.js =
|
|
'campaigns.results(inputs.campaign, ["custom_country", "count(*) AS count_all", "SUM(IF(tracker.count IS NULL, 0, 1)) AS count_opened"], "GROUP BY custom_country", (err, results) => {\n' +
|
|
' if (err) {\n' +
|
|
' return callback(err);\n' +
|
|
' }\n' +
|
|
'\n' +
|
|
' for (let row of results) {\n' +
|
|
' row["percentage"] = Math.round((row.count_opened / row.count_all) * 100);\n' +
|
|
' }\n' +
|
|
'\n' +
|
|
' let data = {\n' +
|
|
' results: results\n' +
|
|
' };\n' +
|
|
'\n' +
|
|
' return callback(null, data);\n' +
|
|
'});';
|
|
|
|
if (!('hbs' in data)) data.hbs =
|
|
'<h2>{{title}}</h2>\n' +
|
|
'\n' +
|
|
'<div class="table-responsive">\n' +
|
|
' <table class="table table-bordered table-hover data-table display nowrap" width="100%" data-row-sort="1,1,1,1" data-paging="false">\n' +
|
|
' <thead>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}Country{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}Opened{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}All{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' <th>\n' +
|
|
' {{#translate}}Percentage{{/translate}}\n' +
|
|
' </th>\n' +
|
|
' </thead>\n' +
|
|
' {{#if results}}\n' +
|
|
' <tbody>\n' +
|
|
' {{#each results}}\n' +
|
|
' <tr>\n' +
|
|
' <th scope="row">\n' +
|
|
' {{custom_country}}\n' +
|
|
' </th>\n' +
|
|
' <td style="width: 20%;">\n' +
|
|
' {{count_opened}}\n' +
|
|
' </td>\n' +
|
|
' <td style="width: 20%;">\n' +
|
|
' {{count_all}}\n' +
|
|
' </td>\n' +
|
|
' <td style="width: 20%;">\n' +
|
|
' {{percentage}}%\n' +
|
|
' </td>\n' +
|
|
' </tr>\n' +
|
|
' {{/each}}\n' +
|
|
' </tbody>\n' +
|
|
' {{/if}}\n' +
|
|
' </table>\n' +
|
|
'</div>';
|
|
|
|
} else if (wizard == 'export-list-csv') {
|
|
if (!('description' in data)) data.description = 'Exports a list as a CSV file.';
|
|
|
|
if (!('mimeType' in data)) data.mimeType = 'text/csv';
|
|
|
|
if (!('userFields' in data)) data.userFields =
|
|
'[\n' +
|
|
' {\n' +
|
|
' "id": "list",\n' +
|
|
' "name": "List",\n' +
|
|
' "type": "list",\n' +
|
|
' "minOccurences": 1,\n' +
|
|
' "maxOccurences": 1\n' +
|
|
' }\n' +
|
|
']';
|
|
|
|
if (!('js' in data)) data.js =
|
|
'subscriptions.list(inputs.list.id,0,0, (err, results) => {\n' +
|
|
' if (err) {\n' +
|
|
' return callback(err);\n' +
|
|
' }\n' +
|
|
'\n' +
|
|
' let data = {\n' +
|
|
' results: results\n' +
|
|
' };\n' +
|
|
'\n' +
|
|
' return callback(null, data);\n' +
|
|
'});';
|
|
|
|
if (!('hbs' in data)) data.hbs =
|
|
'{{#each results}}\n' +
|
|
'{{firstName}},{{lastName}},{{email}}\n' +
|
|
'{{/each}}';
|
|
}
|
|
|
|
data.csrfToken = req.csrfToken();
|
|
data.title = _('Create Report Template');
|
|
data.useEditor = true;
|
|
|
|
data.mimeTypes = Object.keys(allowedMimeTypes).map(key => ({
|
|
key: key,
|
|
value: allowedMimeTypes[key],
|
|
selected: data.mimeType == key
|
|
}));
|
|
|
|
res.render('report-templates/create', data);
|
|
});
|
|
|
|
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
|
|
reportTemplates.createOrUpdate(true, req.body, (err, id) => {
|
|
if (err || !id) {
|
|
req.flash('danger', err && err.message || err || _('Could not create report template'));
|
|
return res.redirect('/report-templates/create?' + tools.queryParams(req.body));
|
|
}
|
|
req.flash('success', util.format(_('Report template “%s” created'), req.body.name));
|
|
res.redirect('/report-templates');
|
|
});
|
|
});
|
|
|
|
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
|
|
reportTemplates.get(req.params.id, (err, template) => {
|
|
if (err || !template) {
|
|
req.flash('danger', err && err.message || err || _('Could not find report template with specified ID'));
|
|
return res.redirect('/report-templates');
|
|
}
|
|
|
|
template.csrfToken = req.csrfToken();
|
|
template.title = _('Edit Report Template');
|
|
template.useEditor = true;
|
|
|
|
template.mimeTypes = Object.keys(allowedMimeTypes).map(key => ({
|
|
key: key,
|
|
value: allowedMimeTypes[key],
|
|
selected: template.mimeType == key
|
|
}));
|
|
|
|
res.render('report-templates/edit', template);
|
|
});
|
|
});
|
|
|
|
router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) => {
|
|
reportTemplates.createOrUpdate(false, req.body, (err, updated) => {
|
|
if (err) {
|
|
req.flash('danger', err.message || err);
|
|
} else if (updated) {
|
|
req.flash('success', _('Report template updated'));
|
|
} else {
|
|
req.flash('info', _('Report template not updated'));
|
|
}
|
|
|
|
if (req.body['submit'] == 'update-and-stay') {
|
|
return res.redirect('/report-templates/edit/' + req.body.id);
|
|
} else {
|
|
return res.redirect('/report-templates');
|
|
}
|
|
});
|
|
});
|
|
|
|
router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
|
|
reportTemplates.delete(req.body.id, (err, deleted) => {
|
|
if (err) {
|
|
req.flash('danger', err && err.message || err);
|
|
} else if (deleted) {
|
|
req.flash('success', _('Report template deleted'));
|
|
} else {
|
|
req.flash('info', _('Could not delete specified report template'));
|
|
}
|
|
|
|
return res.redirect('/report-templates');
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|