lynx-admin-ui
Version:
this module adds annotations for entities in order to automatically generate an Administration UI
593 lines (592 loc) • 29.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseUIController = void 0;
var controller_1 = require("./controller");
var _1 = require(".");
var datagrid_1 = require("lynx-datagrid/datagrid");
var sprintf_js_1 = require("sprintf-js");
/**
* Basic controller that generates all the UI that contains the logic to perform the
* different operations.
*/
var BaseUIController = /** @class */ (function (_super) {
__extends(BaseUIController, _super);
function BaseUIController() {
return _super !== null && _super.apply(this, arguments) || this;
}
/**
* Generate the list of entities
* @param req The current Lynx request
*/
BaseUIController.prototype.generateEntitiesIndex = function (req) {
return __awaiter(this, void 0, void 0, function () {
var ctx;
return __generator(this, function (_a) {
ctx = {
parentTemplate: _1.default.indexParentTemplatePath,
entities: this.entitiesList(),
};
return [2 /*return*/, this.render(_1.default.indexTemplatePath, req, ctx)];
});
});
};
/**
* Perform ajax request for selection values
* @param entityName the name of the entity
* @param id the id of the editing entity
* @param field the name of the field that is edited
* @param req the current Lynx request
*/
BaseUIController.prototype.performAjaxRequest = function (entityName, id, field, req) {
return __awaiter(this, void 0, void 0, function () {
var entityData, metadata, _field, tmp, e_1, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 1:
entityData = _a.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
metadata = this.retrieveMetadata(entityName);
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 2:
metadata = _a.sent();
_field = metadata.fields[field];
if (!_field) {
throw this.error(500, 'field not found');
}
if (!_field.searchRequest) {
throw this.error(500, "You need to implement the 'searchRequest' parameter for the field '" +
field +
"'");
}
if (!req.query.selection) return [3 /*break*/, 7];
tmp = entityData[field];
if (!req.query.defaultId) return [3 /*break*/, 6];
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, this.retrieveEntity(_field.selfType, req.query.defaultId)];
case 4:
tmp = (_a.sent());
return [3 /*break*/, 6];
case 5:
e_1 = _a.sent();
return [3 /*break*/, 6];
case 6: return [2 /*return*/, {
data: [{ id: tmp.getId(), text: tmp.getLabel() }],
pagination: false,
}];
case 7: return [4 /*yield*/, _field.searchRequest(req, entityData, req.query.term, Number(req.query.page))];
case 8:
response = _a.sent();
return [2 /*return*/, {
data: response[0].map(function (b) {
return { id: b.key, text: b.value };
}),
pagination: response[1],
}];
}
});
});
};
/**
* Retrieve entity data to dynamically display results
* @param entityName the name of the entity
* @param id the id of the editing entity
* @param field the name of the field that is edited
* @param selection the current selection for the field
* @param req the current Lynx request
*/
BaseUIController.prototype.performAjaxDetails = function (entityName, id, field, selection, req) {
return __awaiter(this, void 0, void 0, function () {
var entityData, metadata, _field, _meta, data, cleaned, key;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 1:
entityData = _a.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
metadata = this.retrieveMetadata(entityName);
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 2:
metadata = _a.sent();
_field = metadata.fields[field];
if (!_field) {
throw this.error(500, 'field not found');
}
_meta = this.retrieveMetadata(_field.selfType);
return [4 /*yield*/, this.generateContextMetadata(_meta, req)];
case 3:
_meta = _a.sent();
return [4 /*yield*/, this.retrieveEntity(_field.selfType, selection)];
case 4:
data = _a.sent();
if (!data) {
throw this.error(404, 'not found');
}
return [4 /*yield*/, this.cleanData(req, data, _meta, false)];
case 5:
cleaned = _a.sent();
for (key in cleaned) {
if (cleaned[key] instanceof datagrid_1.default) {
this.logger.log('WARNING: currently not supporting Datagrid data with ExpandedAndSelection type...');
delete cleaned[key];
}
}
return [2 /*return*/, cleaned];
}
});
});
};
/**
* Delete the specified entity.
* @param entityName The name of the entity class
* @param id The id of the entity
* @param req The current Lynx request
*/
BaseUIController.prototype.performEntityDelete = function (entityName, id, req) {
return __awaiter(this, void 0, void 0, function () {
var entityData, e_2, msg;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 1:
entityData = _a.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, entityData.remove()];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
e_2 = _a.sent();
this.logger.error(e_2);
msg = this.tr('admin-ui.unable-delete', req);
this.addErrorMessage(sprintf_js_1.sprintf(msg, entityData.getLabel()), req);
return [3 /*break*/, 5];
case 5:
if (req.query.redirect) {
return [2 /*return*/, this.redirect(req.query.redirect)];
}
return [2 /*return*/, this.redirect('adminUI.list', { entityName: entityName })];
}
});
});
};
/**
* Delete the specified entities.
* @param entityName The name of the entity class
* @param req The current Lynx request, containing the query parameter ids, with an array of id
*/
BaseUIController.prototype.performEntityDeleteMultiple = function (entityName, req) {
return __awaiter(this, void 0, void 0, function () {
var ids, _i, ids_1, id, entityData, e_3, msg;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
ids = JSON.parse(req.query.ids);
_i = 0, ids_1 = ids;
_a.label = 1;
case 1:
if (!(_i < ids_1.length)) return [3 /*break*/, 7];
id = ids_1[_i];
return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 2:
entityData = _a.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, entityData.remove()];
case 4:
_a.sent();
return [3 /*break*/, 6];
case 5:
e_3 = _a.sent();
this.logger.error(e_3);
msg = this.tr('admin-ui.unable-delete', req);
this.addErrorMessage(sprintf_js_1.sprintf(msg, entityData.getLabel()), req);
return [3 /*break*/, 6];
case 6:
_i++;
return [3 /*break*/, 1];
case 7:
if (req.query.redirect) {
return [2 /*return*/, this.redirect(req.query.redirect)];
}
return [2 /*return*/, this.redirect('adminUI.list', { entityName: entityName })];
}
});
});
};
/**
* Retrieve and display the list of a specified entity
* @param entityName The name of the entity class
* @param req The current Lynx request
*/
BaseUIController.prototype.retrieveEntityList = function (entityName, req) {
return __awaiter(this, void 0, void 0, function () {
var metadata, datagrid, fields, hasSmartSearchable, key, field, data, k, tmp, u, _i, tmp_1, t, ctx;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
metadata = this.retrieveMetadata(entityName);
if (!metadata) {
throw this.error(404, 'Not found');
}
datagrid = new datagrid_1.default('', req);
return [4 /*yield*/, this.retrieveList(req, entityName, datagrid)];
case 1:
_a.sent();
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 2:
metadata = _a.sent();
return [4 /*yield*/, this.generateContextFields(metadata, req, {})];
case 3:
fields = _a.sent();
hasSmartSearchable = false;
for (key in fields) {
field = fields[key];
if (field.smartSearchable || field.searchable) {
field.readOnly = false;
field.required = false;
}
if (field.smartSearchable) {
hasSmartSearchable = true;
}
}
data = {};
for (k in req.query) {
tmp = req.query[k];
if (tmp instanceof Array) {
u = [];
for (_i = 0, tmp_1 = tmp; _i < tmp_1.length; _i++) {
t = tmp_1[_i];
try {
u.push(parseInt(t));
}
catch (e) {
u.push(t);
}
}
tmp = u;
}
data[k] = tmp;
}
ctx = {
metadata: metadata,
configuration: _1.default.configuration,
parentTemplate: metadata.classParameters.listParentTemplate,
gridData: datagrid,
data: data,
fields: fields,
hasSmartSearchable: hasSmartSearchable,
};
return [2 /*return*/, this.render(metadata.classParameters.listTemplate, req, ctx)];
}
});
});
};
BaseUIController.prototype.generateCxtForEditingRender = function (data, isPopup, metadata, fields) {
var _a, _b, _c, _d, _e, _f;
var usedTypes = [];
for (var key in fields) {
if (usedTypes.indexOf(fields[key].type) == -1) {
usedTypes.push(fields[key].type);
}
}
var ctx = {
data: data,
configuration: _1.default.configuration,
parentTemplate: isPopup
? metadata.classParameters.popupEditorParentTemplate
: metadata.classParameters.editorParentTemplate,
tabs: (_a = metadata.classParameters.uiSettings) === null || _a === void 0 ? void 0 : _a.tabs,
defaultTab: (_b = metadata.classParameters.uiSettings) === null || _b === void 0 ? void 0 : _b.defaultTab,
tabsAsSections: (_c = metadata.classParameters.uiSettings) === null || _c === void 0 ? void 0 : _c.tabsAsSections,
hideTabsInExpanded: (_d = metadata.classParameters.uiSettings) === null || _d === void 0 ? void 0 : _d.hideTabsInExpanded,
hideTabsInModal: (_e = metadata.classParameters.uiSettings) === null || _e === void 0 ? void 0 : _e.hideTabsInModal,
hasRightColumn: (_f = metadata.classParameters.uiSettings) === null || _f === void 0 ? void 0 : _f.hasRightColumn,
backButtonTemplate: metadata.classParameters.backButtonTemplate,
nested: false,
metadata: metadata,
fields: fields,
usedTypes: usedTypes,
};
return ctx;
};
/**
* Retrieve the details of a specific entity by its id
* @param entityName The name of the entity class
* @param id The id of the entity to retrieve
* @param req The current Lynx request
*/
BaseUIController.prototype.retrieveEntityDetails = function (entityName, id, req) {
return __awaiter(this, void 0, void 0, function () {
var entityData, metadata, _a, fields, data, usedTypes, key, isPopup, ctx;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 1:
entityData = _b.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
metadata = this.retrieveMetadata(entityName);
_a = [{}];
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 2:
metadata = __assign.apply(void 0, _a.concat([(_b.sent())]));
return [4 /*yield*/, this.generateContextFields(metadata, req, entityData)];
case 3:
fields = _b.sent();
metadata.fields = fields;
return [4 /*yield*/, this.cleanData(req, entityData, metadata)];
case 4:
data = _b.sent();
usedTypes = [];
for (key in fields) {
if (usedTypes.indexOf(fields[key].type) == -1) {
usedTypes.push(fields[key].type);
}
}
isPopup = req.query.popup;
ctx = this.generateCxtForEditingRender(data, isPopup, metadata, fields);
return [2 /*return*/, this.render(isPopup
? metadata.classParameters.popupEditorTemplate
: metadata.classParameters.editorTemplate, req, ctx)];
}
});
});
};
/**
* Retrieve the nested view of an entity
* @param entityName The name of the entity class
* @param id The id of the entity to retrieve
* @param nestedKey The name of the property of the entity to display
* @param req The current Lynx request
* @param enableDelete Determines if the delete action is enabled
*/
BaseUIController.prototype.retrieveNestedView = function (entityName, id, nestedKey, req, enableDelete) {
if (enableDelete === void 0) { enableDelete = false; }
return __awaiter(this, void 0, void 0, function () {
var entityData, metadata, nestedField, entityData_1, e_4, data, ctx, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 1:
entityData = _b.sent();
if (!entityData) {
throw this.error(404, 'not found');
}
metadata = __assign({}, this.retrieveMetadata(entityName));
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 2:
metadata = _b.sent();
nestedField = metadata.fields[nestedKey];
metadata.fields = {};
metadata.fields[nestedKey] = nestedField;
if (!(enableDelete && req.query.remove)) return [3 /*break*/, 7];
return [4 /*yield*/, this.retrieveEntity(nestedField.selfType, req.query.remove)];
case 3:
entityData_1 = _b.sent();
if (!entityData_1) {
throw this.error(404, 'not found');
}
_b.label = 4;
case 4:
_b.trys.push([4, 6, , 7]);
return [4 /*yield*/, entityData_1.remove()];
case 5:
_b.sent();
return [3 /*break*/, 7];
case 6:
e_4 = _b.sent();
this.logger.error(e_4);
this.addErrorMessage('Unable to delete ' +
entityData_1.getLabel() +
'. Please check its dependencies.', req);
return [3 /*break*/, 7];
case 7: return [4 /*yield*/, this.cleanData(req, entityData, metadata)];
case 8:
data = _b.sent();
_a = {
data: data,
configuration: _1.default.configuration,
parentTemplate: _1.default.nestedParentTemplatePath,
nested: true,
metadata: metadata
};
return [4 /*yield*/, this.generateContextFields(metadata, req, entityData)];
case 9:
ctx = (_a.fields = _b.sent(),
_a);
return [2 /*return*/, this.render(_1.default.nestedTemplatePath, req, ctx)];
}
});
});
};
/**
* Perform the edit of a specific entity
* @param entityName The name of the entity class
* @param id The id of the entity to edit
* @param req The current Lynx request
*/
BaseUIController.prototype.performEntityEdit = function (entityName, id, req) {
return __awaiter(this, void 0, void 0, function () {
var metadata, entity, _a, _b, originalData, updated, e_5, errorField, field, _c, isPopup, fields, ctx;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
metadata = this.retrieveMetadata(entityName);
if (!metadata) {
throw this.error(404, 'not found');
}
if (!(!id || id == '0')) return [3 /*break*/, 1];
entity = new (this.retrieveEntityClass(entityName))();
return [3 /*break*/, 3];
case 1: return [4 /*yield*/, this.retrieveEntity(entityName, id)];
case 2:
entity = (_d.sent());
if (!entity) {
throw this.error(404, 'not found');
}
_d.label = 3;
case 3:
_a = [__assign({}, metadata)];
_b = {};
return [4 /*yield*/, this.generateContextFields(metadata, req, entity)];
case 4:
metadata = __assign.apply(void 0, _a.concat([(_b.fields = _d.sent(), _b)]));
return [4 /*yield*/, this.cleanData(req, entity, metadata)];
case 5:
originalData = _d.sent();
return [4 /*yield*/, this.setData(req, entity, req.body, metadata)];
case 6:
_d.sent();
_d.label = 7;
case 7:
_d.trys.push([7, 9, , 12]);
return [4 /*yield*/, controller_1.Controller.saveEntity(entity, req)];
case 8:
updated = _d.sent();
return [3 /*break*/, 12];
case 9:
e_5 = _d.sent();
this.logger.log('error saving the entity', e_5);
try {
errorField = /'([a-zA-Z0-0_)]+)'/.exec(e_5.message)[1];
field = metadata.fields[errorField];
if (field) {
this.addErrorMessage(this.app.translate('admin-ui.error-on-field', req) +
this.app.translate(field.name, req), req);
}
else {
this.addErrorMessage(e_5.message, req);
}
}
catch (ee) {
this.logger.error(e_5);
this.addErrorMessage(e_5.message, req);
}
_c = [{}];
return [4 /*yield*/, this.generateContextMetadata(metadata, req)];
case 10:
metadata = __assign.apply(void 0, _c.concat([(_d.sent())]));
isPopup = req.query.popup;
return [4 /*yield*/, this.generateContextFields(metadata, req, entity)];
case 11:
fields = _d.sent();
metadata.fields = fields;
ctx = this.generateCxtForEditingRender(__assign(__assign({}, originalData), req.body), isPopup, metadata, fields);
return [2 /*return*/, this.render(isPopup
? metadata.classParameters.popupEditorTemplate
: metadata.classParameters.editorTemplate, req, ctx)];
case 12:
if (id == 0) {
this.addSuccessMessage('admin-ui.success-create', req);
}
else {
this.addSuccessMessage('admin-ui.success-update', req);
}
return [2 /*return*/, this.redirect('adminUI.details', {
entityName: entityName,
id: updated.id,
})];
}
});
});
};
return BaseUIController;
}(controller_1.Controller));
exports.BaseUIController = BaseUIController;