landmark-serve
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
138 lines (110 loc) • 3.5 kB
JavaScript
var landmark = require('../../'),
_ = require('underscore'),
async = require('async'),
moment = require('moment'),
csv = require('csv');
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
exports = module.exports = function(req, res) {
var filters = req.list.processFilters(req.query.q),
queryFilters = req.list.getSearchFilters(req.query.search, filters);
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);
}
_.each(req.list.fields, function(field) {
if (field.type === 'boolean') {
rowData[field.path] = i.get(field.path) ? 'true' : 'false';
} else {
rowData[field.path] = field.format(i);
}
});
return rowData;
};
req.list.model.find(queryFilters).exec(function(err, results) {
var sendCSV = function(data) {
var columns = data.length ? Object.keys(data[0]) : [];
csv().from(data).to(res.attachment(req.list.path + '-' + moment().format('YYYYMMDD-HHMMSS') + '.csv'), {
header: true,
columns: columns
});
};
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(landmark.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
_.each(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
_.each(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 = [];
_.each(results, function(i) {
data.push(getRowData(i));
});
return sendCSV(data);
}
});
};