@reldens/cms 0.23.0 → 0.25.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.
|
@@ -31,6 +31,7 @@ class RouterContents
|
|
|
31
31
|
this.fetchTranslation = props.fetchTranslation;
|
|
32
32
|
this.fetchEntityIdPropertyKey = props.fetchEntityIdPropertyKey;
|
|
33
33
|
this.fetchUploadProperties = props.fetchUploadProperties;
|
|
34
|
+
this.uploaderFactory = props.uploaderFactory;
|
|
34
35
|
this.filtersManager = new AdminFiltersManager();
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -41,7 +42,7 @@ class RouterContents
|
|
|
41
42
|
let shouldClearFilters = 'true' === req?.query?.clearFilters;
|
|
42
43
|
let sessionFilters = this.filtersManager.getFiltersFromSession(req, entityPath);
|
|
43
44
|
let filtersFromParams = shouldClearFilters ? {} : req?.body?.filters || req?.query?.filters || {};
|
|
44
|
-
let entityFilterTerm = shouldClearFilters ? '' : req?.body
|
|
45
|
+
let entityFilterTerm = shouldClearFilters ? '' : sc.get(req?.body, 'entityFilterTerm', '');
|
|
45
46
|
if(shouldClearFilters){
|
|
46
47
|
this.filtersManager.clearFiltersFromSession(req, entityPath);
|
|
47
48
|
}
|
|
@@ -107,9 +108,18 @@ class RouterContents
|
|
|
107
108
|
driverResource.options.properties[property]?.alias || '',
|
|
108
109
|
driverResource.id()
|
|
109
110
|
);
|
|
110
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
name: property,
|
|
113
|
+
value: '' !== alias ? alias + ' ('+propertyTitle+')' : propertyTitle
|
|
114
|
+
};
|
|
111
115
|
}),
|
|
112
|
-
rows: await this.loadEntitiesForList(
|
|
116
|
+
rows: await this.loadEntitiesForList(
|
|
117
|
+
driverResource,
|
|
118
|
+
pageSize,
|
|
119
|
+
currentPage,
|
|
120
|
+
req,
|
|
121
|
+
combinedFilters
|
|
122
|
+
)
|
|
113
123
|
}),
|
|
114
124
|
pagination: renderedPagination,
|
|
115
125
|
extraContentForList: sc.get(listProperties, 'extraContentForList', ''),
|
|
@@ -132,6 +142,10 @@ class RouterContents
|
|
|
132
142
|
return '';
|
|
133
143
|
}
|
|
134
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
|
+
}
|
|
135
149
|
let renderedViewProperties = {
|
|
136
150
|
entityEditRoute: this.generateEntityRoute('editPath', driverResource, idProperty, loadedEntity),
|
|
137
151
|
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath,
|
|
@@ -155,7 +169,8 @@ class RouterContents
|
|
|
155
169
|
}
|
|
156
170
|
let extraDataEvent = {entitySerializedData, entityId: driverResource.id(), entity: loadedEntity};
|
|
157
171
|
await this.emitEvent('adminEntityExtraData', extraDataEvent);
|
|
158
|
-
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
172
|
+
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
173
|
+
.replace(/"/g, '"');
|
|
159
174
|
await this.emitEvent('reldens.adminViewPropertiesPopulation', {
|
|
160
175
|
idProperty,
|
|
161
176
|
req,
|
|
@@ -240,10 +255,30 @@ class RouterContents
|
|
|
240
255
|
|
|
241
256
|
async processSaveEntity(req, res, driverResource, entityPath)
|
|
242
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
|
+
}
|
|
243
272
|
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
244
273
|
let id = (req?.body[idProperty] || '');
|
|
245
274
|
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
246
|
-
let entityDataPatch = this.preparePatchData(
|
|
275
|
+
let entityDataPatch = this.preparePatchData(
|
|
276
|
+
driverResource,
|
|
277
|
+
idProperty,
|
|
278
|
+
req,
|
|
279
|
+
driverResource.options.properties,
|
|
280
|
+
id
|
|
281
|
+
);
|
|
247
282
|
if(!entityDataPatch){
|
|
248
283
|
Logger.error('Bad patch data.', entityDataPatch);
|
|
249
284
|
return this.rootPath+'/'+entityPath+'?result=saveBadPatchData';
|
|
@@ -252,7 +287,8 @@ class RouterContents
|
|
|
252
287
|
let saveResult = await this.saveEntity(id, entityRepository, entityDataPatch);
|
|
253
288
|
if(!saveResult){
|
|
254
289
|
Logger.error('Save result error.', saveResult, entityDataPatch);
|
|
255
|
-
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
290
|
+
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
291
|
+
+'?result=saveEntityStorageError';
|
|
256
292
|
}
|
|
257
293
|
await this.emitEvent('reldens.adminAfterEntitySave', {
|
|
258
294
|
req,
|
|
@@ -288,6 +324,40 @@ class RouterContents
|
|
|
288
324
|
}
|
|
289
325
|
}
|
|
290
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
|
+
|
|
291
361
|
async countTotalEntities(driverResource, filters)
|
|
292
362
|
{
|
|
293
363
|
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
@@ -390,12 +460,19 @@ class RouterContents
|
|
|
390
460
|
preparePatchData(driverResource, idProperty, req, resourceProperties, id)
|
|
391
461
|
{
|
|
392
462
|
let entityDataPatch = {};
|
|
393
|
-
for(let i of
|
|
463
|
+
for(let i of Object.keys(resourceProperties)){
|
|
394
464
|
if(i === idProperty){
|
|
395
465
|
continue;
|
|
396
466
|
}
|
|
397
|
-
let propertyUpdateValue = sc.get(req.body, i, null);
|
|
398
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);
|
|
399
476
|
if('null' === propertyUpdateValue){
|
|
400
477
|
propertyUpdateValue = null;
|
|
401
478
|
}
|
|
@@ -410,7 +487,15 @@ class RouterContents
|
|
|
410
487
|
let isEmpty = '' === propertyUpdateValue;
|
|
411
488
|
let isNull = null === propertyUpdateValue;
|
|
412
489
|
if('number' === propertyType && !isNull && !isEmpty){
|
|
413
|
-
|
|
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;
|
|
414
499
|
}
|
|
415
500
|
if('string' === propertyType && !isNull && !isEmpty){
|
|
416
501
|
propertyUpdateValue = String(propertyUpdateValue);
|
|
@@ -420,7 +505,11 @@ class RouterContents
|
|
|
420
505
|
}
|
|
421
506
|
let isUploadCreate = property.isUpload && !id;
|
|
422
507
|
if(property.isRequired && (isNull || isEmpty) && (!property.isUpload || isUploadCreate)){
|
|
423
|
-
|
|
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
|
+
);
|
|
424
513
|
return false;
|
|
425
514
|
}
|
|
426
515
|
if(!property.isUpload || (property.isUpload && !isNull)){
|
|
@@ -430,6 +519,7 @@ class RouterContents
|
|
|
430
519
|
return entityDataPatch;
|
|
431
520
|
}
|
|
432
521
|
|
|
522
|
+
|
|
433
523
|
prepareUploadPatchData(req, i, propertyUpdateValue, property)
|
|
434
524
|
{
|
|
435
525
|
let filesData = sc.get(req.files, i, null);
|
|
@@ -557,7 +647,7 @@ class RouterContents
|
|
|
557
647
|
{
|
|
558
648
|
let relationEntityRepository = this.dataServer.getEntity(relationDriverResource.entityKey);
|
|
559
649
|
if(!relationEntityRepository){
|
|
560
|
-
return
|
|
650
|
+
return [];
|
|
561
651
|
}
|
|
562
652
|
return await relationEntityRepository.loadAll();
|
|
563
653
|
}
|
|
@@ -171,6 +171,7 @@ class Router
|
|
|
171
171
|
}
|
|
172
172
|
let fields = [];
|
|
173
173
|
let allowedFileTypes = {};
|
|
174
|
+
let entityBuckets = {};
|
|
174
175
|
for(let uploadPropertyKey of Object.keys(uploadProperties)){
|
|
175
176
|
let property = uploadProperties[uploadPropertyKey];
|
|
176
177
|
allowedFileTypes[uploadPropertyKey] = property.allowedTypes || false;
|
|
@@ -179,12 +180,12 @@ class Router
|
|
|
179
180
|
field.maxCount = 1;
|
|
180
181
|
}
|
|
181
182
|
fields.push(field);
|
|
182
|
-
|
|
183
|
+
entityBuckets[uploadPropertyKey] = property.bucket;
|
|
183
184
|
}
|
|
184
185
|
this.adminRouter.post(
|
|
185
186
|
entityRoute + this.savePath,
|
|
186
187
|
this.isAuthenticated.bind(this),
|
|
187
|
-
this.uploaderFactory.createUploader(fields,
|
|
188
|
+
this.uploaderFactory.createUploader(fields, entityBuckets, allowedFileTypes),
|
|
188
189
|
async (req, res) => {
|
|
189
190
|
//await this.reloadTemplatesIfNeeded();
|
|
190
191
|
await this.emitEvent('reldens.adminBeforeEntitySave', {
|
package/lib/admin-manager.js
CHANGED
|
@@ -241,7 +241,7 @@ class AdminManager
|
|
|
241
241
|
|
|
242
242
|
fetchEntityIdPropertyKey(driverResource)
|
|
243
243
|
{
|
|
244
|
-
let resourceProperties = driverResource
|
|
244
|
+
let resourceProperties = driverResource?.options?.properties;
|
|
245
245
|
if(!resourceProperties){
|
|
246
246
|
Logger.error('Property "ID" not found.', resourceProperties);
|
|
247
247
|
return '';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/cms",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.25.0",
|
|
5
5
|
"description": "Reldens - CMS",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"url": "https://github.com/damian-pastorini/reldens-cms/issues"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@reldens/server-utils": "^0.
|
|
37
|
-
"@reldens/storage": "^0.
|
|
38
|
-
"@reldens/utils": "^0.
|
|
36
|
+
"@reldens/server-utils": "^0.26.0",
|
|
37
|
+
"@reldens/storage": "^0.68.0",
|
|
38
|
+
"@reldens/utils": "^0.53.0",
|
|
39
39
|
"dotenv": "^17.2.1",
|
|
40
40
|
"mustache": "^4.2.0"
|
|
41
41
|
}
|