From 5876977f4318436aad3feffb4690b330109cb2ca Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Fri, 29 Apr 2016 11:52:02 +0300 Subject: [PATCH] use ajax to load campaign listing instead of prerendering --- lib/models/campaigns.js | 78 +++++++++++++++++++++++++++++++++ public/javascript/tables.js | 22 ++++++++-- routes/campaigns.js | 81 +++++++++++++++++++---------------- views/campaigns/campaigns.hbs | 34 +-------------- views/lists/view.hbs | 4 +- 5 files changed, 144 insertions(+), 75 deletions(-) diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js index 672541df..ddf1be27 100644 --- a/lib/models/campaigns.js +++ b/lib/models/campaigns.js @@ -32,6 +32,84 @@ module.exports.list = (start, limit, callback) => { }); }; +module.exports.filter = (request, callback) => { + let columns = ['#', 'name', 'description', 'status', 'created']; + let processQuery = queryData => { + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'SELECT COUNT(id) AS total FROM `campaigns`'; + let values = []; + + if (queryData.where) { + query += ' WHERE ' + queryData.where; + values = values.concat(queryData.values || []); + } + + connection.query(query, values, (err, total) => { + if (err) { + connection.release(); + return callback(err); + } + total = total && total[0] && total[0].total || 0; + + let ordering = []; + + if (request.order && request.order.length) { + + request.order.forEach(order => { + let orderField = columns[Number(order.column)]; + let orderDirection = (order.dir || '').toString().toLowerCase() === 'desc' ? 'DESC' : 'ASC'; + if (orderField) { + ordering.push('`' + orderField + '` ' + orderDirection); + } + }); + } + + if (!ordering.length) { + ordering.push('`created` DESC'); + } + + let args = [Number(request.length) || 50, Number(request.start) || 0]; + let query; + + if (request.search && request.search.value) { + query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `campaigns` WHERE name LIKE ? ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?'; + + let searchVal = '%' + request.search.value.replace(/\\/g, '\\\\').replace(/([%_])/g, '\\$1') + '%'; + args = [searchVal].concat(queryData.values || []).concat(args); + } else { + query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `campaigns` WHERE 1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?'; + args = [].concat(queryData.values || []).concat(args); + } + + connection.query(query, args, (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + connection.query('SELECT FOUND_ROWS() AS total', (err, filteredTotal) => { + connection.release(); + if (err) { + return callback(err); + } + + let subscriptions = rows.map(row => tools.convertKeys(row)); + + filteredTotal = filteredTotal && filteredTotal[0] && filteredTotal[0].total || 0; + return callback(null, subscriptions, total, filteredTotal); + }); + }); + }); + }); + }; + + processQuery(false); +}; + module.exports.getByCid = (cid, callback) => { cid = (cid || '').toString().trim(); if (!cid) { diff --git a/public/javascript/tables.js b/public/javascript/tables.js index da363f75..492d318a 100644 --- a/public/javascript/tables.js +++ b/public/javascript/tables.js @@ -29,8 +29,20 @@ $('.data-table').each(function () { $('.data-table-ajax').each(function () { var rowSort = $(this).data('rowSort') || false; var columns = false; - var listArgs = $(this).data('listArgs') || false; - var ajaxUrl = '/lists/ajax/' + $(this).data('listId') + (listArgs ? '?' + listArgs : ''); + + var topicUrl = $(this).data('topicUrl') || '/lists'; + var topicArgs = $(this).data('topicArgs') || false; + var topicId = $(this).data('topicId') || ''; + + var sortColumn = Number($(this).data('sortColumn')) || 1; + var sortOrder = ($(this).data('sortOrder') || 'asc').toString().trim().toLowerCase(); + + // allow only asc and desc + if (sortOrder !== 'desc') { + sortOrder = 'asc'; + } + + var ajaxUrl = topicUrl + '/ajax/' + topicId + (topicArgs ? '?' + topicArgs : ''); if (rowSort) { columns = rowSort.split(',').map(function (sort) { @@ -48,11 +60,15 @@ $('.data-table-ajax').each(function () { type: 'POST' }, order: [ - [1, 'asc'] + [sortColumn, sortOrder] ], columns: columns, pageLength: 50, processing: true + }).on('draw', function () { + $('.datestring').each(function () { + $(this).html(moment($(this).data('date')).fromNow()); + }); }); }); diff --git a/routes/campaigns.js b/routes/campaigns.js index 3165ef38..fec97ff3 100644 --- a/routes/campaigns.js +++ b/routes/campaigns.js @@ -9,6 +9,7 @@ let settings = require('../lib/models/settings'); let tools = require('../lib/tools'); let striptags = require('striptags'); let passport = require('../lib/passport'); +let htmlescape = require('escape-html'); router.all('/*', (req, res, next) => { if (!req.user) { @@ -20,43 +21,8 @@ router.all('/*', (req, res, next) => { }); router.get('/', (req, res) => { - let limit = 999999999; - let start = 0; - - campaigns.list(start, limit, (err, rows, total) => { - if (err) { - req.flash('danger', err.message || err); - return res.redirect('/'); - } - - res.render('campaigns/campaigns', { - rows: rows.map((row, i) => { - row.index = start + i + 1; - row.description = striptags(row.description); - switch (row.status) { - case 1: - row.statusText = 'Idling'; - break; - case 2: - if (row.scheduled && row.scheduled > new Date()) { - row.statusText = 'Scheduled'; - } else { - row.statusText = 'Sending'; - } - break; - case 3: - row.statusText = 'Finished'; - break; - case 4: - row.statusText = 'Paused'; - break; - } - row.createdTimestamp = row.created.getTime(); - row.created = row.created.toISOString(); - return row; - }), - total - }); + res.render('campaigns/campaigns', { + title: 'Campaigns' }); }); @@ -211,6 +177,47 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) = }); }); +router.post('/ajax', (req, res) => { + campaigns.filter(req.body, (err, data, total, filteredTotal) => { + if (err) { + return res.json({ + error: err.message || err, + data: [] + }); + } + + let getStatusText = data => { + switch (data.status) { + case 1: + return 'Idling'; + case 2: + if (data.scheduled && data.scheduled > new Date()) { + return 'Scheduled'; + } + return 'Sending'; + case 3: + return 'Finished'; + case 4: + return 'Paused'; + } + return 'Other'; + }; + + 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) || ''), + getStatusText(row), + '' + row.created.toISOString() + '' + ].concat('Edit')) + }); + }); +}); + router.get('/view/:id', passport.csrfProtection, (req, res) => { campaigns.get(req.params.id, true, (err, campaign) => { if (err || !campaign) { diff --git a/views/campaigns/campaigns.hbs b/views/campaigns/campaigns.hbs index 05f4b4f5..29aba51d 100644 --- a/views/campaigns/campaigns.hbs +++ b/views/campaigns/campaigns.hbs @@ -12,7 +12,7 @@
- +
- {{#if rows}} - - {{#each rows}} - - - - - - - - - {{/each}} - - {{/if}}
# @@ -33,37 +33,5 @@  
- {{index}} - - - - {{name}} - - -

{{description}}

-
-

{{statusText}}

-
- {{createdTimestamp}} - - - - Edit - -
diff --git a/views/lists/view.hbs b/views/lists/view.hbs index a5eed89b..0b2f01eb 100644 --- a/views/lists/view.hbs +++ b/views/lists/view.hbs @@ -71,7 +71,7 @@

- +
@@ -166,7 +166,7 @@ {{updated}} - {{#if failed}}{{failed}}{{else}} 0 {{/if}} + {{#if failed}}{{failed}}{{else}}0 {{/if}} {{#if error}}