@reldens/cms 0.24.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', ''),
|
|
@@ -159,7 +169,8 @@ class RouterContents
|
|
|
159
169
|
}
|
|
160
170
|
let extraDataEvent = {entitySerializedData, entityId: driverResource.id(), entity: loadedEntity};
|
|
161
171
|
await this.emitEvent('adminEntityExtraData', extraDataEvent);
|
|
162
|
-
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
172
|
+
renderedViewProperties.entitySerializedData = JSON.stringify(extraDataEvent.entitySerializedData)
|
|
173
|
+
.replace(/"/g, '"');
|
|
163
174
|
await this.emitEvent('reldens.adminViewPropertiesPopulation', {
|
|
164
175
|
idProperty,
|
|
165
176
|
req,
|
|
@@ -244,10 +255,30 @@ class RouterContents
|
|
|
244
255
|
|
|
245
256
|
async processSaveEntity(req, res, driverResource, entityPath)
|
|
246
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
|
+
}
|
|
247
272
|
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
248
273
|
let id = (req?.body[idProperty] || '');
|
|
249
274
|
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
250
|
-
let entityDataPatch = this.preparePatchData(
|
|
275
|
+
let entityDataPatch = this.preparePatchData(
|
|
276
|
+
driverResource,
|
|
277
|
+
idProperty,
|
|
278
|
+
req,
|
|
279
|
+
driverResource.options.properties,
|
|
280
|
+
id
|
|
281
|
+
);
|
|
251
282
|
if(!entityDataPatch){
|
|
252
283
|
Logger.error('Bad patch data.', entityDataPatch);
|
|
253
284
|
return this.rootPath+'/'+entityPath+'?result=saveBadPatchData';
|
|
@@ -256,7 +287,8 @@ class RouterContents
|
|
|
256
287
|
let saveResult = await this.saveEntity(id, entityRepository, entityDataPatch);
|
|
257
288
|
if(!saveResult){
|
|
258
289
|
Logger.error('Save result error.', saveResult, entityDataPatch);
|
|
259
|
-
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
290
|
+
return this.generateEntityRoute('editPath', driverResource, idProperty, null, id)
|
|
291
|
+
+'?result=saveEntityStorageError';
|
|
260
292
|
}
|
|
261
293
|
await this.emitEvent('reldens.adminAfterEntitySave', {
|
|
262
294
|
req,
|
|
@@ -292,6 +324,40 @@ class RouterContents
|
|
|
292
324
|
}
|
|
293
325
|
}
|
|
294
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
|
+
|
|
295
361
|
async countTotalEntities(driverResource, filters)
|
|
296
362
|
{
|
|
297
363
|
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
@@ -394,12 +460,19 @@ class RouterContents
|
|
|
394
460
|
preparePatchData(driverResource, idProperty, req, resourceProperties, id)
|
|
395
461
|
{
|
|
396
462
|
let entityDataPatch = {};
|
|
397
|
-
for(let i of
|
|
463
|
+
for(let i of Object.keys(resourceProperties)){
|
|
398
464
|
if(i === idProperty){
|
|
399
465
|
continue;
|
|
400
466
|
}
|
|
401
|
-
let propertyUpdateValue = sc.get(req.body, i, null);
|
|
402
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);
|
|
403
476
|
if('null' === propertyUpdateValue){
|
|
404
477
|
propertyUpdateValue = null;
|
|
405
478
|
}
|
|
@@ -414,7 +487,15 @@ class RouterContents
|
|
|
414
487
|
let isEmpty = '' === propertyUpdateValue;
|
|
415
488
|
let isNull = null === propertyUpdateValue;
|
|
416
489
|
if('number' === propertyType && !isNull && !isEmpty){
|
|
417
|
-
|
|
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;
|
|
418
499
|
}
|
|
419
500
|
if('string' === propertyType && !isNull && !isEmpty){
|
|
420
501
|
propertyUpdateValue = String(propertyUpdateValue);
|
|
@@ -424,7 +505,11 @@ class RouterContents
|
|
|
424
505
|
}
|
|
425
506
|
let isUploadCreate = property.isUpload && !id;
|
|
426
507
|
if(property.isRequired && (isNull || isEmpty) && (!property.isUpload || isUploadCreate)){
|
|
427
|
-
|
|
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
|
+
);
|
|
428
513
|
return false;
|
|
429
514
|
}
|
|
430
515
|
if(!property.isUpload || (property.isUpload && !isNull)){
|
|
@@ -434,6 +519,7 @@ class RouterContents
|
|
|
434
519
|
return entityDataPatch;
|
|
435
520
|
}
|
|
436
521
|
|
|
522
|
+
|
|
437
523
|
prepareUploadPatchData(req, i, propertyUpdateValue, property)
|
|
438
524
|
{
|
|
439
525
|
let filesData = sc.get(req.files, i, null);
|
|
@@ -561,7 +647,7 @@ class RouterContents
|
|
|
561
647
|
{
|
|
562
648
|
let relationEntityRepository = this.dataServer.getEntity(relationDriverResource.entityKey);
|
|
563
649
|
if(!relationEntityRepository){
|
|
564
|
-
return
|
|
650
|
+
return [];
|
|
565
651
|
}
|
|
566
652
|
return await relationEntityRepository.loadAll();
|
|
567
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,8 +33,8 @@
|
|
|
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.
|
|
36
|
+
"@reldens/server-utils": "^0.26.0",
|
|
37
|
+
"@reldens/storage": "^0.68.0",
|
|
38
38
|
"@reldens/utils": "^0.53.0",
|
|
39
39
|
"dotenv": "^17.2.1",
|
|
40
40
|
"mustache": "^4.2.0"
|