Fluid layout
Reworked routing and breadcrumb mechanism. It allows resolved parameters in paths, which allows including names of entities in the breadcrumb. Secondary navigation which is aware of permissions.
This commit is contained in:
parent
86fce404a9
commit
602364caae
33 changed files with 808 additions and 907 deletions
|
@ -10,6 +10,7 @@ const shares = require('./shares');
|
|||
const fieldsLegacy = require('../lib/models/fields');
|
||||
const bluebird = require('bluebird');
|
||||
const validators = require('../shared/validators');
|
||||
const shortid = require('shortid');
|
||||
|
||||
const allowedKeysCreate = new Set(['name', 'key', 'default_value', 'type', 'group', 'settings']);
|
||||
const allowedKeysUpdate = new Set(['name', 'key', 'default_value', 'group', 'settings']);
|
||||
|
@ -62,7 +63,7 @@ fieldTypes['radio-enum'] = fieldTypes['dropdown-enum'] = {
|
|||
};
|
||||
|
||||
fieldTypes.option = {
|
||||
validate: entity => [],
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.boolean(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
|
@ -156,13 +157,18 @@ async function serverValidate(context, listId, data) {
|
|||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageFields');
|
||||
|
||||
if (data.key) {
|
||||
const existing = await tx('custom_fields').where({
|
||||
const existingKeyQuery = tx('custom_fields').where({
|
||||
list: listId,
|
||||
key: data.key
|
||||
}).whereNot('id', data.id).first();
|
||||
});
|
||||
|
||||
if (data.id) {
|
||||
existingKeyQuery.whereNot('id', data.id);
|
||||
}
|
||||
|
||||
const existingKey = await existingKeyQuery.first();
|
||||
result.key = {
|
||||
exists: !!existing
|
||||
exists: !!existingKey
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -180,7 +186,6 @@ async function _validateAndPreprocess(tx, listId, entity, isCreate) {
|
|||
enforce(fieldType, 'Unknown field type');
|
||||
|
||||
const validateErrs = fieldType.validate(entity);
|
||||
enforce(!validateErrs.length, 'Invalid field');
|
||||
|
||||
enforce(validators.mergeTagValid(entity.key), 'Merge tag is not valid.');
|
||||
|
||||
|
@ -189,7 +194,7 @@ async function _validateAndPreprocess(tx, listId, entity, isCreate) {
|
|||
key: entity.key
|
||||
});
|
||||
if (!isCreate) {
|
||||
existingWithKeyQuery.whereNot('id', data.id);
|
||||
existingWithKeyQuery.whereNot('id', entity.id);
|
||||
}
|
||||
const existingWithKey = await existingWithKeyQuery.first();
|
||||
if (existingWithKey) {
|
||||
|
@ -255,9 +260,11 @@ async function create(context, listId, entity) {
|
|||
|
||||
await _validateAndPreprocess(tx, listId, entity, true);
|
||||
|
||||
const fieldType = fieldTypes[entity.type];
|
||||
|
||||
let columnName;
|
||||
if (!fieldType.grouped) {
|
||||
columnName = ('custom_' + slugify(name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, '');
|
||||
columnName = ('custom_' + slugify(entity.name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, '');
|
||||
}
|
||||
|
||||
const filteredEntity = filterObject(entity, allowedKeysCreate);
|
||||
|
@ -267,7 +274,7 @@ async function create(context, listId, entity) {
|
|||
const ids = await tx('custom_fields').insert(filteredEntity);
|
||||
id = ids[0];
|
||||
|
||||
_sortIn(tx, listId, id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
await _sortIn(tx, listId, id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
|
||||
if (columnName) {
|
||||
await knex.schema.table('subscription__' + listId, table => {
|
||||
|
@ -297,10 +304,10 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
}
|
||||
|
||||
enforce(entity.type === existing.type, 'Field type cannot be changed');
|
||||
await _validateAndPreprocess(tx, listId, entity, true);
|
||||
await _validateAndPreprocess(tx, listId, entity, false);
|
||||
|
||||
await tx('custom_fields').where('id', entity.id).update(filterObject(entity, allowedKeysUpdate));
|
||||
_sortIn(tx, listId, entity.id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
await _sortIn(tx, listId, entity.id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ async function getById(context, id) {
|
|||
await knex.transaction(async tx => {
|
||||
shares.enforceEntityPermissionTx(tx, context, 'customForm', id, 'view');
|
||||
entity = await _getById(tx, id);
|
||||
entity.permissions = await shares.getPermissions(tx, context, 'customForm', id);
|
||||
});
|
||||
|
||||
return entity;
|
||||
|
@ -146,7 +147,7 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
const existing = await _getById(tx, entity.id);
|
||||
|
||||
const existingHash = hash(existing);
|
||||
if (texistingHash !== entity.originalHash) {
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,19 @@ async function listDTAjax(context, params) {
|
|||
}
|
||||
|
||||
async function getById(context, id) {
|
||||
shares.enforceEntityPermission(context, 'list', id, 'view');
|
||||
let entity;
|
||||
|
||||
const entity = await knex('lists').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
await knex.transaction(async tx => {
|
||||
|
||||
shares.enforceEntityPermissionTx(tx, context, 'list', id, 'view');
|
||||
|
||||
entity = await tx('lists').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
entity.permissions = await shares.getPermissions(tx, context, 'list', id);
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
@ -73,7 +80,7 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
}
|
||||
|
||||
const existingHash = hash(existing);
|
||||
if (texistingHash !== entity.originalHash) {
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ const { enforce, filterObject } = require('../lib/helpers');
|
|||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const permissions = require('../lib/permissions');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'namespace']);
|
||||
|
||||
|
@ -106,12 +108,19 @@ function hash(entity) {
|
|||
}
|
||||
|
||||
async function getById(context, id) {
|
||||
await shares.enforceEntityPermission(context, 'namespace', id, 'view');
|
||||
let entity;
|
||||
|
||||
const entity = await knex('namespaces').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
await knex.transaction(async tx => {
|
||||
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'namespace', id, 'view');
|
||||
|
||||
entity = await tx('namespaces').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
entity.permissions = await shares.getPermissions(tx, context, 'namespace', id);
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
@ -145,7 +154,7 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
}
|
||||
|
||||
const existingHash = hash(existing);
|
||||
if (texistingHash !== entity.originalHash) {
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,12 +15,19 @@ function hash(entity) {
|
|||
}
|
||||
|
||||
async function getById(context, id) {
|
||||
await shares.enforceEntityPermission(context, 'reportTemplate', id, 'view');
|
||||
let entity;
|
||||
|
||||
const entity = await knex('report_templates').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
await knex.transaction(async tx => {
|
||||
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'reportTemplate', id, 'view');
|
||||
|
||||
entity = await tx('report_templates').where('id', id).first();
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
entity.permissions = await shares.getPermissions(tx, context, 'reportTemplate', id);
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
@ -60,7 +67,7 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
}
|
||||
|
||||
const existingHash = hash(existing);
|
||||
if (texistingHash !== entity.originalHash) {
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,20 +19,27 @@ function hash(entity) {
|
|||
}
|
||||
|
||||
async function getByIdWithTemplate(context, id) {
|
||||
await shares.enforceEntityPermission(context, 'report', id, 'view');
|
||||
let entity;
|
||||
|
||||
const entity = await knex('reports')
|
||||
.where('reports.id', id)
|
||||
.innerJoin('report_templates', 'reports.report_template', 'report_templates.id')
|
||||
.select(['reports.id', 'reports.name', 'reports.description', 'reports.report_template', 'reports.params', 'reports.state', 'reports.namespace', 'report_templates.user_fields', 'report_templates.mime_type', 'report_templates.hbs', 'report_templates.js'])
|
||||
.first();
|
||||
await knex.transaction(async tx => {
|
||||
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'report', id, 'view');
|
||||
|
||||
entity.user_fields = JSON.parse(entity.user_fields);
|
||||
entity.params = JSON.parse(entity.params);
|
||||
entity = await tx('reports')
|
||||
.where('reports.id', id)
|
||||
.innerJoin('report_templates', 'reports.report_template', 'report_templates.id')
|
||||
.select(['reports.id', 'reports.name', 'reports.description', 'reports.report_template', 'reports.params', 'reports.state', 'reports.namespace', 'report_templates.user_fields', 'report_templates.mime_type', 'report_templates.hbs', 'report_templates.js'])
|
||||
.first();
|
||||
|
||||
if (!entity) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
entity.user_fields = JSON.parse(entity.user_fields);
|
||||
entity.params = JSON.parse(entity.params);
|
||||
|
||||
entity.permissions = await shares.getPermissions(tx, context, 'report', id);
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
@ -89,7 +96,7 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
existing.params = JSON.parse(existing.params);
|
||||
|
||||
const existingHash = hash(existing);
|
||||
if (texistingHash !== entity.originalHash) {
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
|
|
|
@ -518,6 +518,17 @@ async function enforceTypePermissionTx(tx, context, entityTypeId, requiredOperat
|
|||
}
|
||||
}
|
||||
|
||||
async function getPermissions(tx, context, entityTypeId, entityId) {
|
||||
const entityType = permissions.getEntityType(entityTypeId);
|
||||
|
||||
const rows = await tx(entityType.permissionsTable)
|
||||
.select('operation')
|
||||
.where('entity', entityId)
|
||||
.where('user', context.user.id);
|
||||
|
||||
return rows.map(x => x.operation);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
listByEntityDTAjax,
|
||||
|
@ -535,5 +546,6 @@ module.exports = {
|
|||
checkTypePermission,
|
||||
enforceGlobalPermission,
|
||||
throwPermissionDenied,
|
||||
regenerateRoleNamesTable
|
||||
regenerateRoleNamesTable,
|
||||
getPermissions
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue