Added support for Datatables

Added support for ajax-based server side validation (useful for validation of emails, duplicate usernames, etc.)
User form more or less ready in the basic version (i.e. without permission management)
This commit is contained in:
Tomas Bures 2017-06-21 02:14:14 +02:00
parent f776170854
commit c81f5544e6
26 changed files with 1097 additions and 167 deletions

54
lib/dt-helpers.js Normal file
View file

@ -0,0 +1,54 @@
'use strict';
const knex = require('../lib/knex');
async function ajaxList(params, queryFun, columns) {
return await knex.transaction(async (tx) => {
const query = queryFun(tx);
const recordsTotalQuery = query.clone().count('* as recordsTotal').first();
const recordsTotal = (await recordsTotalQuery).recordsTotal;
query.where(function() {
let searchVal = '%' + params.search.value.replace(/\\/g, '\\\\').replace(/([%_])/g, '\\$1') + '%';
for (let colIdx = 0; colIdx < params.columns.length; colIdx++) {
const col = params.columns[colIdx];
if (col.searchable === 'true') {
this.orWhere(columns[parseInt(col.data)], 'like', searchVal);
}
}
});
const recordsFilteredQuery = query.clone().count('* as recordsFiltered').first();
const recordsFiltered = (await recordsFilteredQuery).recordsFiltered;
query.offset(parseInt(params.start));
const limit = parseInt(params.length);
if (limit >= 0) {
query.limit(limit);
}
query.select(columns);
for (const order of params.order) {
query.orderBy(columns[params.columns[order.column].data], order.dir);
}
const rows = await query;
const rowsOfArray = rows.map(row => Object.keys(row).map(key => row[key]));
const result = {
draw: params.draw,
recordsTotal,
recordsFiltered,
data: rowsOfArray
};
return result;
});
}
module.exports = {
ajaxList
};

View file

@ -1,81 +0,0 @@
'use strict';
const knex = require('../knex');
const hasher = require('node-object-hash')();
const { enforce, filterObject } = require('../helpers');
const interoperableErrors = require('../../shared/interoperable-errors');
const allowedKeys = new Set(['id', 'name', 'description', 'parent']);
const allowedUpdateKeys = new Set(['name', 'description', 'parent']);
async function list() {
return await knex('namespaces');
}
function hash(ns) {
return hasher.hash(filterObject(ns, allowedKeys));
}
async function getById(nsId) {
const ns = await knex('namespaces').where('id', nsId).first();
if (!ns) {
throw new interoperableErrors.NotFoundError();
}
ns.hash = hash(ns);
return ns;
}
async function create(ns) {
const nsId = await knex('namespaces').insert(filterObject(ns, allowedKeys));
return nsId;
}
async function updateWithConsistencyCheck(ns) {
enforce(ns.id !== 1 || ns.parent === null, 'Cannot assign a parent to the root namespace.');
await knex.transaction(async tx => {
const existingNs = await tx('namespaces').where('id', ns.id).first();
if (!ns) {
throw new interoperableErrors.NotFoundError();
}
const existingNsHash = hash(existingNs);
if (existingNsHash != ns.originalHash) {
throw new interoperableErrors.ChangedError();
}
let iter = ns;
while (iter.parent != null) {
iter = await tx('namespaces').where('id', iter.parent).first();
if (iter.id == ns.id) {
throw new interoperableErrors.LoopDetectedError();
}
}
await tx('namespaces').where('id', ns.id).update(filterObject(ns, allowedUpdateKeys));
});
}
async function remove(nsId) {
enforce(nsId !== 1, 'Cannot delete the root namespace.');
await knex.transaction(async tx => {
const childNs = await tx('namespaces').where('parent', nsId).first();
if (childNs) {
throw new interoperableErrors.ChildDetectedError();
}
await tx('namespaces').where('id', nsId).del();
});
}
module.exports = {
hash,
list,
getById,
create,
updateWithConsistencyCheck,
remove
};

View file

@ -10,7 +10,7 @@ let LocalStrategy = require('passport-local').Strategy;
let csrf = require('csurf');
let bodyParser = require('body-parser');
let users = require('./models/users');
let users = require('./models/users-legacy');
let LdapStrategy;
try {

41
lib/tools-async.js Normal file
View file

@ -0,0 +1,41 @@
'use strict';
const _ = require('./translate')._;
const util = require('util');
const Promise = require('bluebird');
const isemail = require('isemail')
module.exports = {
validateEmail
};
async function validateEmail(address, checkBlocked) {
let user = (address || '').toString().split('@').shift().toLowerCase().replace(/[^a-z0-9]/g, '');
if (checkBlocked && blockedUsers.indexOf(user) >= 0) {
throw new new Error(util.format(_('Blocked email address "%s"'), address));
}
const result = await new Promise(resolve => {
const result = isemail.validate(address, {
checkDNS: true,
errorLevel: 1
}, resolve);
});
if (result !== 0) {
let message = util.format(_('Invalid email address "%s".'), address);
switch (result) {
case 5:
message += ' ' + _('MX record not found for domain');
break;
case 6:
message += ' ' + _('Address domain not found');
break;
case 12:
message += ' ' + _('Address domain name is required');
break;
}
throw new Error(message);
}
}