mailtrain/routes/reports.js

329 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 reports = require('../lib/models/reports');
const campaigns = require('../lib/models/campaigns');
const lists = require('../lib/models/lists');
const tools = require('../lib/tools');
const util = require('util');
const htmlescape = require('escape-html');
const striptags = require('striptags');
const fs = require('fs');
const fsTools = require('../lib/fs-tools');
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('reports/reports', {
title: _('Reports')
});
});
router.post('/ajax', (req, res) => {
function getViewLink(row) {
if (row.state == 0) {
// TODO: Render waiting
// TODO: Add error output
return '<span class="glyphicon glyphicon-hourglass" aria-hidden="true"></span> ';
} else if (row.state == 1) {
let icon = 'eye-open';
if (row.mimeType == 'text/csv') icon = 'download-alt';
// TODO: Add error output
return '<a href="/reports/view/' + row.id + '"><span class="glyphicon glyphicon-' + icon + '" aria-hidden="true"></span></a> ';
} else if (row.state == 2) {
// TODO: Add error output
return '<span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span> ';
}
return '';
}
reports.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(row.reportTemplateName || ''),
htmlescape(striptags(row.description) || ''),
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>',
getViewLink(row) +
'<a href="/reports/edit/' + row.id + '"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a>']
)
});
});
});
router.get('/create', passport.csrfProtection, (req, res) => {
const reqData = req.query;
reqData.csrfToken = req.csrfToken();
reqData.title = _('Create Report');
reqData.useEditor = true;
reportTemplates.quicklist((err, items) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/reports');
}
const reportTemplateId = Number(reqData.reportTemplate);
if (reportTemplateId) {
items.forEach(item => {
if (item.id === reportTemplateId) {
item.selected = true;
}
});
}
reqData.reportTemplates = items;
if (!reportTemplateId) {
res.render('reports/create-select-template', reqData);
} else {
addUserFields(reportTemplateId, reqData, null, (err, data) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/reports');
}
res.render('reports/create', data);
});
}
});
});
router.post('/create', passport.parseForm, passport.csrfProtection, (req, res) => {
const reqData = req.body;
delete reqData.filename; // This is to make sure no one inserts a fake filename when editing the report.
const reportTemplateId = Number(reqData.reportTemplate);
addParamsObject(reportTemplateId, reqData, (err, data) => {
if (err) {
req.flash('danger', err && err.message || err || _('Could not create report'));
return res.redirect('/reports/create?' + tools.queryParams(data));
}
reports.createOrUpdate(true, data, (err, id) => {
if (err || !id) {
req.flash('danger', err && err.message || err || _('Could not create report'));
return res.redirect('/reports/create?' + tools.queryParams(data));
}
req.flash('success', util.format(_('Report “%s” created'), data.name));
res.redirect('/reports');
});
});
});
router.get('/edit/:id', passport.csrfProtection, (req, res) => {
const reqData = req.query;
reports.get(req.params.id, (err, report) => {
if (err || !report) {
req.flash('danger', err && err.message || err || _('Could not find report with specified ID'));
return res.redirect('/reports');
}
report.csrfToken = req.csrfToken();
report.title = _('Edit Report');
report.useEditor = true;
reportTemplates.quicklist((err, items) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/');
}
const reportTemplateId = report.reportTemplate;
items.forEach(item => {
if (item.id === reportTemplateId) {
item.selected = true;
}
});
report.reportTemplates = items;
addUserFields(reportTemplateId, reqData, report, (err, data) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/reports');
}
res.render('reports/edit', data);
});
});
});
});
router.post('/edit', passport.parseForm, passport.csrfProtection, (req, res) => {
const reqData = req.body;
delete reqData.filename; // This is to make sure no one inserts a fake filename when editing the report.
const reportTemplateId = Number(reqData.reportTemplate);
addParamsObject(reportTemplateId, reqData, (err, data) => {
if (err) {
req.flash('danger', err && err.message || err || _('Could not update report'));
return res.redirect('/reports/create?' + tools.queryParams(data));
}
reports.createOrUpdate(false, data, (err, updated) => {
if (err) {
req.flash('danger', err && err.message || err || _('Could not update report'));
return res.redirect('/reports/edit/' + data.id + '?' + tools.queryParams(data));
} else if (updated) {
req.flash('success', _('Report updated'));
} else {
req.flash('info', _('Report not updated'));
}
return res.redirect('/reports');
});
});
});
router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) => {
reports.delete(req.body.id, (err, deleted) => {
if (err) {
req.flash('danger', err && err.message || err);
} else if (deleted) {
req.flash('success', _('Report deleted'));
} else {
req.flash('info', _('Could not delete specified report'));
}
return res.redirect('/reports');
});
});
router.get('/view/:id', passport.csrfProtection, (req, res) => {
reports.get(req.params.id, (err, report) => {
if (err || !report) {
req.flash('danger', err && err.message || err || _('Could not find report with specified ID'));
return res.redirect('/reports');
}
reportTemplates.get(report.reportTemplate, (err, reportTemplate) => {
if (err) {
req.flash('danger', err && err.message || err || _('Could not find report template'));
return res.redirect('/reports');
}
if (report.state == 1) {
if (reportTemplate.mimeType == 'text/html') {
fs.readFile(path.join(__dirname, '../protected/reports', report.filename + '.report'), (err, reportContent) => {
if (err) {
req.flash('danger', err && err.message || err || _('Could not find report with specified ID'));
return res.redirect('/reports');
}
const data = {
csrfToken: req.csrfToken(),
report: new hbs.handlebars.SafeString(reportContent),
title: report.name
};
res.render('reports/view', data);
});
} else if (reportTemplate.mimeType == 'text/csv') {
const headers = {
'Content-Disposition': 'attachment;filename=' + fsTools.nameToFileName(report.name) + '.csv',
'Content-Type': 'text/csv'
};
res.sendFile(path.join(__dirname, '../protected/reports', report.filename + '.report'), {headers: headers});
} else {
req.flash('danger', _('Unknown type of template'));
res.redirect('/reports');
}
} else {
req.flash('danger', err && err.message || err || _('Could not find report with specified ID'));
return res.redirect('/reports');
}
});
});
});
function addUserFields(reportTemplateId, reqData, report, callback) {
reportTemplates.get(reportTemplateId, (err, reportTemplate) => {
if (err) {
return callback(err);
}
const userFields = [];
for (const spec of reportTemplate.userFieldsObject) {
let value = '';
if ((spec.id + 'Selection') in reqData) {
value = reqData[spec.id + 'Selection'];
} else if (report && report.paramsObject && spec.id in report.paramsObject) {
value = report.paramsObject[spec.id].join(',');
}
userFields.push({
'id': spec.id,
'name': spec.name,
'type': spec.type,
'value': value,
'isMulti': !(spec.minOccurences == 1 && spec.maxOccurences == 1)
});
}
const data = report ? report : reqData;
data.userFields = userFields;
callback(null, data);
});
}
function addParamsObject(reportTemplateId, data, callback) {
reportTemplates.get(reportTemplateId, (err, reportTemplate) => {
if (err) {
return callback(err);
}
const paramsObject = {};
for (const spec of reportTemplate.userFieldsObject) {
const sel = data[spec.id + 'Selection'];
if (!sel) {
paramsObject[spec.id] = [];
} else {
paramsObject[spec.id] = sel.split(',').map(item => Number(item));
}
}
data.paramsObject = paramsObject;
callback(null, data);
});
}
module.exports = router;