UNPKG

keystone

Version:

Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose

188 lines (154 loc) 5.06 kB
/* TODO: Deprecate. Has been replaced by the new implementation in list/download, but this version supports more features at the moment (custom .toCSV method on lists, etc) */ var _ = require('lodash'); var async = require('async'); var moment = require('moment'); var escapeValueForExcel = require('../security/escapeValueForExcel'); var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; module.exports = function (req, res) { var baby = require('babyparse'); var keystone = req.keystone; var filters = req.list.processFilters(req.query.q); var queryFilters = req.list.getSearchFilters(req.query.search, filters); var relFields = []; _.forEach(req.list.fields, function (field) { if (field.type === 'relationship') { relFields.push(field.path); } }); var getRowData = function getRowData (i) { var rowData = { id: i.id }; if (req.list.get('autokey')) { rowData[req.list.get('autokey').path] = i.get(req.list.get('autokey').path); } _.forEach(req.list.fields, function (field) { if (field.type === 'boolean') { rowData[field.path] = i.get(field.path) ? 'true' : 'false'; } else if (field.type === 'relationship') { var refData = i.get(field.path); if (field.many) { var values = []; if (Array.isArray(refData) && refData.length) { _.forEach(refData, function (i) { var name = field.refList.getDocumentName(i); if (keystone.get('csv expanded')) { name = '[' + i.id + ',' + name + ']'; } values.push(name); }); } rowData[field.path] = values.join(', '); } else { if (keystone.get('csv expanded')) { rowData[field.path + '_id'] = refData ? refData.id : ''; rowData[field.path + '_name'] = refData ? field.refList.getDocumentName(refData) : field.format(i); } else { rowData[field.path] = refData ? field.refList.getDocumentName(refData) : field.format(i); } } } else { rowData[field.path] = field.format(i); } }); // Prevent CSV macro injection _.forOwn(rowData, (value, prop) => { rowData[prop] = escapeValueForExcel(value); }); return rowData; }; var query = req.list.model.find(queryFilters); if (relFields) { query.populate(relFields.join(' ')); } query.exec(function (err, results) { if (err) return res.status(500).json(err); var sendCSV = function (data) { res.attachment(req.list.path + '-' + moment().format('YYYYMMDD-HHMMSS') + '.csv'); res.setHeader('Content-Type', 'application/octet-stream'); var content = baby.unparse(data, { delimiter: keystone.get('csv field delimiter') || ',', }); res.end(content, 'utf-8'); }; if (!results.length) { // fast bail on no results return sendCSV([]); } var data; if (results[0].toCSV) { /** * Custom toCSV Method present * * Detect dependencies and call it. If the last dependency is `callback`, call it asynchronously. * * Support dependencies are: * - req (current express request object) * - user (currently authenticated user) * - row (default row data, as generated without custom toCSV()) * - callback (invokes async mode, must be provided last) */ var deps = _.map(results[0].toCSV.toString().match(FN_ARGS)[1].split(','), function (i) { return i.trim(); }); var includeRowData = (deps.indexOf('row') > -1); var map = { req: req, user: req.user, }; var applyDeps = function (fn, _this, _map) { var args = _.map(deps, function (key) { return _map[key]; }); return fn.apply(_this, args); }; if (_.last(deps) === 'callback') { // Allow async toCSV by detecting the last argument is callback return async.map(results, function (i, callback) { var _map = _.clone(map); _map.callback = callback; if (includeRowData) { _map.row = getRowData(i); } applyDeps(i.toCSV, i, _map); }, function (err, results) { if (err) { console.log('Error generating CSV for list ' + req.list.key); console.log(err); return res.send(keystone.wrapHTMLError('Error generating CSV', 'Please check the log for more details, or contact support.')); } sendCSV(results); }); } else { // Without a callback, toCSV must return the value data = []; if (includeRowData) { // if row data is required, add it to the map for each iteration _.forEach(results, function (i) { var _map = _.clone(map); _map.row = getRowData(i); data.push(applyDeps(i.toCSV, i, _map)); }); } else { // fast path: use the same map for each iteration _.forEach(results, function (i) { data.push(applyDeps(i.toCSV, i, map)); }); } return sendCSV(data); } } else { /** * Generic conversion to CSV * * Loops through each of the fields in the List and uses each field's `format` method * to generate the data */ data = []; _.forEach(results, function (i) { data.push(getRowData(i)); }); return sendCSV(data); } }); };