@reldens/cms 0.25.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/reldens-cms-generate-entities.js +1 -1
- package/bin/reldens-cms.js +1 -1
- package/lib/admin-manager/contents-builder.js +258 -258
- package/lib/admin-manager/router-contents.js +724 -720
- package/lib/admin-manager/router.js +228 -228
- package/lib/admin-manager.js +282 -279
- package/package.json +3 -3
|
@@ -1,720 +1,724 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Reldens - RouterContents
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { PageRangeProvider, Logger, sc } = require('@reldens/utils');
|
|
8
|
-
const { FileHandler } = require('@reldens/server-utils');
|
|
9
|
-
const { AdminFiltersManager } = require('./admin-filters-manager');
|
|
10
|
-
|
|
11
|
-
class RouterContents
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
constructor(props)
|
|
15
|
-
{
|
|
16
|
-
this.dataServer = props.dataServer;
|
|
17
|
-
this.translations = props.translations;
|
|
18
|
-
this.rootPath = props.rootPath;
|
|
19
|
-
this.relations = props.relations;
|
|
20
|
-
this.resourcesByReference = props.resourcesByReference;
|
|
21
|
-
this.adminFilesContents = props.adminFilesContents;
|
|
22
|
-
this.autoSyncDistCallback = props.autoSyncDistCallback;
|
|
23
|
-
this.viewPath = props.viewPath;
|
|
24
|
-
this.editPath = props.editPath;
|
|
25
|
-
this.deletePath = props.deletePath;
|
|
26
|
-
this.emitEvent = props.emitEvent;
|
|
27
|
-
this.adminContentsRender = props.adminContentsRender;
|
|
28
|
-
this.adminContentsRenderRoute = props.adminContentsRenderRoute;
|
|
29
|
-
this.adminContentsEntities = props.adminContentsEntities;
|
|
30
|
-
this.adminContentsSideBar = props.adminContentsSideBar;
|
|
31
|
-
this.fetchTranslation = props.fetchTranslation;
|
|
32
|
-
this.fetchEntityIdPropertyKey = props.fetchEntityIdPropertyKey;
|
|
33
|
-
this.fetchUploadProperties = props.fetchUploadProperties;
|
|
34
|
-
this.uploaderFactory = props.uploaderFactory;
|
|
35
|
-
this.filtersManager = new AdminFiltersManager();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async generateListRouteContent(req, driverResource, entityPath)
|
|
39
|
-
{
|
|
40
|
-
let currentPage = Number(req?.query?.page || 1);
|
|
41
|
-
let pageSize = Number(req?.query?.pageSize || 25);
|
|
42
|
-
let shouldClearFilters = 'true' === req?.query?.clearFilters;
|
|
43
|
-
let sessionFilters = this.filtersManager.getFiltersFromSession(req, entityPath);
|
|
44
|
-
let filtersFromParams = shouldClearFilters ? {} : req?.body?.filters || req?.query?.filters || {};
|
|
45
|
-
let entityFilterTerm = shouldClearFilters ? '' : sc.get(req?.body, 'entityFilterTerm', '');
|
|
46
|
-
if(shouldClearFilters){
|
|
47
|
-
this.filtersManager.clearFiltersFromSession(req, entityPath);
|
|
48
|
-
}
|
|
49
|
-
let hasNewEntityFilterTerm = entityFilterTerm && '' !== entityFilterTerm;
|
|
50
|
-
let hasNewToggleFilters = Object.keys(filtersFromParams).length > 0;
|
|
51
|
-
if(hasNewEntityFilterTerm && hasNewToggleFilters){
|
|
52
|
-
filtersFromParams = {};
|
|
53
|
-
hasNewToggleFilters = false;
|
|
54
|
-
}
|
|
55
|
-
if(hasNewEntityFilterTerm || hasNewToggleFilters){
|
|
56
|
-
let filtersToSave = {
|
|
57
|
-
regular: hasNewToggleFilters ? filtersFromParams : {},
|
|
58
|
-
entityFilterTerm: hasNewEntityFilterTerm ? entityFilterTerm : ''
|
|
59
|
-
};
|
|
60
|
-
this.filtersManager.saveFiltersToSession(req, entityPath, filtersToSave);
|
|
61
|
-
sessionFilters = filtersToSave;
|
|
62
|
-
}
|
|
63
|
-
let mergedFilters = shouldClearFilters ? {} : Object.assign({}, sessionFilters.regular || {});
|
|
64
|
-
let finalEntityFilterTerm = shouldClearFilters ? '' : sessionFilters.entityFilterTerm || '';
|
|
65
|
-
let filters = this.filtersManager.prepareFilters(mergedFilters, driverResource);
|
|
66
|
-
let textFilters = this.filtersManager.prepareTextFilters(finalEntityFilterTerm, driverResource);
|
|
67
|
-
let combinedFilters = this.filtersManager.combineFilters(filters, textFilters);
|
|
68
|
-
let totalEntities = await this.countTotalEntities(driverResource, combinedFilters);
|
|
69
|
-
let totalPages = totalEntities <= pageSize ? 1 : Math.ceil(totalEntities / pageSize);
|
|
70
|
-
let renderedPagination = '';
|
|
71
|
-
for(let paginationItem of PageRangeProvider.fetch(currentPage, totalPages)){
|
|
72
|
-
let paginationUrl = this.rootPath+'/'+driverResource.entityPath+'?page='+ paginationItem.value;
|
|
73
|
-
if(finalEntityFilterTerm){
|
|
74
|
-
paginationUrl += '&entityFilterTerm='+encodeURIComponent(finalEntityFilterTerm);
|
|
75
|
-
}
|
|
76
|
-
for(let filterKey of Object.keys(mergedFilters)){
|
|
77
|
-
if(mergedFilters[filterKey]){
|
|
78
|
-
paginationUrl += '&filters['+filterKey+']='+encodeURIComponent(mergedFilters[filterKey]);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
renderedPagination += await this.adminContentsRender(
|
|
82
|
-
this.adminFilesContents.fields.view['link'],
|
|
83
|
-
{
|
|
84
|
-
fieldName: paginationItem.label,
|
|
85
|
-
fieldValue: paginationUrl,
|
|
86
|
-
fieldOriginalValue: paginationItem.value,
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
let listProperties = {
|
|
91
|
-
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath,
|
|
92
|
-
entityFilterTermValue: finalEntityFilterTerm || ''
|
|
93
|
-
};
|
|
94
|
-
await this.emitEvent('reldens.adminListPropertiesPopulation', {
|
|
95
|
-
req,
|
|
96
|
-
driverResource,
|
|
97
|
-
listProperties
|
|
98
|
-
});
|
|
99
|
-
return await this.adminContentsRenderRoute(
|
|
100
|
-
await this.adminContentsRender(
|
|
101
|
-
this.adminContentsEntities()[entityPath].list,
|
|
102
|
-
Object.assign({
|
|
103
|
-
list: await this.adminContentsRender(this.adminFilesContents.listContent, {
|
|
104
|
-
deletePath: this.rootPath + '/' + driverResource.entityPath + this.deletePath,
|
|
105
|
-
fieldsHeaders: driverResource.options.listProperties.map((property) => {
|
|
106
|
-
let propertyTitle = this.fetchTranslation(property, driverResource.id());
|
|
107
|
-
let alias = this.fetchTranslation(
|
|
108
|
-
driverResource.options.properties[property]?.alias || '',
|
|
109
|
-
driverResource.id()
|
|
110
|
-
);
|
|
111
|
-
return {
|
|
112
|
-
name: property,
|
|
113
|
-
value: '' !== alias ? alias + ' ('+propertyTitle+')' : propertyTitle
|
|
114
|
-
};
|
|
115
|
-
}),
|
|
116
|
-
rows: await this.loadEntitiesForList(
|
|
117
|
-
driverResource,
|
|
118
|
-
pageSize,
|
|
119
|
-
currentPage,
|
|
120
|
-
req,
|
|
121
|
-
combinedFilters
|
|
122
|
-
)
|
|
123
|
-
}),
|
|
124
|
-
pagination: renderedPagination,
|
|
125
|
-
extraContentForList: sc.get(listProperties, 'extraContentForList', ''),
|
|
126
|
-
entityFilterTermValue: listProperties.entityFilterTermValue
|
|
127
|
-
}, ...driverResource.options.filterProperties.map((property) => {
|
|
128
|
-
let filterValue = (mergedFilters[property] || '');
|
|
129
|
-
return {[property]: '' === filterValue ? '' : 'value="'+filterValue+'"'};
|
|
130
|
-
}))
|
|
131
|
-
),
|
|
132
|
-
this.adminContentsSideBar()
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async generateViewRouteContent(req, driverResource, entityPath)
|
|
137
|
-
{
|
|
138
|
-
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
139
|
-
let id = (sc.get(req.query, idProperty, ''));
|
|
140
|
-
if('' === id){
|
|
141
|
-
Logger.error('Missing ID on view route.', entityPath, id, idProperty);
|
|
142
|
-
return '';
|
|
143
|
-
}
|
|
144
|
-
let loadedEntity = await this.loadEntityById(driverResource, id);
|
|
145
|
-
if(!loadedEntity){
|
|
146
|
-
Logger.critical('Entity not found on view route.', entityPath, id, idProperty);
|
|
147
|
-
return '';
|
|
148
|
-
}
|
|
149
|
-
let renderedViewProperties = {
|
|
150
|
-
entityEditRoute: this.generateEntityRoute('editPath', driverResource, idProperty, loadedEntity),
|
|
151
|
-
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath,
|
|
152
|
-
id
|
|
153
|
-
};
|
|
154
|
-
let entitySerializedData = {};
|
|
155
|
-
for(let propertyKey of driverResource.options.showProperties){
|
|
156
|
-
let property = driverResource.options.properties[propertyKey];
|
|
157
|
-
let {fieldValue, fieldName} = this.generatePropertyRenderedValueWithLabel(
|
|
158
|
-
loadedEntity,
|
|
159
|
-
propertyKey,
|
|
160
|
-
property
|
|
161
|
-
);
|
|
162
|
-
entitySerializedData[fieldName] = fieldValue;
|
|
163
|
-
renderedViewProperties[propertyKey] = await this.generatePropertyRenderedValue(
|
|
164
|
-
fieldValue,
|
|
165
|
-
fieldName,
|
|
166
|
-
property,
|
|
167
|
-
'view'
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
let extraDataEvent = {entitySerializedData, entityId: driverResource.id(), entity: loadedEntity};
|
|
171
|
-
await this.emitEvent('adminEntityExtraData', extraDataEvent);
|
|
172
|
-
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
173
|
-
.replace(/"/g, '"');
|
|
174
|
-
await this.emitEvent('reldens.adminViewPropertiesPopulation', {
|
|
175
|
-
idProperty,
|
|
176
|
-
req,
|
|
177
|
-
driverResource,
|
|
178
|
-
loadedEntity,
|
|
179
|
-
renderedViewProperties
|
|
180
|
-
});
|
|
181
|
-
return await this.adminContentsRenderRoute(
|
|
182
|
-
await this.adminContentsRender(this.adminContentsEntities()[entityPath].view, renderedViewProperties),
|
|
183
|
-
this.adminContentsSideBar()
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async generateEditRouteContent(req, driverResource, entityPath)
|
|
188
|
-
{
|
|
189
|
-
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
190
|
-
let idValue = String(sc.get(req?.query, idProperty, ''));
|
|
191
|
-
let loadedEntity = !idValue ? null : await this.loadEntityById(driverResource, idValue);
|
|
192
|
-
let renderedEditProperties = {
|
|
193
|
-
idValue,
|
|
194
|
-
idProperty,
|
|
195
|
-
idPropertyLabel: this.fetchTranslation(idProperty),
|
|
196
|
-
templateTitle: (!idValue ? 'Create' : 'Edit')+' '+this.translations.labels[driverResource.id()],
|
|
197
|
-
entityViewRoute: !idValue
|
|
198
|
-
? this.rootPath+'/'+driverResource.entityPath
|
|
199
|
-
: this.generateEntityRoute('viewPath', driverResource, idProperty, loadedEntity)
|
|
200
|
-
};
|
|
201
|
-
await this.emitEvent('reldens.adminEditPropertiesPopulation', {
|
|
202
|
-
req,
|
|
203
|
-
driverResource,
|
|
204
|
-
renderedEditProperties,
|
|
205
|
-
loadedEntity
|
|
206
|
-
});
|
|
207
|
-
for(let propertyKey of Object.keys(driverResource.options.properties)){
|
|
208
|
-
let property = driverResource.options.properties[propertyKey];
|
|
209
|
-
let fieldDisabled = -1 === driverResource.options.editProperties.indexOf(propertyKey);
|
|
210
|
-
renderedEditProperties[propertyKey] = await this.adminContentsRender(
|
|
211
|
-
this.adminFilesContents.fields.edit[this.propertyType(property, 'edit')],
|
|
212
|
-
{
|
|
213
|
-
fieldName: propertyKey,
|
|
214
|
-
fieldValue: await this.generatePropertyEditRenderedValue(
|
|
215
|
-
loadedEntity,
|
|
216
|
-
propertyKey,
|
|
217
|
-
property,
|
|
218
|
-
fieldDisabled
|
|
219
|
-
),
|
|
220
|
-
fieldDisabled: fieldDisabled ? ' disabled="disabled"' : '',
|
|
221
|
-
required: (!property.isUpload || !loadedEntity) && property.isRequired ? ' required="required"' : '',
|
|
222
|
-
multiple: property.isArray ? ' multiple="multiple"' : '',
|
|
223
|
-
inputType: this.getInputType(property, fieldDisabled)
|
|
224
|
-
}
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
return await this.adminContentsRenderRoute(
|
|
228
|
-
await this.adminContentsRender(this.adminContentsEntities()[entityPath].edit, renderedEditProperties),
|
|
229
|
-
this.adminContentsSideBar()
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async processDeleteEntities(req, res, driverResource, entityPath)
|
|
234
|
-
{
|
|
235
|
-
let ids = req?.body?.ids;
|
|
236
|
-
if('string' === typeof ids){
|
|
237
|
-
ids = ids.split(',');
|
|
238
|
-
}
|
|
239
|
-
let redirectPath = this.rootPath+'/'+entityPath+'?result=';
|
|
240
|
-
if(!ids || 0 === ids.length){
|
|
241
|
-
return redirectPath + 'errorMissingId';
|
|
242
|
-
}
|
|
243
|
-
try {
|
|
244
|
-
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
245
|
-
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
246
|
-
let idsFilter = {[idProperty]: {operator: 'IN', value: ids}};
|
|
247
|
-
let loadedEntities = await entityRepository.load(idsFilter);
|
|
248
|
-
await this.deleteEntitiesRelatedFiles(driverResource, loadedEntities);
|
|
249
|
-
let deleteResult = await entityRepository.delete(idsFilter);
|
|
250
|
-
return redirectPath + (deleteResult ? 'success' : 'errorStorageFailure');
|
|
251
|
-
} catch (error) {
|
|
252
|
-
return redirectPath + 'errorDeleteFailure';
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async processSaveEntity(req, res, driverResource, entityPath)
|
|
257
|
-
{
|
|
258
|
-
let uploadProperties = this.fetchUploadProperties(driverResource);
|
|
259
|
-
if(0 < Object.keys(uploadProperties).length){
|
|
260
|
-
if(this.uploaderFactory
|
|
261
|
-
Logger.error('Upload factory error detected', this.uploaderFactory.error);
|
|
262
|
-
return this.rootPath+'/'+entityPath+'?result=uploadError&error='
|
|
263
|
-
+encodeURIComponent(this.uploaderFactory.error.message);
|
|
264
|
-
}
|
|
265
|
-
let uploadValidationResult = this.validateUploadedFiles(req, uploadProperties);
|
|
266
|
-
if(!uploadValidationResult.success){
|
|
267
|
-
Logger.error('Upload validation failed', uploadValidationResult.error);
|
|
268
|
-
return this.rootPath+'/'+entityPath+'?result=uploadValidationError&error='
|
|
269
|
-
+encodeURIComponent(uploadValidationResult.error.message);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
273
|
-
let id = (req?.body[idProperty] || '');
|
|
274
|
-
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
275
|
-
let entityDataPatch = this.preparePatchData(
|
|
276
|
-
driverResource,
|
|
277
|
-
idProperty,
|
|
278
|
-
req,
|
|
279
|
-
driverResource.options.properties,
|
|
280
|
-
id
|
|
281
|
-
);
|
|
282
|
-
if(!entityDataPatch){
|
|
283
|
-
Logger.error('Bad patch data.', entityDataPatch);
|
|
284
|
-
return this.rootPath+'/'+entityPath+'?result=saveBadPatchData';
|
|
285
|
-
}
|
|
286
|
-
try {
|
|
287
|
-
let saveResult = await this.saveEntity(id, entityRepository, entityDataPatch);
|
|
288
|
-
if(!saveResult){
|
|
289
|
-
Logger.error('Save result error.', saveResult, entityDataPatch);
|
|
290
|
-
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
291
|
-
+'?result=saveEntityStorageError';
|
|
292
|
-
}
|
|
293
|
-
await this.emitEvent('reldens.adminAfterEntitySave', {
|
|
294
|
-
req,
|
|
295
|
-
res,
|
|
296
|
-
driverResource,
|
|
297
|
-
entityPath,
|
|
298
|
-
entityData: saveResult
|
|
299
|
-
});
|
|
300
|
-
if(sc.isFunction(this.autoSyncDistCallback)){
|
|
301
|
-
let uploadProperties = this.fetchUploadProperties(driverResource);
|
|
302
|
-
if(0 < Object.keys(uploadProperties).length){
|
|
303
|
-
for(let uploadPropertyKey of Object.keys(uploadProperties)){
|
|
304
|
-
let property = uploadProperties[uploadPropertyKey];
|
|
305
|
-
await this.autoSyncDistCallback(
|
|
306
|
-
property.bucket,
|
|
307
|
-
saveResult[uploadPropertyKey],
|
|
308
|
-
property.distFolder
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
let saveAction = sc.get(req.body, 'saveAction', 'save');
|
|
314
|
-
if('saveAndContinue' === saveAction){
|
|
315
|
-
return this.generateEntityRoute('editPath', driverResource, idProperty, saveResult) +'&result=success';
|
|
316
|
-
}
|
|
317
|
-
if('saveAndGoBack' === saveAction){
|
|
318
|
-
return this.rootPath+'/'+entityPath+'?result=success';
|
|
319
|
-
}
|
|
320
|
-
return this.generateEntityRoute('viewPath', driverResource, idProperty, saveResult) +'&result=success';
|
|
321
|
-
} catch (error) {
|
|
322
|
-
Logger.error('Save entity error.', error);
|
|
323
|
-
return this.rootPath+'/'+entityPath+'?result=saveEntityError';
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
validateUploadedFiles(req, uploadProperties)
|
|
328
|
-
{
|
|
329
|
-
for(let uploadPropertyKey of Object.keys(uploadProperties)){
|
|
330
|
-
let property = uploadProperties[uploadPropertyKey];
|
|
331
|
-
let uploadedFiles = req.files?.[uploadPropertyKey];
|
|
332
|
-
if(!uploadedFiles || 0 === uploadedFiles.length){
|
|
333
|
-
if(property.isRequired){
|
|
334
|
-
return {success: false, error: {message: 'Required file upload missing: '+uploadPropertyKey}};
|
|
335
|
-
}
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
for(let file of uploadedFiles){
|
|
339
|
-
let filePath = file.path;
|
|
340
|
-
if(!FileHandler.exists(filePath)){
|
|
341
|
-
return {
|
|
342
|
-
success: false,
|
|
343
|
-
error: {
|
|
344
|
-
message: 'Uploaded file not found in bucket: '+file.filename,
|
|
345
|
-
filePath,
|
|
346
|
-
bucket: property.bucket
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
if(!FileHandler.isFile(filePath)){
|
|
351
|
-
return {
|
|
352
|
-
success: false,
|
|
353
|
-
error: {message: 'Uploaded file is not a valid file: '+file.filename, filePath}
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return {success: true};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async countTotalEntities(driverResource, filters)
|
|
362
|
-
{
|
|
363
|
-
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
364
|
-
if(!entityRepository){
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
return await entityRepository.count(filters);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
async loadEntitiesForList(driverResource, pageSize, currentPage, req, filters)
|
|
371
|
-
{
|
|
372
|
-
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
373
|
-
entityRepository.limit = pageSize;
|
|
374
|
-
if(1 < currentPage){
|
|
375
|
-
entityRepository.offset = (currentPage - 1) * pageSize;
|
|
376
|
-
}
|
|
377
|
-
entityRepository.sortBy = req?.body?.sortBy || false;
|
|
378
|
-
entityRepository.sortDirection = req?.body?.sortDirection || false;
|
|
379
|
-
let loadedEntities = await entityRepository.loadWithRelations(filters, []);
|
|
380
|
-
entityRepository.limit = 0;
|
|
381
|
-
entityRepository.offset = 0;
|
|
382
|
-
entityRepository.sortBy = false;
|
|
383
|
-
entityRepository.sortDirection = false;
|
|
384
|
-
let entityRows = [];
|
|
385
|
-
let resourceProperties = driverResource.options?.properties;
|
|
386
|
-
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
387
|
-
for(let entity of loadedEntities){
|
|
388
|
-
let fields = [];
|
|
389
|
-
for(let property of driverResource.options.listProperties){
|
|
390
|
-
let {fieldValue, fieldName} = this.generatePropertyRenderedValueWithLabel(
|
|
391
|
-
entity,
|
|
392
|
-
property,
|
|
393
|
-
resourceProperties[property]
|
|
394
|
-
);
|
|
395
|
-
fields.push({
|
|
396
|
-
name: property,
|
|
397
|
-
value: await this.generatePropertyRenderedValue(
|
|
398
|
-
fieldValue,
|
|
399
|
-
fieldName,
|
|
400
|
-
resourceProperties[property]
|
|
401
|
-
),
|
|
402
|
-
viewLink: '' !== idProperty
|
|
403
|
-
? this.generateEntityRoute('viewPath', driverResource, idProperty, entity)
|
|
404
|
-
: ''
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
entityRows.push({
|
|
408
|
-
fields,
|
|
409
|
-
editLink: '' !== idProperty
|
|
410
|
-
? this.generateEntityRoute('editPath', driverResource, idProperty, entity)
|
|
411
|
-
: '',
|
|
412
|
-
deleteLink: this.rootPath + '/' + driverResource.entityPath + this.deletePath,
|
|
413
|
-
id: entity[idProperty]
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
return entityRows;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async loadEntityById(driverResource, id)
|
|
420
|
-
{
|
|
421
|
-
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
422
|
-
if(!entityRepository){
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
await this.emitEvent('reldens.adminBeforeEntityLoad', {
|
|
426
|
-
driverResource,
|
|
427
|
-
entityId: id
|
|
428
|
-
});
|
|
429
|
-
return await entityRepository.loadByIdWithRelations(id);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
async saveEntity(id, entityRepository, entityDataPatch)
|
|
433
|
-
{
|
|
434
|
-
if('' === id){
|
|
435
|
-
return entityRepository.create(entityDataPatch);
|
|
436
|
-
}
|
|
437
|
-
return entityRepository.updateById(id, entityDataPatch);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
async deleteEntitiesRelatedFiles(driverResource, entities)
|
|
441
|
-
{
|
|
442
|
-
for(let propertyKey of Object.keys(driverResource.options.properties)){
|
|
443
|
-
let property = driverResource.options.properties[propertyKey];
|
|
444
|
-
if(!property.isUpload){
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
for(let entity of entities){
|
|
448
|
-
let bucket = sc.get(property, 'bucket', '');
|
|
449
|
-
if(!property.isArray){
|
|
450
|
-
FileHandler.remove([bucket, entity[propertyKey]]);
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
for(let entityFile of entity[propertyKey].split(property.isArray)){
|
|
454
|
-
FileHandler.remove([bucket, entityFile]);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
preparePatchData(driverResource, idProperty, req, resourceProperties, id)
|
|
461
|
-
{
|
|
462
|
-
let entityDataPatch = {};
|
|
463
|
-
for(let i of Object.keys(resourceProperties)){
|
|
464
|
-
if(i === idProperty){
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
let property = resourceProperties[i];
|
|
468
|
-
let shouldProcess = driverResource.options.editProperties.includes(i);
|
|
469
|
-
if(!id && !shouldProcess && property.isRequired && 'reference' === property.type){
|
|
470
|
-
shouldProcess = true;
|
|
471
|
-
}
|
|
472
|
-
if(!shouldProcess){
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
let propertyUpdateValue = sc.get(req.body, i, null);
|
|
476
|
-
if('null' === propertyUpdateValue){
|
|
477
|
-
propertyUpdateValue = null;
|
|
478
|
-
}
|
|
479
|
-
let propertyType = sc.get(property, 'type', 'string');
|
|
480
|
-
if(property.isUpload){
|
|
481
|
-
propertyType = 'upload';
|
|
482
|
-
propertyUpdateValue = this.prepareUploadPatchData(req, i, propertyUpdateValue, property);
|
|
483
|
-
}
|
|
484
|
-
if('boolean' === propertyType){
|
|
485
|
-
propertyUpdateValue = '1' === propertyUpdateValue || 'on' === propertyUpdateValue;
|
|
486
|
-
}
|
|
487
|
-
let isEmpty = '' === propertyUpdateValue;
|
|
488
|
-
let isNull = null === propertyUpdateValue;
|
|
489
|
-
if('number' === propertyType && !isNull && !isEmpty){
|
|
490
|
-
let numValue = Number(propertyUpdateValue);
|
|
491
|
-
if(isNaN(numValue)){
|
|
492
|
-
Logger.critical(
|
|
493
|
-
'Invalid number value for property.',
|
|
494
|
-
{propertyKey: i, value: propertyUpdateValue, entity: driverResource.entityKey}
|
|
495
|
-
);
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
propertyUpdateValue = numValue;
|
|
499
|
-
}
|
|
500
|
-
if('string' === propertyType && !isNull && !isEmpty){
|
|
501
|
-
propertyUpdateValue = String(propertyUpdateValue);
|
|
502
|
-
}
|
|
503
|
-
if(isEmpty && !property.isRequired){
|
|
504
|
-
propertyUpdateValue = null;
|
|
505
|
-
}
|
|
506
|
-
let isUploadCreate = property.isUpload && !id;
|
|
507
|
-
if(property.isRequired && (isNull || isEmpty) && (!property.isUpload || isUploadCreate)){
|
|
508
|
-
// missing required fields would break the update:
|
|
509
|
-
Logger.critical(
|
|
510
|
-
'Missing property on prepare patch data.',
|
|
511
|
-
{propertyKey: i, config: property, propertyUpdateValue, entity: driverResource.entityKey}
|
|
512
|
-
);
|
|
513
|
-
return false;
|
|
514
|
-
}
|
|
515
|
-
if(!property.isUpload || (property.isUpload && !isNull)){
|
|
516
|
-
entityDataPatch[i] = propertyUpdateValue;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
return entityDataPatch;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
prepareUploadPatchData(req, i, propertyUpdateValue, property)
|
|
524
|
-
{
|
|
525
|
-
let filesData = sc.get(req.files, i, null);
|
|
526
|
-
if(null === filesData){
|
|
527
|
-
return null;
|
|
528
|
-
}
|
|
529
|
-
let fileNames = [];
|
|
530
|
-
for(let file of filesData){
|
|
531
|
-
fileNames.push(file.filename);
|
|
532
|
-
}
|
|
533
|
-
return fileNames.join(property.isArray);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
async generatePropertyRenderedValue(fieldValue, fieldName, resourceProperty, templateType)
|
|
537
|
-
{
|
|
538
|
-
let fieldOriginalValue = fieldValue;
|
|
539
|
-
if('view' === templateType){
|
|
540
|
-
if(resourceProperty.isArray){
|
|
541
|
-
fieldValue = fieldValue.split(resourceProperty.isArray).map((value) => {
|
|
542
|
-
let target = resourceProperty.isUpload ? ' target="_blank"' : '';
|
|
543
|
-
let fieldValuePart = resourceProperty.isUpload && resourceProperty.bucketPath
|
|
544
|
-
? resourceProperty.bucketPath+value
|
|
545
|
-
: value;
|
|
546
|
-
return {fieldValuePart, fieldOriginalValuePart: value, target};
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
if(!resourceProperty.isArray && resourceProperty.isUpload){
|
|
550
|
-
fieldValue = resourceProperty.bucketPath+fieldValue;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
return await this.adminContentsRender(
|
|
554
|
-
this.adminFilesContents.fields.view[this.propertyType(resourceProperty, templateType, fieldValue)],
|
|
555
|
-
{fieldName, fieldValue, fieldOriginalValue, target: ' target="_blank"'}
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
generatePropertyRenderedValueWithLabel(entity, propertyKey, resourceProperty)
|
|
560
|
-
{
|
|
561
|
-
let fieldValue = (0 === entity[propertyKey] ? '0' : entity[propertyKey] || '');
|
|
562
|
-
if((sc.isObject(fieldValue) || sc.isArray(fieldValue)) && 'json' === resourceProperty.dbType){
|
|
563
|
-
fieldValue = sc.toJsonString(fieldValue);
|
|
564
|
-
}
|
|
565
|
-
let fieldName = propertyKey;
|
|
566
|
-
if('boolean' === resourceProperty.type){
|
|
567
|
-
fieldValue = 1 === entity[propertyKey]
|
|
568
|
-
|| '1' === entity[propertyKey]
|
|
569
|
-
|| true === entity[propertyKey] ? 'Yes' : 'No';
|
|
570
|
-
}
|
|
571
|
-
if('datetime' === resourceProperty.type){
|
|
572
|
-
fieldValue = '' !== fieldValue ? sc.formatDate(new Date(fieldValue)) : '';
|
|
573
|
-
}
|
|
574
|
-
if('reference' === resourceProperty.type){
|
|
575
|
-
let relationEntity = entity[resourceProperty.alias || resourceProperty.reference];
|
|
576
|
-
if(relationEntity){
|
|
577
|
-
let relation = this.relations()[resourceProperty.reference];
|
|
578
|
-
if(relation){
|
|
579
|
-
let relationTitleProperty = relation[resourceProperty.alias || resourceProperty.reference];
|
|
580
|
-
if(relationTitleProperty && '' !== String(relationEntity[relationTitleProperty] || '')){
|
|
581
|
-
fieldName = relationTitleProperty;
|
|
582
|
-
fieldValue = relationEntity[relationTitleProperty]+(' ('+fieldValue+')');
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
if(resourceProperty.availableValues){
|
|
588
|
-
let optionData = resourceProperty.availableValues.filter((availableValue) => {
|
|
589
|
-
return String(availableValue.value) === String(fieldValue);
|
|
590
|
-
}).shift();
|
|
591
|
-
if(optionData){
|
|
592
|
-
fieldValue = optionData.label + ' (' + fieldValue + ')';
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return {fieldValue, fieldName};
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
async generatePropertyEditRenderedValue(entity, propertyKey, resourceProperty)
|
|
599
|
-
{
|
|
600
|
-
let entityPropertyValue = sc.get(entity, propertyKey, null);
|
|
601
|
-
let fieldValue = (0 === entityPropertyValue ? '0' : entityPropertyValue || '');
|
|
602
|
-
if('null' === fieldValue){
|
|
603
|
-
fieldValue = '';
|
|
604
|
-
}
|
|
605
|
-
if((sc.isObject(fieldValue) || sc.isArray(fieldValue)) && 'json' === resourceProperty.dbType){
|
|
606
|
-
fieldValue = sc.toJsonString(fieldValue);
|
|
607
|
-
}
|
|
608
|
-
if('boolean' === resourceProperty.type){
|
|
609
|
-
fieldValue = 1 === entityPropertyValue
|
|
610
|
-
|| '1' === entityPropertyValue
|
|
611
|
-
|| true === entityPropertyValue ? ' checked="checked"' : '';
|
|
612
|
-
}
|
|
613
|
-
if('datetime' === resourceProperty.type){
|
|
614
|
-
fieldValue = !entityPropertyValue || '' === entityPropertyValue
|
|
615
|
-
? ''
|
|
616
|
-
: sc.formatDate(new Date(entityPropertyValue), 'Y-m-d H:i:s');
|
|
617
|
-
}
|
|
618
|
-
if('reference' === resourceProperty.type){
|
|
619
|
-
let relationDriverResource = this.resourcesByReference()[resourceProperty.reference];
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
let
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
if(entity){
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - RouterContents
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { PageRangeProvider, Logger, sc } = require('@reldens/utils');
|
|
8
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
9
|
+
const { AdminFiltersManager } = require('./admin-filters-manager');
|
|
10
|
+
|
|
11
|
+
class RouterContents
|
|
12
|
+
{
|
|
13
|
+
|
|
14
|
+
constructor(props)
|
|
15
|
+
{
|
|
16
|
+
this.dataServer = props.dataServer;
|
|
17
|
+
this.translations = props.translations;
|
|
18
|
+
this.rootPath = props.rootPath;
|
|
19
|
+
this.relations = props.relations;
|
|
20
|
+
this.resourcesByReference = props.resourcesByReference;
|
|
21
|
+
this.adminFilesContents = props.adminFilesContents;
|
|
22
|
+
this.autoSyncDistCallback = props.autoSyncDistCallback;
|
|
23
|
+
this.viewPath = props.viewPath;
|
|
24
|
+
this.editPath = props.editPath;
|
|
25
|
+
this.deletePath = props.deletePath;
|
|
26
|
+
this.emitEvent = props.emitEvent;
|
|
27
|
+
this.adminContentsRender = props.adminContentsRender;
|
|
28
|
+
this.adminContentsRenderRoute = props.adminContentsRenderRoute;
|
|
29
|
+
this.adminContentsEntities = props.adminContentsEntities;
|
|
30
|
+
this.adminContentsSideBar = props.adminContentsSideBar;
|
|
31
|
+
this.fetchTranslation = props.fetchTranslation;
|
|
32
|
+
this.fetchEntityIdPropertyKey = props.fetchEntityIdPropertyKey;
|
|
33
|
+
this.fetchUploadProperties = props.fetchUploadProperties;
|
|
34
|
+
this.uploaderFactory = props.uploaderFactory;
|
|
35
|
+
this.filtersManager = new AdminFiltersManager();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async generateListRouteContent(req, driverResource, entityPath)
|
|
39
|
+
{
|
|
40
|
+
let currentPage = Number(req?.query?.page || 1);
|
|
41
|
+
let pageSize = Number(req?.query?.pageSize || 25);
|
|
42
|
+
let shouldClearFilters = 'true' === req?.query?.clearFilters;
|
|
43
|
+
let sessionFilters = this.filtersManager.getFiltersFromSession(req, entityPath);
|
|
44
|
+
let filtersFromParams = shouldClearFilters ? {} : req?.body?.filters || req?.query?.filters || {};
|
|
45
|
+
let entityFilterTerm = shouldClearFilters ? '' : sc.get(req?.body, 'entityFilterTerm', '');
|
|
46
|
+
if(shouldClearFilters){
|
|
47
|
+
this.filtersManager.clearFiltersFromSession(req, entityPath);
|
|
48
|
+
}
|
|
49
|
+
let hasNewEntityFilterTerm = entityFilterTerm && '' !== entityFilterTerm;
|
|
50
|
+
let hasNewToggleFilters = Object.keys(filtersFromParams).length > 0;
|
|
51
|
+
if(hasNewEntityFilterTerm && hasNewToggleFilters){
|
|
52
|
+
filtersFromParams = {};
|
|
53
|
+
hasNewToggleFilters = false;
|
|
54
|
+
}
|
|
55
|
+
if(hasNewEntityFilterTerm || hasNewToggleFilters){
|
|
56
|
+
let filtersToSave = {
|
|
57
|
+
regular: hasNewToggleFilters ? filtersFromParams : {},
|
|
58
|
+
entityFilterTerm: hasNewEntityFilterTerm ? entityFilterTerm : ''
|
|
59
|
+
};
|
|
60
|
+
this.filtersManager.saveFiltersToSession(req, entityPath, filtersToSave);
|
|
61
|
+
sessionFilters = filtersToSave;
|
|
62
|
+
}
|
|
63
|
+
let mergedFilters = shouldClearFilters ? {} : Object.assign({}, sessionFilters.regular || {});
|
|
64
|
+
let finalEntityFilterTerm = shouldClearFilters ? '' : sessionFilters.entityFilterTerm || '';
|
|
65
|
+
let filters = this.filtersManager.prepareFilters(mergedFilters, driverResource);
|
|
66
|
+
let textFilters = this.filtersManager.prepareTextFilters(finalEntityFilterTerm, driverResource);
|
|
67
|
+
let combinedFilters = this.filtersManager.combineFilters(filters, textFilters);
|
|
68
|
+
let totalEntities = await this.countTotalEntities(driverResource, combinedFilters);
|
|
69
|
+
let totalPages = totalEntities <= pageSize ? 1 : Math.ceil(totalEntities / pageSize);
|
|
70
|
+
let renderedPagination = '';
|
|
71
|
+
for(let paginationItem of PageRangeProvider.fetch(currentPage, totalPages)){
|
|
72
|
+
let paginationUrl = this.rootPath+'/'+driverResource.entityPath+'?page='+ paginationItem.value;
|
|
73
|
+
if(finalEntityFilterTerm){
|
|
74
|
+
paginationUrl += '&entityFilterTerm='+encodeURIComponent(finalEntityFilterTerm);
|
|
75
|
+
}
|
|
76
|
+
for(let filterKey of Object.keys(mergedFilters)){
|
|
77
|
+
if(mergedFilters[filterKey]){
|
|
78
|
+
paginationUrl += '&filters['+filterKey+']='+encodeURIComponent(mergedFilters[filterKey]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
renderedPagination += await this.adminContentsRender(
|
|
82
|
+
this.adminFilesContents.fields.view['link'],
|
|
83
|
+
{
|
|
84
|
+
fieldName: paginationItem.label,
|
|
85
|
+
fieldValue: paginationUrl,
|
|
86
|
+
fieldOriginalValue: paginationItem.value,
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
let listProperties = {
|
|
91
|
+
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath,
|
|
92
|
+
entityFilterTermValue: finalEntityFilterTerm || ''
|
|
93
|
+
};
|
|
94
|
+
await this.emitEvent('reldens.adminListPropertiesPopulation', {
|
|
95
|
+
req,
|
|
96
|
+
driverResource,
|
|
97
|
+
listProperties
|
|
98
|
+
});
|
|
99
|
+
return await this.adminContentsRenderRoute(
|
|
100
|
+
await this.adminContentsRender(
|
|
101
|
+
this.adminContentsEntities()[entityPath].list,
|
|
102
|
+
Object.assign({
|
|
103
|
+
list: await this.adminContentsRender(this.adminFilesContents.listContent, {
|
|
104
|
+
deletePath: this.rootPath + '/' + driverResource.entityPath + this.deletePath,
|
|
105
|
+
fieldsHeaders: driverResource.options.listProperties.map((property) => {
|
|
106
|
+
let propertyTitle = this.fetchTranslation(property, driverResource.id());
|
|
107
|
+
let alias = this.fetchTranslation(
|
|
108
|
+
driverResource.options.properties[property]?.alias || '',
|
|
109
|
+
driverResource.id()
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
name: property,
|
|
113
|
+
value: '' !== alias ? alias + ' ('+propertyTitle+')' : propertyTitle
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
116
|
+
rows: await this.loadEntitiesForList(
|
|
117
|
+
driverResource,
|
|
118
|
+
pageSize,
|
|
119
|
+
currentPage,
|
|
120
|
+
req,
|
|
121
|
+
combinedFilters
|
|
122
|
+
)
|
|
123
|
+
}),
|
|
124
|
+
pagination: renderedPagination,
|
|
125
|
+
extraContentForList: sc.get(listProperties, 'extraContentForList', ''),
|
|
126
|
+
entityFilterTermValue: listProperties.entityFilterTermValue
|
|
127
|
+
}, ...driverResource.options.filterProperties.map((property) => {
|
|
128
|
+
let filterValue = (mergedFilters[property] || '');
|
|
129
|
+
return {[property]: '' === filterValue ? '' : 'value="'+filterValue+'"'};
|
|
130
|
+
}))
|
|
131
|
+
),
|
|
132
|
+
this.adminContentsSideBar()
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async generateViewRouteContent(req, driverResource, entityPath)
|
|
137
|
+
{
|
|
138
|
+
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
139
|
+
let id = (sc.get(req.query, idProperty, ''));
|
|
140
|
+
if('' === id){
|
|
141
|
+
Logger.error('Missing ID on view route.', entityPath, id, idProperty);
|
|
142
|
+
return '';
|
|
143
|
+
}
|
|
144
|
+
let loadedEntity = await this.loadEntityById(driverResource, id);
|
|
145
|
+
if(!loadedEntity){
|
|
146
|
+
Logger.critical('Entity not found on view route.', entityPath, id, idProperty);
|
|
147
|
+
return '';
|
|
148
|
+
}
|
|
149
|
+
let renderedViewProperties = {
|
|
150
|
+
entityEditRoute: this.generateEntityRoute('editPath', driverResource, idProperty, loadedEntity),
|
|
151
|
+
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath,
|
|
152
|
+
id
|
|
153
|
+
};
|
|
154
|
+
let entitySerializedData = {};
|
|
155
|
+
for(let propertyKey of driverResource.options.showProperties){
|
|
156
|
+
let property = driverResource.options.properties[propertyKey];
|
|
157
|
+
let {fieldValue, fieldName} = this.generatePropertyRenderedValueWithLabel(
|
|
158
|
+
loadedEntity,
|
|
159
|
+
propertyKey,
|
|
160
|
+
property
|
|
161
|
+
);
|
|
162
|
+
entitySerializedData[fieldName] = fieldValue;
|
|
163
|
+
renderedViewProperties[propertyKey] = await this.generatePropertyRenderedValue(
|
|
164
|
+
fieldValue,
|
|
165
|
+
fieldName,
|
|
166
|
+
property,
|
|
167
|
+
'view'
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
let extraDataEvent = {entitySerializedData, entityId: driverResource.id(), entity: loadedEntity};
|
|
171
|
+
await this.emitEvent('adminEntityExtraData', extraDataEvent);
|
|
172
|
+
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
173
|
+
.replace(/"/g, '"');
|
|
174
|
+
await this.emitEvent('reldens.adminViewPropertiesPopulation', {
|
|
175
|
+
idProperty,
|
|
176
|
+
req,
|
|
177
|
+
driverResource,
|
|
178
|
+
loadedEntity,
|
|
179
|
+
renderedViewProperties
|
|
180
|
+
});
|
|
181
|
+
return await this.adminContentsRenderRoute(
|
|
182
|
+
await this.adminContentsRender(this.adminContentsEntities()[entityPath].view, renderedViewProperties),
|
|
183
|
+
this.adminContentsSideBar()
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async generateEditRouteContent(req, driverResource, entityPath)
|
|
188
|
+
{
|
|
189
|
+
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
190
|
+
let idValue = String(sc.get(req?.query, idProperty, ''));
|
|
191
|
+
let loadedEntity = !idValue ? null : await this.loadEntityById(driverResource, idValue);
|
|
192
|
+
let renderedEditProperties = {
|
|
193
|
+
idValue,
|
|
194
|
+
idProperty,
|
|
195
|
+
idPropertyLabel: this.fetchTranslation(idProperty),
|
|
196
|
+
templateTitle: (!idValue ? 'Create' : 'Edit')+' '+this.translations.labels[driverResource.id()],
|
|
197
|
+
entityViewRoute: !idValue
|
|
198
|
+
? this.rootPath+'/'+driverResource.entityPath
|
|
199
|
+
: this.generateEntityRoute('viewPath', driverResource, idProperty, loadedEntity)
|
|
200
|
+
};
|
|
201
|
+
await this.emitEvent('reldens.adminEditPropertiesPopulation', {
|
|
202
|
+
req,
|
|
203
|
+
driverResource,
|
|
204
|
+
renderedEditProperties,
|
|
205
|
+
loadedEntity
|
|
206
|
+
});
|
|
207
|
+
for(let propertyKey of Object.keys(driverResource.options.properties)){
|
|
208
|
+
let property = driverResource.options.properties[propertyKey];
|
|
209
|
+
let fieldDisabled = -1 === driverResource.options.editProperties.indexOf(propertyKey);
|
|
210
|
+
renderedEditProperties[propertyKey] = await this.adminContentsRender(
|
|
211
|
+
this.adminFilesContents.fields.edit[this.propertyType(property, 'edit')],
|
|
212
|
+
{
|
|
213
|
+
fieldName: propertyKey,
|
|
214
|
+
fieldValue: await this.generatePropertyEditRenderedValue(
|
|
215
|
+
loadedEntity,
|
|
216
|
+
propertyKey,
|
|
217
|
+
property,
|
|
218
|
+
fieldDisabled
|
|
219
|
+
),
|
|
220
|
+
fieldDisabled: fieldDisabled ? ' disabled="disabled"' : '',
|
|
221
|
+
required: (!property.isUpload || !loadedEntity) && property.isRequired ? ' required="required"' : '',
|
|
222
|
+
multiple: property.isArray ? ' multiple="multiple"' : '',
|
|
223
|
+
inputType: this.getInputType(property, fieldDisabled)
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return await this.adminContentsRenderRoute(
|
|
228
|
+
await this.adminContentsRender(this.adminContentsEntities()[entityPath].edit, renderedEditProperties),
|
|
229
|
+
this.adminContentsSideBar()
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async processDeleteEntities(req, res, driverResource, entityPath)
|
|
234
|
+
{
|
|
235
|
+
let ids = req?.body?.ids;
|
|
236
|
+
if('string' === typeof ids){
|
|
237
|
+
ids = ids.split(',');
|
|
238
|
+
}
|
|
239
|
+
let redirectPath = this.rootPath+'/'+entityPath+'?result=';
|
|
240
|
+
if(!ids || 0 === ids.length){
|
|
241
|
+
return redirectPath + 'errorMissingId';
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
245
|
+
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
246
|
+
let idsFilter = {[idProperty]: {operator: 'IN', value: ids}};
|
|
247
|
+
let loadedEntities = await entityRepository.load(idsFilter);
|
|
248
|
+
await this.deleteEntitiesRelatedFiles(driverResource, loadedEntities);
|
|
249
|
+
let deleteResult = await entityRepository.delete(idsFilter);
|
|
250
|
+
return redirectPath + (deleteResult ? 'success' : 'errorStorageFailure');
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return redirectPath + 'errorDeleteFailure';
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async processSaveEntity(req, res, driverResource, entityPath)
|
|
257
|
+
{
|
|
258
|
+
let uploadProperties = this.fetchUploadProperties(driverResource);
|
|
259
|
+
if(0 < Object.keys(uploadProperties).length){
|
|
260
|
+
if(this.uploaderFactory?.error?.message){
|
|
261
|
+
Logger.error('Upload factory error detected', this.uploaderFactory.error);
|
|
262
|
+
return this.rootPath+'/'+entityPath+'?result=uploadError&error='
|
|
263
|
+
+encodeURIComponent(this.uploaderFactory.error.message);
|
|
264
|
+
}
|
|
265
|
+
let uploadValidationResult = this.validateUploadedFiles(req, uploadProperties);
|
|
266
|
+
if(!uploadValidationResult.success){
|
|
267
|
+
Logger.error('Upload validation failed', uploadValidationResult.error);
|
|
268
|
+
return this.rootPath+'/'+entityPath+'?result=uploadValidationError&error='
|
|
269
|
+
+encodeURIComponent(uploadValidationResult.error.message);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
273
|
+
let id = (req?.body[idProperty] || '');
|
|
274
|
+
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
275
|
+
let entityDataPatch = this.preparePatchData(
|
|
276
|
+
driverResource,
|
|
277
|
+
idProperty,
|
|
278
|
+
req,
|
|
279
|
+
driverResource.options.properties,
|
|
280
|
+
id
|
|
281
|
+
);
|
|
282
|
+
if(!entityDataPatch){
|
|
283
|
+
Logger.error('Bad patch data.', entityDataPatch);
|
|
284
|
+
return this.rootPath+'/'+entityPath+'?result=saveBadPatchData';
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
let saveResult = await this.saveEntity(id, entityRepository, entityDataPatch);
|
|
288
|
+
if(!saveResult){
|
|
289
|
+
Logger.error('Save result error.', saveResult, entityDataPatch);
|
|
290
|
+
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
291
|
+
+'?result=saveEntityStorageError';
|
|
292
|
+
}
|
|
293
|
+
await this.emitEvent('reldens.adminAfterEntitySave', {
|
|
294
|
+
req,
|
|
295
|
+
res,
|
|
296
|
+
driverResource,
|
|
297
|
+
entityPath,
|
|
298
|
+
entityData: saveResult
|
|
299
|
+
});
|
|
300
|
+
if(sc.isFunction(this.autoSyncDistCallback)){
|
|
301
|
+
let uploadProperties = this.fetchUploadProperties(driverResource);
|
|
302
|
+
if(0 < Object.keys(uploadProperties).length){
|
|
303
|
+
for(let uploadPropertyKey of Object.keys(uploadProperties)){
|
|
304
|
+
let property = uploadProperties[uploadPropertyKey];
|
|
305
|
+
await this.autoSyncDistCallback(
|
|
306
|
+
property.bucket,
|
|
307
|
+
saveResult[uploadPropertyKey],
|
|
308
|
+
property.distFolder
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
let saveAction = sc.get(req.body, 'saveAction', 'save');
|
|
314
|
+
if('saveAndContinue' === saveAction){
|
|
315
|
+
return this.generateEntityRoute('editPath', driverResource, idProperty, saveResult) +'&result=success';
|
|
316
|
+
}
|
|
317
|
+
if('saveAndGoBack' === saveAction){
|
|
318
|
+
return this.rootPath+'/'+entityPath+'?result=success';
|
|
319
|
+
}
|
|
320
|
+
return this.generateEntityRoute('viewPath', driverResource, idProperty, saveResult) +'&result=success';
|
|
321
|
+
} catch (error) {
|
|
322
|
+
Logger.error('Save entity error.', error);
|
|
323
|
+
return this.rootPath+'/'+entityPath+'?result=saveEntityError';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
validateUploadedFiles(req, uploadProperties)
|
|
328
|
+
{
|
|
329
|
+
for(let uploadPropertyKey of Object.keys(uploadProperties)){
|
|
330
|
+
let property = uploadProperties[uploadPropertyKey];
|
|
331
|
+
let uploadedFiles = req.files?.[uploadPropertyKey];
|
|
332
|
+
if(!uploadedFiles || 0 === uploadedFiles.length){
|
|
333
|
+
if(property.isRequired){
|
|
334
|
+
return {success: false, error: {message: 'Required file upload missing: '+uploadPropertyKey}};
|
|
335
|
+
}
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
for(let file of uploadedFiles){
|
|
339
|
+
let filePath = file.path;
|
|
340
|
+
if(!FileHandler.exists(filePath)){
|
|
341
|
+
return {
|
|
342
|
+
success: false,
|
|
343
|
+
error: {
|
|
344
|
+
message: 'Uploaded file not found in bucket: '+file.filename,
|
|
345
|
+
filePath,
|
|
346
|
+
bucket: property.bucket
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if(!FileHandler.isFile(filePath)){
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
error: {message: 'Uploaded file is not a valid file: '+file.filename, filePath}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return {success: true};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async countTotalEntities(driverResource, filters)
|
|
362
|
+
{
|
|
363
|
+
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
364
|
+
if(!entityRepository){
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return await entityRepository.count(filters);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async loadEntitiesForList(driverResource, pageSize, currentPage, req, filters)
|
|
371
|
+
{
|
|
372
|
+
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
373
|
+
entityRepository.limit = pageSize;
|
|
374
|
+
if(1 < currentPage){
|
|
375
|
+
entityRepository.offset = (currentPage - 1) * pageSize;
|
|
376
|
+
}
|
|
377
|
+
entityRepository.sortBy = req?.body?.sortBy || false;
|
|
378
|
+
entityRepository.sortDirection = req?.body?.sortDirection || false;
|
|
379
|
+
let loadedEntities = await entityRepository.loadWithRelations(filters, []);
|
|
380
|
+
entityRepository.limit = 0;
|
|
381
|
+
entityRepository.offset = 0;
|
|
382
|
+
entityRepository.sortBy = false;
|
|
383
|
+
entityRepository.sortDirection = false;
|
|
384
|
+
let entityRows = [];
|
|
385
|
+
let resourceProperties = driverResource.options?.properties;
|
|
386
|
+
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
387
|
+
for(let entity of loadedEntities){
|
|
388
|
+
let fields = [];
|
|
389
|
+
for(let property of driverResource.options.listProperties){
|
|
390
|
+
let {fieldValue, fieldName} = this.generatePropertyRenderedValueWithLabel(
|
|
391
|
+
entity,
|
|
392
|
+
property,
|
|
393
|
+
resourceProperties[property]
|
|
394
|
+
);
|
|
395
|
+
fields.push({
|
|
396
|
+
name: property,
|
|
397
|
+
value: await this.generatePropertyRenderedValue(
|
|
398
|
+
fieldValue,
|
|
399
|
+
fieldName,
|
|
400
|
+
resourceProperties[property]
|
|
401
|
+
),
|
|
402
|
+
viewLink: '' !== idProperty
|
|
403
|
+
? this.generateEntityRoute('viewPath', driverResource, idProperty, entity)
|
|
404
|
+
: ''
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
entityRows.push({
|
|
408
|
+
fields,
|
|
409
|
+
editLink: '' !== idProperty
|
|
410
|
+
? this.generateEntityRoute('editPath', driverResource, idProperty, entity)
|
|
411
|
+
: '',
|
|
412
|
+
deleteLink: this.rootPath + '/' + driverResource.entityPath + this.deletePath,
|
|
413
|
+
id: entity[idProperty]
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
return entityRows;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async loadEntityById(driverResource, id)
|
|
420
|
+
{
|
|
421
|
+
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
422
|
+
if(!entityRepository){
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
await this.emitEvent('reldens.adminBeforeEntityLoad', {
|
|
426
|
+
driverResource,
|
|
427
|
+
entityId: id
|
|
428
|
+
});
|
|
429
|
+
return await entityRepository.loadByIdWithRelations(id);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async saveEntity(id, entityRepository, entityDataPatch)
|
|
433
|
+
{
|
|
434
|
+
if('' === id){
|
|
435
|
+
return entityRepository.create(entityDataPatch);
|
|
436
|
+
}
|
|
437
|
+
return entityRepository.updateById(id, entityDataPatch);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async deleteEntitiesRelatedFiles(driverResource, entities)
|
|
441
|
+
{
|
|
442
|
+
for(let propertyKey of Object.keys(driverResource.options.properties)){
|
|
443
|
+
let property = driverResource.options.properties[propertyKey];
|
|
444
|
+
if(!property.isUpload){
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
for(let entity of entities){
|
|
448
|
+
let bucket = sc.get(property, 'bucket', '');
|
|
449
|
+
if(!property.isArray){
|
|
450
|
+
FileHandler.remove([bucket, entity[propertyKey]]);
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
for(let entityFile of entity[propertyKey].split(property.isArray)){
|
|
454
|
+
FileHandler.remove([bucket, entityFile]);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
preparePatchData(driverResource, idProperty, req, resourceProperties, id)
|
|
461
|
+
{
|
|
462
|
+
let entityDataPatch = {};
|
|
463
|
+
for(let i of Object.keys(resourceProperties)){
|
|
464
|
+
if(i === idProperty){
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
let property = resourceProperties[i];
|
|
468
|
+
let shouldProcess = driverResource.options.editProperties.includes(i);
|
|
469
|
+
if(!id && !shouldProcess && property.isRequired && 'reference' === property.type){
|
|
470
|
+
shouldProcess = true;
|
|
471
|
+
}
|
|
472
|
+
if(!shouldProcess){
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
let propertyUpdateValue = sc.get(req.body, i, null);
|
|
476
|
+
if('null' === propertyUpdateValue){
|
|
477
|
+
propertyUpdateValue = null;
|
|
478
|
+
}
|
|
479
|
+
let propertyType = sc.get(property, 'type', 'string');
|
|
480
|
+
if(property.isUpload){
|
|
481
|
+
propertyType = 'upload';
|
|
482
|
+
propertyUpdateValue = this.prepareUploadPatchData(req, i, propertyUpdateValue, property);
|
|
483
|
+
}
|
|
484
|
+
if('boolean' === propertyType){
|
|
485
|
+
propertyUpdateValue = '1' === propertyUpdateValue || 'on' === propertyUpdateValue;
|
|
486
|
+
}
|
|
487
|
+
let isEmpty = '' === propertyUpdateValue;
|
|
488
|
+
let isNull = null === propertyUpdateValue;
|
|
489
|
+
if('number' === propertyType && !isNull && !isEmpty){
|
|
490
|
+
let numValue = Number(propertyUpdateValue);
|
|
491
|
+
if(isNaN(numValue)){
|
|
492
|
+
Logger.critical(
|
|
493
|
+
'Invalid number value for property.',
|
|
494
|
+
{propertyKey: i, value: propertyUpdateValue, entity: driverResource.entityKey}
|
|
495
|
+
);
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
propertyUpdateValue = numValue;
|
|
499
|
+
}
|
|
500
|
+
if('string' === propertyType && !isNull && !isEmpty){
|
|
501
|
+
propertyUpdateValue = String(propertyUpdateValue);
|
|
502
|
+
}
|
|
503
|
+
if(isEmpty && !property.isRequired){
|
|
504
|
+
propertyUpdateValue = null;
|
|
505
|
+
}
|
|
506
|
+
let isUploadCreate = property.isUpload && !id;
|
|
507
|
+
if(property.isRequired && (isNull || isEmpty) && (!property.isUpload || isUploadCreate)){
|
|
508
|
+
// missing required fields would break the update:
|
|
509
|
+
Logger.critical(
|
|
510
|
+
'Missing property on prepare patch data.',
|
|
511
|
+
{propertyKey: i, config: property, propertyUpdateValue, entity: driverResource.entityKey}
|
|
512
|
+
);
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
if(!property.isUpload || (property.isUpload && !isNull)){
|
|
516
|
+
entityDataPatch[i] = propertyUpdateValue;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return entityDataPatch;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
prepareUploadPatchData(req, i, propertyUpdateValue, property)
|
|
524
|
+
{
|
|
525
|
+
let filesData = sc.get(req.files, i, null);
|
|
526
|
+
if(null === filesData){
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
let fileNames = [];
|
|
530
|
+
for(let file of filesData){
|
|
531
|
+
fileNames.push(file.filename);
|
|
532
|
+
}
|
|
533
|
+
return fileNames.join(property.isArray);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async generatePropertyRenderedValue(fieldValue, fieldName, resourceProperty, templateType)
|
|
537
|
+
{
|
|
538
|
+
let fieldOriginalValue = fieldValue;
|
|
539
|
+
if('view' === templateType){
|
|
540
|
+
if(resourceProperty.isArray){
|
|
541
|
+
fieldValue = fieldValue.split(resourceProperty.isArray).map((value) => {
|
|
542
|
+
let target = resourceProperty.isUpload ? ' target="_blank"' : '';
|
|
543
|
+
let fieldValuePart = resourceProperty.isUpload && resourceProperty.bucketPath
|
|
544
|
+
? resourceProperty.bucketPath+value
|
|
545
|
+
: value;
|
|
546
|
+
return {fieldValuePart, fieldOriginalValuePart: value, target};
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
if(!resourceProperty.isArray && resourceProperty.isUpload){
|
|
550
|
+
fieldValue = resourceProperty.bucketPath+fieldValue;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return await this.adminContentsRender(
|
|
554
|
+
this.adminFilesContents.fields.view[this.propertyType(resourceProperty, templateType, fieldValue)],
|
|
555
|
+
{fieldName, fieldValue, fieldOriginalValue, target: ' target="_blank"'}
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
generatePropertyRenderedValueWithLabel(entity, propertyKey, resourceProperty)
|
|
560
|
+
{
|
|
561
|
+
let fieldValue = (0 === entity[propertyKey] ? '0' : entity[propertyKey] || '');
|
|
562
|
+
if((sc.isObject(fieldValue) || sc.isArray(fieldValue)) && 'json' === resourceProperty.dbType){
|
|
563
|
+
fieldValue = sc.toJsonString(fieldValue);
|
|
564
|
+
}
|
|
565
|
+
let fieldName = propertyKey;
|
|
566
|
+
if('boolean' === resourceProperty.type){
|
|
567
|
+
fieldValue = 1 === entity[propertyKey]
|
|
568
|
+
|| '1' === entity[propertyKey]
|
|
569
|
+
|| true === entity[propertyKey] ? 'Yes' : 'No';
|
|
570
|
+
}
|
|
571
|
+
if('datetime' === resourceProperty.type){
|
|
572
|
+
fieldValue = '' !== fieldValue ? sc.formatDate(new Date(fieldValue)) : '';
|
|
573
|
+
}
|
|
574
|
+
if('reference' === resourceProperty.type){
|
|
575
|
+
let relationEntity = entity[resourceProperty.alias || resourceProperty.reference];
|
|
576
|
+
if(relationEntity){
|
|
577
|
+
let relation = this.relations()[resourceProperty.reference];
|
|
578
|
+
if(relation){
|
|
579
|
+
let relationTitleProperty = relation[resourceProperty.alias || resourceProperty.reference];
|
|
580
|
+
if(relationTitleProperty && '' !== String(relationEntity[relationTitleProperty] || '')){
|
|
581
|
+
fieldName = relationTitleProperty;
|
|
582
|
+
fieldValue = relationEntity[relationTitleProperty]+(' ('+fieldValue+')');
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if(resourceProperty.availableValues){
|
|
588
|
+
let optionData = resourceProperty.availableValues.filter((availableValue) => {
|
|
589
|
+
return String(availableValue.value) === String(fieldValue);
|
|
590
|
+
}).shift();
|
|
591
|
+
if(optionData){
|
|
592
|
+
fieldValue = optionData.label + ' (' + fieldValue + ')';
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return {fieldValue, fieldName};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async generatePropertyEditRenderedValue(entity, propertyKey, resourceProperty)
|
|
599
|
+
{
|
|
600
|
+
let entityPropertyValue = sc.get(entity, propertyKey, null);
|
|
601
|
+
let fieldValue = (0 === entityPropertyValue ? '0' : entityPropertyValue || '');
|
|
602
|
+
if('null' === fieldValue){
|
|
603
|
+
fieldValue = '';
|
|
604
|
+
}
|
|
605
|
+
if((sc.isObject(fieldValue) || sc.isArray(fieldValue)) && 'json' === resourceProperty.dbType){
|
|
606
|
+
fieldValue = sc.toJsonString(fieldValue);
|
|
607
|
+
}
|
|
608
|
+
if('boolean' === resourceProperty.type){
|
|
609
|
+
fieldValue = 1 === entityPropertyValue
|
|
610
|
+
|| '1' === entityPropertyValue
|
|
611
|
+
|| true === entityPropertyValue ? ' checked="checked"' : '';
|
|
612
|
+
}
|
|
613
|
+
if('datetime' === resourceProperty.type){
|
|
614
|
+
fieldValue = !entityPropertyValue || '' === entityPropertyValue
|
|
615
|
+
? ''
|
|
616
|
+
: sc.formatDate(new Date(entityPropertyValue), 'Y-m-d H:i:s');
|
|
617
|
+
}
|
|
618
|
+
if('reference' === resourceProperty.type){
|
|
619
|
+
let relationDriverResource = this.resourcesByReference()[resourceProperty.reference];
|
|
620
|
+
if(!relationDriverResource){
|
|
621
|
+
Logger.critical('Driver resource not found by reference.', resourceProperty);
|
|
622
|
+
return fieldValue;
|
|
623
|
+
}
|
|
624
|
+
let relation = this.relations()[resourceProperty.reference];
|
|
625
|
+
let relationKey = resourceProperty.alias || resourceProperty.reference;
|
|
626
|
+
let relationTitleProperty = relation
|
|
627
|
+
? relation[relationKey]
|
|
628
|
+
: this.fetchEntityIdPropertyKey(relationDriverResource);
|
|
629
|
+
let options = (await this.fetchRelationOptions(relationDriverResource)).map((option) => {
|
|
630
|
+
let value = option[this.fetchEntityIdPropertyKey(relationDriverResource)];
|
|
631
|
+
return {
|
|
632
|
+
label: option[relationTitleProperty]+' (ID: '+value+')',
|
|
633
|
+
value,
|
|
634
|
+
selected: entity && entity[propertyKey] === value ? ' selected="selected"' : ''
|
|
635
|
+
};
|
|
636
|
+
});
|
|
637
|
+
if(!resourceProperty.isRequired){
|
|
638
|
+
let isSelected = !entity || null === entity[propertyKey] || '' === entity[propertyKey];
|
|
639
|
+
options.unshift({
|
|
640
|
+
label: '-- Select --',
|
|
641
|
+
value: '',
|
|
642
|
+
selected: isSelected ? ' selected="selected"' : ''
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return options;
|
|
646
|
+
}
|
|
647
|
+
return fieldValue;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
async fetchRelationOptions(relationDriverResource)
|
|
651
|
+
{
|
|
652
|
+
let relationEntityRepository = this.dataServer.getEntity(relationDriverResource.entityKey);
|
|
653
|
+
if(!relationEntityRepository){
|
|
654
|
+
return [];
|
|
655
|
+
}
|
|
656
|
+
return await relationEntityRepository.loadAll();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
propertyType(resourceProperty, templateType, fieldValue)
|
|
660
|
+
{
|
|
661
|
+
let propertyType = sc.get(resourceProperty, 'type', 'text');
|
|
662
|
+
if('reference' === propertyType && 'edit' === templateType){
|
|
663
|
+
return 'select';
|
|
664
|
+
}
|
|
665
|
+
if(resourceProperty.isUpload){
|
|
666
|
+
if('edit' === templateType){
|
|
667
|
+
return 'file';
|
|
668
|
+
}
|
|
669
|
+
if('view' === templateType){
|
|
670
|
+
if(!fieldValue || '' === fieldValue){
|
|
671
|
+
return 'text';
|
|
672
|
+
}
|
|
673
|
+
let multiple = resourceProperty.isArray ? 's' : '';
|
|
674
|
+
let allowedTypes = sc.get(resourceProperty, 'allowedTypes', '');
|
|
675
|
+
if('' !== allowedTypes){
|
|
676
|
+
let templateName = allowedTypes + multiple;
|
|
677
|
+
if(sc.hasOwn(this.adminFilesContents.fields.view, templateName)){
|
|
678
|
+
return templateName;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if('text' === allowedTypes){
|
|
682
|
+
return 'link'+multiple
|
|
683
|
+
}
|
|
684
|
+
return 'text';
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if('textarea' === resourceProperty.type){
|
|
688
|
+
return 'textarea';
|
|
689
|
+
}
|
|
690
|
+
if(-1 !== ['reference', 'number', 'datetime'].indexOf(propertyType)){
|
|
691
|
+
propertyType = 'text';
|
|
692
|
+
}
|
|
693
|
+
return propertyType;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
getInputType(resourceProperty, fieldDisabled)
|
|
697
|
+
{
|
|
698
|
+
if('datetime' === resourceProperty.type && !fieldDisabled){
|
|
699
|
+
return 'datetime-local';
|
|
700
|
+
}
|
|
701
|
+
if('number' === resourceProperty.type){
|
|
702
|
+
return 'number';
|
|
703
|
+
}
|
|
704
|
+
return 'text';
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
generateEntityRoute(routeType, driverResource, idProperty, entity, entityId)
|
|
708
|
+
{
|
|
709
|
+
if(!idProperty || (!entity && !entityId)){
|
|
710
|
+
return this.rootPath + '/' + driverResource.entityPath;
|
|
711
|
+
}
|
|
712
|
+
let idParam = '?' + idProperty + '=';
|
|
713
|
+
if(entity){
|
|
714
|
+
idParam = idParam + entity[idProperty];
|
|
715
|
+
}
|
|
716
|
+
if(entityId){
|
|
717
|
+
idParam = idParam + entityId;
|
|
718
|
+
}
|
|
719
|
+
return this.rootPath + '/' + driverResource.entityPath + this[routeType] + idParam;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
module.exports.RouterContents = RouterContents;
|