Report templates ported to ReactJS and Knex.
Does not run yet because reports have dependencies on the old report templates.
This commit is contained in:
parent
be7da791db
commit
d4cea46f07
29 changed files with 807 additions and 688 deletions
10
routes/report-templates-legacy-integration.js
Normal file
10
routes/report-templates-legacy-integration.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const _ = require('../lib/translate')._;
|
||||
const clientHelpers = require('../lib/client-helpers');
|
||||
|
||||
const router = require('../lib/router-async').create();
|
||||
|
||||
clientHelpers.registerRootRoute(router, _('Report Templates'), 'reportTemplates');
|
||||
|
||||
module.exports = router;
|
|
@ -1,307 +0,0 @@
|
|||
'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('/account/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;
|
39
routes/rest/report-templates.js
Normal file
39
routes/rest/report-templates.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
const passport = require('../../lib/passport');
|
||||
const _ = require('../../lib/translate')._;
|
||||
const reportTemplates = require('../../models/report-templates');
|
||||
|
||||
const router = require('../../lib/router-async').create();
|
||||
|
||||
|
||||
router.getAsync('/report-templates/:reportTemplateId', passport.loggedIn, async (req, res) => {
|
||||
const reportTemplate = await reportTemplates.getById(req.params.reportTemplateId);
|
||||
reportTemplate.hash = reportTemplates.hash(reportTemplate);
|
||||
return res.json(reportTemplate);
|
||||
});
|
||||
|
||||
router.postAsync('/report-templates', passport.loggedIn, passport.csrfProtection, async (req, res) => {
|
||||
await reportTemplates.create(req.body);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
router.putAsync('/report-templates/:reportTemplateId', passport.loggedIn, passport.csrfProtection, async (req, res) => {
|
||||
const reportTemplate = req.body;
|
||||
reportTemplate.id = parseInt(req.params.reportTemplateId);
|
||||
|
||||
await reportTemplates.updateWithConsistencyCheck(reportTemplate);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
router.deleteAsync('/report-templates/:reportTemplateId', passport.loggedIn, passport.csrfProtection, async (req, res) => {
|
||||
await reportTemplates.remove(req.context, req.params.reportTemplateId);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
router.postAsync('/report-templates-table', passport.loggedIn, async (req, res) => {
|
||||
return res.json(await reportTemplates.listDTAjax(req.body));
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
|
@ -27,7 +27,7 @@ router.putAsync('/users/:userId', passport.loggedIn, passport.csrfProtection, as
|
|||
});
|
||||
|
||||
router.deleteAsync('/users/:userId', passport.loggedIn, passport.csrfProtection, async (req, res) => {
|
||||
await users.remove(req.params.userId);
|
||||
await users.remove(req.context, req.params.userId);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue