@reldens/cms 0.18.0 → 0.20.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/README.md +260 -20
- package/admin/reldens-admin-client.css +127 -80
- package/admin/reldens-admin-client.js +24 -0
- package/admin/templates/clear-all-cache-button.html +18 -0
- package/lib/admin-manager/contents-builder.js +7 -6
- package/lib/admin-manager/router-contents.js +58 -16
- package/lib/admin-manager-validator.js +2 -1
- package/lib/admin-manager.js +4 -2
- package/lib/admin-translations.js +9 -1
- package/lib/cache/add-cache-button-subscriber.js +53 -5
- package/lib/cache/cache-manager.js +47 -8
- package/lib/cache/cache-routes-handler.js +23 -0
- package/lib/cms-pages-route-manager.js +16 -4
- package/lib/frontend.js +310 -119
- package/lib/manager.js +43 -3
- package/lib/pagination-handler.js +243 -0
- package/lib/search-renderer.js +116 -0
- package/lib/search.js +344 -0
- package/lib/template-engine/asset-transformer.js +41 -0
- package/lib/template-engine/collections-single-transformer.js +70 -0
- package/lib/template-engine/collections-transformer-base.js +84 -0
- package/lib/template-engine/collections-transformer.js +374 -0
- package/lib/template-engine/date-transformer.js +53 -0
- package/lib/template-engine/entities-transformer.js +67 -0
- package/lib/template-engine/partials-transformer.js +175 -0
- package/lib/template-engine/system-variables-provider.js +105 -0
- package/lib/template-engine/translate-transformer.js +98 -0
- package/lib/template-engine/translation-service.js +104 -0
- package/lib/template-engine/url-transformer.js +41 -0
- package/lib/template-engine.js +133 -438
- package/lib/templates-list.js +1 -0
- package/migrations/install.sql +18 -18
- package/package.json +4 -4
- package/templates/page.html +19 -2
- package/templates/partials/entriesListView.html +14 -0
- package/templates/partials/pagedCollection.html +33 -0
|
@@ -37,7 +37,10 @@ class ContentsBuilder
|
|
|
37
37
|
this.adminContents.layout = await this.buildLayout();
|
|
38
38
|
this.adminContents.sideBar = await this.buildSideBar();
|
|
39
39
|
this.adminContents.login = await this.renderRoute(this.adminFilesContents.login, '');
|
|
40
|
-
this.adminContents.dashboard = await this.renderRoute(
|
|
40
|
+
this.adminContents.dashboard = await this.renderRoute(
|
|
41
|
+
this.adminFilesContents.dashboard,
|
|
42
|
+
this.adminContents.sideBar
|
|
43
|
+
);
|
|
41
44
|
this.adminContents.entities = await this.buildEntitiesContents();
|
|
42
45
|
await this.emitEvent('reldens.buildAdminContentsAfter');
|
|
43
46
|
return this.adminContents;
|
|
@@ -67,7 +70,8 @@ class ContentsBuilder
|
|
|
67
70
|
navigationContents = eventBuildSideBarBefore.navigationContents;
|
|
68
71
|
for(let driverResource of this.resources()){
|
|
69
72
|
let navigation = driverResource.options?.navigation;
|
|
70
|
-
let name = this.translations.labels[driverResource.id()]
|
|
73
|
+
let name = this.translations.labels[driverResource.id()]
|
|
74
|
+
|| this.translations.labels[driverResource.entityKey];
|
|
71
75
|
let path = this.rootPath+'/'+(driverResource.id().replace(/_/g, '-'));
|
|
72
76
|
if(navigation?.name){
|
|
73
77
|
if(!navigationContents[navigation.name]){
|
|
@@ -93,10 +97,7 @@ class ContentsBuilder
|
|
|
93
97
|
for(let subId of Object.keys(navigationContents[id])){
|
|
94
98
|
subItems += navigationContents[id][subId];
|
|
95
99
|
}
|
|
96
|
-
navigationView += await this.render(
|
|
97
|
-
this.adminFilesContents.sideBarHeader,
|
|
98
|
-
{name: id, subItems}
|
|
99
|
-
);
|
|
100
|
+
navigationView += await this.render(this.adminFilesContents.sideBarHeader, {name: id, subItems});
|
|
100
101
|
continue;
|
|
101
102
|
}
|
|
102
103
|
navigationView += navigationContents[id];
|
|
@@ -51,6 +51,14 @@ class RouterContents
|
|
|
51
51
|
}
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
|
+
let listProperties = {
|
|
55
|
+
entityNewRoute: this.rootPath+'/'+driverResource.entityPath+this.editPath
|
|
56
|
+
};
|
|
57
|
+
await this.emitEvent('reldens.adminListPropertiesPopulation', {
|
|
58
|
+
req,
|
|
59
|
+
driverResource,
|
|
60
|
+
listProperties
|
|
61
|
+
});
|
|
54
62
|
return await this.adminContentsRenderRoute(
|
|
55
63
|
await this.adminContentsRender(
|
|
56
64
|
this.adminContentsEntities()[entityPath].list,
|
|
@@ -67,7 +75,8 @@ class RouterContents
|
|
|
67
75
|
}),
|
|
68
76
|
rows: await this.loadEntitiesForList(driverResource, pageSize, currentPage, req, filters)
|
|
69
77
|
}),
|
|
70
|
-
pagination: renderedPagination
|
|
78
|
+
pagination: renderedPagination,
|
|
79
|
+
extraContentForList: sc.get(listProperties, 'extraContentForList', '')
|
|
71
80
|
}, ...driverResource.options.filterProperties.map((property) => {
|
|
72
81
|
let filterValue = (filtersFromParams[property] || '');
|
|
73
82
|
return {[property]: '' === filterValue ? '' : 'value="'+filterValue+'"'};
|
|
@@ -274,13 +283,21 @@ class RouterContents
|
|
|
274
283
|
);
|
|
275
284
|
fields.push({
|
|
276
285
|
name: property,
|
|
277
|
-
value: await this.generatePropertyRenderedValue(
|
|
278
|
-
|
|
286
|
+
value: await this.generatePropertyRenderedValue(
|
|
287
|
+
fieldValue,
|
|
288
|
+
fieldName,
|
|
289
|
+
resourceProperties[property]
|
|
290
|
+
),
|
|
291
|
+
viewLink: '' !== idProperty
|
|
292
|
+
? this.generateEntityRoute('viewPath', driverResource, idProperty, entity)
|
|
293
|
+
: ''
|
|
279
294
|
});
|
|
280
295
|
}
|
|
281
296
|
entityRows.push({
|
|
282
297
|
fields,
|
|
283
|
-
editLink: '' !== idProperty
|
|
298
|
+
editLink: '' !== idProperty
|
|
299
|
+
? this.generateEntityRoute('editPath', driverResource, idProperty, entity)
|
|
300
|
+
: '',
|
|
284
301
|
deleteLink: this.rootPath + '/' + driverResource.entityPath + this.deletePath,
|
|
285
302
|
id: entity[idProperty]
|
|
286
303
|
});
|
|
@@ -317,12 +334,13 @@ class RouterContents
|
|
|
317
334
|
continue;
|
|
318
335
|
}
|
|
319
336
|
for(let entity of entities){
|
|
337
|
+
let bucket = sc.get(property, 'bucket', '');
|
|
320
338
|
if(!property.isArray){
|
|
321
|
-
FileHandler.remove([
|
|
339
|
+
FileHandler.remove([bucket, entity[propertyKey]]);
|
|
322
340
|
continue;
|
|
323
341
|
}
|
|
324
342
|
for(let entityFile of entity[propertyKey].split(property.isArray)){
|
|
325
|
-
FileHandler.remove([
|
|
343
|
+
FileHandler.remove([bucket, entityFile]);
|
|
326
344
|
}
|
|
327
345
|
}
|
|
328
346
|
}
|
|
@@ -337,7 +355,10 @@ class RouterContents
|
|
|
337
355
|
}
|
|
338
356
|
let propertyUpdateValue = sc.get(req.body, i, null);
|
|
339
357
|
let property = resourceProperties[i];
|
|
340
|
-
|
|
358
|
+
if('null' === propertyUpdateValue){
|
|
359
|
+
propertyUpdateValue = null;
|
|
360
|
+
}
|
|
361
|
+
let propertyType = sc.get(property, 'type', 'string');
|
|
341
362
|
if(property.isUpload){
|
|
342
363
|
propertyType = 'upload';
|
|
343
364
|
propertyUpdateValue = this.prepareUploadPatchData(req, i, propertyUpdateValue, property);
|
|
@@ -345,21 +366,23 @@ class RouterContents
|
|
|
345
366
|
if('boolean' === propertyType){
|
|
346
367
|
propertyUpdateValue = '1' === propertyUpdateValue || 'on' === propertyUpdateValue;
|
|
347
368
|
}
|
|
348
|
-
|
|
369
|
+
let isEmpty = '' === propertyUpdateValue;
|
|
370
|
+
let isNull = null === propertyUpdateValue;
|
|
371
|
+
if('number' === propertyType && !isNull && !isEmpty){
|
|
349
372
|
propertyUpdateValue = Number(propertyUpdateValue);
|
|
350
373
|
}
|
|
351
|
-
if('string' === propertyType &&
|
|
374
|
+
if('string' === propertyType && !isNull && !isEmpty){
|
|
352
375
|
propertyUpdateValue = String(propertyUpdateValue);
|
|
353
376
|
}
|
|
354
|
-
if(
|
|
377
|
+
if(isEmpty && !property.isRequired){
|
|
355
378
|
propertyUpdateValue = null;
|
|
356
379
|
}
|
|
357
380
|
let isUploadCreate = property.isUpload && !id;
|
|
358
|
-
if(property.isRequired && (
|
|
381
|
+
if(property.isRequired && (isNull || isEmpty) && (!property.isUpload || isUploadCreate)){
|
|
359
382
|
Logger.critical('Bad patch data on update.', propertyUpdateValue, property);
|
|
360
383
|
return false;
|
|
361
384
|
}
|
|
362
|
-
if(!property.isUpload || (property.isUpload &&
|
|
385
|
+
if(!property.isUpload || (property.isUpload && !isNull)){
|
|
363
386
|
entityDataPatch[i] = propertyUpdateValue;
|
|
364
387
|
}
|
|
365
388
|
}
|
|
@@ -405,9 +428,14 @@ class RouterContents
|
|
|
405
428
|
generatePropertyRenderedValueWithLabel(entity, propertyKey, resourceProperty)
|
|
406
429
|
{
|
|
407
430
|
let fieldValue = (0 === entity[propertyKey] ? '0' : entity[propertyKey] || '');
|
|
431
|
+
if(sc.isObject(fieldValue) && 'json' === resourceProperty.dbType){
|
|
432
|
+
fieldValue = sc.toJsonString(fieldValue);
|
|
433
|
+
}
|
|
408
434
|
let fieldName = propertyKey;
|
|
409
435
|
if('boolean' === resourceProperty.type){
|
|
410
|
-
fieldValue = 1 === entity[propertyKey]
|
|
436
|
+
fieldValue = 1 === entity[propertyKey]
|
|
437
|
+
|| '1' === entity[propertyKey]
|
|
438
|
+
|| true === entity[propertyKey] ? 'Yes' : 'No';
|
|
411
439
|
}
|
|
412
440
|
if('datetime' === resourceProperty.type){
|
|
413
441
|
fieldValue = '' !== fieldValue ? sc.formatDate(new Date(fieldValue)) : '';
|
|
@@ -440,8 +468,16 @@ class RouterContents
|
|
|
440
468
|
{
|
|
441
469
|
let entityPropertyValue = sc.get(entity, propertyKey, null);
|
|
442
470
|
let fieldValue = (0 === entityPropertyValue ? '0' : entityPropertyValue || '');
|
|
471
|
+
if('null' === fieldValue){
|
|
472
|
+
fieldValue = '';
|
|
473
|
+
}
|
|
474
|
+
if(sc.isObject(fieldValue) && 'json' === resourceProperty.dbType){
|
|
475
|
+
fieldValue = sc.toJsonString(fieldValue);
|
|
476
|
+
}
|
|
443
477
|
if('boolean' === resourceProperty.type){
|
|
444
|
-
fieldValue = 1 === entityPropertyValue
|
|
478
|
+
fieldValue = 1 === entityPropertyValue
|
|
479
|
+
|| '1' === entityPropertyValue
|
|
480
|
+
|| true === entityPropertyValue ? ' checked="checked"' : '';
|
|
445
481
|
}
|
|
446
482
|
if('datetime' === resourceProperty.type){
|
|
447
483
|
fieldValue = !entityPropertyValue || '' === entityPropertyValue
|
|
@@ -452,7 +488,9 @@ class RouterContents
|
|
|
452
488
|
let relationDriverResource = this.resourcesByReference()[resourceProperty.reference];
|
|
453
489
|
let relation = this.relations()[resourceProperty.reference];
|
|
454
490
|
let relationKey = resourceProperty.alias || resourceProperty.reference;
|
|
455
|
-
let relationTitleProperty = relation
|
|
491
|
+
let relationTitleProperty = relation
|
|
492
|
+
? relation[relationKey]
|
|
493
|
+
: this.fetchEntityIdPropertyKey(relationDriverResource);
|
|
456
494
|
let options = (await this.fetchRelationOptions(relationDriverResource)).map((option) => {
|
|
457
495
|
let value = option[this.fetchEntityIdPropertyKey(relationDriverResource)];
|
|
458
496
|
return {
|
|
@@ -462,10 +500,11 @@ class RouterContents
|
|
|
462
500
|
};
|
|
463
501
|
});
|
|
464
502
|
if(!resourceProperty.isRequired){
|
|
503
|
+
let isSelected = !entity || null === entity[propertyKey] || '' === entity[propertyKey];
|
|
465
504
|
options.unshift({
|
|
466
505
|
label: '-- Select --',
|
|
467
506
|
value: '',
|
|
468
|
-
selected:
|
|
507
|
+
selected: isSelected ? ' selected="selected"' : ''
|
|
469
508
|
});
|
|
470
509
|
}
|
|
471
510
|
return options;
|
|
@@ -476,6 +515,9 @@ class RouterContents
|
|
|
476
515
|
async fetchRelationOptions(relationDriverResource)
|
|
477
516
|
{
|
|
478
517
|
let relationEntityRepository = this.dataServer.getEntity(relationDriverResource.entityKey);
|
|
518
|
+
if(!relationEntityRepository){
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
479
521
|
return await relationEntityRepository.loadAll();
|
|
480
522
|
}
|
|
481
523
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Reldens - AdminManagerValidator
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
+
|
|
6
7
|
const { ValidatorInterface, Logger } = require('@reldens/utils');
|
|
7
8
|
|
|
8
9
|
class AdminManagerValidator extends ValidatorInterface
|
|
@@ -29,7 +30,7 @@ class AdminManagerValidator extends ValidatorInterface
|
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
return true
|
|
33
|
+
return true;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
}
|
package/lib/admin-manager.js
CHANGED
|
@@ -115,7 +115,7 @@ class AdminManager
|
|
|
115
115
|
viewPath: this.viewPath,
|
|
116
116
|
editPath: this.editPath,
|
|
117
117
|
deletePath: this.deletePath,
|
|
118
|
-
emitEvent:
|
|
118
|
+
emitEvent: this.emitEvent,
|
|
119
119
|
adminContentsRender: (...args) => this.contentsBuilder.render(...args),
|
|
120
120
|
adminContentsRenderRoute: (...args) => this.contentsBuilder.renderRoute(...args),
|
|
121
121
|
adminContentsEntities: () => this.contentsBuilder.adminContents.entities,
|
|
@@ -135,8 +135,10 @@ class AdminManager
|
|
|
135
135
|
cacheManager: this.cacheManager,
|
|
136
136
|
renderCallback: this.renderCallback,
|
|
137
137
|
cacheCleanButton: this.adminFilesContents.cacheCleanButton,
|
|
138
|
+
clearAllCacheButton: this.adminFilesContents.clearAllCacheButton,
|
|
138
139
|
translations: this.translations,
|
|
139
|
-
cacheCleanRoute: this.cacheRoutesHandler.cacheCleanRoute
|
|
140
|
+
cacheCleanRoute: this.cacheRoutesHandler.cacheCleanRoute,
|
|
141
|
+
clearAllCacheRoute: this.cacheRoutesHandler.clearAllCacheRoute
|
|
140
142
|
});
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -21,7 +21,9 @@ class AdminTranslations
|
|
|
21
21
|
reldensGithubText: 'Need a new feature?'
|
|
22
22
|
+' Would you like to contribute with code?'
|
|
23
23
|
+' Find the source code or create an issue in GitHub',
|
|
24
|
-
reldensLoading: 'Loading...'
|
|
24
|
+
reldensLoading: 'Loading...',
|
|
25
|
+
confirmClearCacheMessage: 'Are you sure you want to clear all cache? This action cannot be undone.',
|
|
26
|
+
clearCacheWarning: 'This will clear all cached routes and may impact site performance temporarily.'
|
|
25
27
|
},
|
|
26
28
|
labels: {
|
|
27
29
|
navigation: 'Reldens - CMS',
|
|
@@ -32,6 +34,12 @@ class AdminTranslations
|
|
|
32
34
|
shuttingDown: 'Server is shutting down in:',
|
|
33
35
|
submitShutdownLabel: 'Shutdown Server',
|
|
34
36
|
submitCancelLabel: 'Cancel Server Shutdown',
|
|
37
|
+
cleanCache: 'Clean Cache',
|
|
38
|
+
clearAllCache: 'Clear All Cache',
|
|
39
|
+
confirmClearCache: 'Confirm Clear Cache',
|
|
40
|
+
warning: 'Warning:',
|
|
41
|
+
cancel: 'Cancel',
|
|
42
|
+
confirm: 'Continue',
|
|
35
43
|
}
|
|
36
44
|
};
|
|
37
45
|
for(let i of Object.keys(translations)){
|
|
@@ -15,8 +15,10 @@ class AddCacheButtonSubscriber
|
|
|
15
15
|
this.cacheManager = sc.get(props, 'cacheManager', false);
|
|
16
16
|
this.renderCallback = sc.get(props, 'renderCallback', false);
|
|
17
17
|
this.cacheCleanButton = sc.get(props, 'cacheCleanButton', '');
|
|
18
|
+
this.clearAllCacheButton = sc.get(props, 'clearAllCacheButton', '');
|
|
18
19
|
this.translations = sc.get(props, 'translations', {});
|
|
19
20
|
this.cacheCleanRoute = sc.get(props, 'cacheCleanRoute', '');
|
|
21
|
+
this.clearAllCacheRoute = sc.get(props, 'clearAllCacheRoute', '');
|
|
20
22
|
this.setupEvents();
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -37,14 +39,14 @@ class AddCacheButtonSubscriber
|
|
|
37
39
|
//Logger.debug('Listening events PropertiesPopulation.');
|
|
38
40
|
this.events.on('reldens.adminViewPropertiesPopulation', this.populateViewFields.bind(this));
|
|
39
41
|
this.events.on('reldens.adminEditPropertiesPopulation', this.populateEditFields.bind(this));
|
|
40
|
-
|
|
42
|
+
this.events.on('reldens.adminListPropertiesPopulation', this.populateListFields.bind(this));
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
async populateViewFields(event)
|
|
44
46
|
{
|
|
45
47
|
let cacheButton = await this.generateCacheCleanButton(event);
|
|
46
48
|
if(!cacheButton){
|
|
47
|
-
Logger.
|
|
49
|
+
//Logger.info('Missing cache button contents on AddCacheButtonSubscriber.');
|
|
48
50
|
return false;
|
|
49
51
|
}
|
|
50
52
|
if(!event.renderedViewProperties.extraContentForView){
|
|
@@ -59,7 +61,7 @@ class AddCacheButtonSubscriber
|
|
|
59
61
|
{
|
|
60
62
|
let cacheButton = await this.generateCacheCleanButton(event);
|
|
61
63
|
if(!cacheButton){
|
|
62
|
-
Logger.
|
|
64
|
+
//Logger.info('Missing cache button contents on AddCacheButtonSubscriber.');
|
|
63
65
|
return false;
|
|
64
66
|
}
|
|
65
67
|
if(!event.renderedEditProperties.extraContentForEdit){
|
|
@@ -70,12 +72,30 @@ class AddCacheButtonSubscriber
|
|
|
70
72
|
return true;
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
async populateListFields(event)
|
|
76
|
+
{
|
|
77
|
+
let clearAllButton = await this.generateClearAllCacheButton(event);
|
|
78
|
+
if(!clearAllButton){
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if(!event.listProperties.extraContentForList){
|
|
82
|
+
event.listProperties.extraContentForList = '';
|
|
83
|
+
}
|
|
84
|
+
event.listProperties.extraContentForList += clearAllButton;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
73
88
|
async generateCacheCleanButton(event)
|
|
74
89
|
{
|
|
75
90
|
if('routes' !== event.driverResource.id()){
|
|
76
91
|
return false;
|
|
77
92
|
}
|
|
78
|
-
if(!event.loadedEntity
|
|
93
|
+
if(!event.loadedEntity){
|
|
94
|
+
Logger.error('Missing loaded entity on AddCacheButtonSubscriber.');
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if(!event.loadedEntity.id){
|
|
98
|
+
Logger.error('Missing loaded entity ID on AddCacheButtonSubscriber.');
|
|
79
99
|
return false;
|
|
80
100
|
}
|
|
81
101
|
if(!this.cacheCleanButton){
|
|
@@ -91,7 +111,35 @@ class AddCacheButtonSubscriber
|
|
|
91
111
|
{
|
|
92
112
|
cacheCleanRoute: this.cacheCleanRoute,
|
|
93
113
|
routeId: event.loadedEntity.id,
|
|
94
|
-
buttonText: sc.get(this.translations, 'cleanCache', '
|
|
114
|
+
buttonText: sc.get(this.translations.labels, 'cleanCache', 'cleanCache')
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async generateClearAllCacheButton(event)
|
|
120
|
+
{
|
|
121
|
+
if('routes' !== event.driverResource.id()){
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if(!this.clearAllCacheButton){
|
|
125
|
+
Logger.error('Clear all cache button template content not found');
|
|
126
|
+
return '';
|
|
127
|
+
}
|
|
128
|
+
if(!this.renderCallback){
|
|
129
|
+
Logger.error('Render callback not available for clear all cache button');
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
return await this.renderCallback(
|
|
133
|
+
this.clearAllCacheButton,
|
|
134
|
+
{
|
|
135
|
+
buttonText: sc.get(this.translations.labels, 'clearAllCache', 'clearAllCache'),
|
|
136
|
+
clearAllCacheRoute: this.clearAllCacheRoute,
|
|
137
|
+
confirmTitle: sc.get(this.translations.labels, 'confirmClearCache', 'confirmClearCache'),
|
|
138
|
+
confirmMessage: sc.get(this.translations.messages, 'confirmClearCacheMessage', 'confirmClearCacheMessage'),
|
|
139
|
+
warningText: sc.get(this.translations.labels, 'warning', 'warning'),
|
|
140
|
+
warningMessage: sc.get(this.translations.messages, 'clearCacheWarning', 'clearCacheWarning'),
|
|
141
|
+
cancelText: sc.get(this.translations.labels, 'cancel', 'cancel'),
|
|
142
|
+
confirmText: sc.get(this.translations.labels, 'confirm', 'confirm')
|
|
95
143
|
}
|
|
96
144
|
);
|
|
97
145
|
}
|
|
@@ -67,17 +67,53 @@ class CacheManager
|
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
findAllCacheFilesForPath(domain, path)
|
|
71
|
+
{
|
|
72
|
+
let cacheInfo = this.generateCacheKey(domain, path);
|
|
73
|
+
if(!FileHandler.exists(cacheInfo.folderPath)){
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
let allFiles = FileHandler.readFolder(cacheInfo.folderPath);
|
|
77
|
+
if(0 === allFiles.length){
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
let baseFileName = cacheInfo.fileName.replace('.html', '');
|
|
81
|
+
let matchingFiles = [];
|
|
82
|
+
for(let file of allFiles){
|
|
83
|
+
if(file === cacheInfo.fileName){
|
|
84
|
+
matchingFiles.push(FileHandler.joinPaths(cacheInfo.folderPath, file));
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if(file.startsWith(baseFileName + '_') && file.endsWith('.html')){
|
|
88
|
+
matchingFiles.push(FileHandler.joinPaths(cacheInfo.folderPath, file));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return matchingFiles;
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
async delete(domain, path)
|
|
71
95
|
{
|
|
72
96
|
let cacheByDomains = this.fetchCachePathsByDomain(domain, path);
|
|
73
97
|
for(let cacheInfo of cacheByDomains){
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
98
|
+
let allCacheFiles = this.findAllCacheFilesForPath(cacheInfo.domain || domain, path);
|
|
99
|
+
if(0 === allCacheFiles.length){
|
|
100
|
+
let singleCacheInfo = this.generateCacheKey(cacheInfo.domain || domain, path);
|
|
101
|
+
if(!FileHandler.exists(singleCacheInfo.fullPath)){
|
|
102
|
+
Logger.debug('No cache files found for: '+path);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
allCacheFiles = [singleCacheInfo.fullPath];
|
|
77
106
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
for(let cacheFilePath of allCacheFiles){
|
|
108
|
+
if(!FileHandler.exists(cacheFilePath)){
|
|
109
|
+
Logger.debug('File does not exist: '+cacheFilePath);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if(!FileHandler.remove(cacheFilePath)){
|
|
113
|
+
Logger.error('Failed to delete cache file: '+cacheFilePath);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
Logger.debug('Deleted cache file: '+cacheFilePath);
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
return true;
|
|
@@ -87,12 +123,15 @@ class CacheManager
|
|
|
87
123
|
{
|
|
88
124
|
let cacheByDomains = [];
|
|
89
125
|
if(domain && 'default' !== domain){
|
|
90
|
-
cacheByDomains.push(
|
|
126
|
+
cacheByDomains.push({domain: domain});
|
|
127
|
+
return cacheByDomains;
|
|
128
|
+
}
|
|
129
|
+
if(!FileHandler.exists(this.cacheBasePath)){
|
|
91
130
|
return cacheByDomains;
|
|
92
131
|
}
|
|
93
132
|
let cachedDomainFolders = FileHandler.readFolder(this.cacheBasePath);
|
|
94
133
|
for(let cachedDomainFolder of cachedDomainFolders) {
|
|
95
|
-
cacheByDomains.push(
|
|
134
|
+
cacheByDomains.push({domain: cachedDomainFolder});
|
|
96
135
|
}
|
|
97
136
|
return cacheByDomains;
|
|
98
137
|
}
|
|
@@ -18,6 +18,8 @@ class CacheRoutesHandler
|
|
|
18
18
|
this.rootPath = sc.get(props, 'rootPath', '');
|
|
19
19
|
this.cacheCleanPath = '/cache-clean';
|
|
20
20
|
this.cacheCleanRoute = this.rootPath+this.cacheCleanPath;
|
|
21
|
+
this.clearAllCachePath = '/cache-clear-all';
|
|
22
|
+
this.clearAllCacheRoute = this.rootPath+this.clearAllCachePath;
|
|
21
23
|
this.setupRoutes();
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -42,6 +44,13 @@ class CacheRoutesHandler
|
|
|
42
44
|
return await this.processCacheClean(req, res);
|
|
43
45
|
}
|
|
44
46
|
);
|
|
47
|
+
this.router.adminRouter.post(
|
|
48
|
+
this.clearAllCachePath,
|
|
49
|
+
this.router.isAuthenticated.bind(this.router),
|
|
50
|
+
async (req, res) => {
|
|
51
|
+
return await this.processClearAllCache(req, res);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
45
54
|
return true;
|
|
46
55
|
}
|
|
47
56
|
|
|
@@ -71,6 +80,20 @@ class CacheRoutesHandler
|
|
|
71
80
|
return res.redirect(this.rootPath+'/routes/view'+'?id='+routeId+'&result=success');
|
|
72
81
|
}
|
|
73
82
|
|
|
83
|
+
async processClearAllCache(req, res)
|
|
84
|
+
{
|
|
85
|
+
if(!this.cacheManager){
|
|
86
|
+
return res.json({error: 'Cache manager not available'});
|
|
87
|
+
}
|
|
88
|
+
let clearResult = await this.cacheManager.clear();
|
|
89
|
+
if(!clearResult){
|
|
90
|
+
Logger.error('Failed to clear all cache');
|
|
91
|
+
return res.redirect(this.rootPath+'/routes?result=errorClearAllCache');
|
|
92
|
+
}
|
|
93
|
+
Logger.info('All cache cleared successfully');
|
|
94
|
+
return res.redirect(this.rootPath+'/routes?result=success');
|
|
95
|
+
}
|
|
96
|
+
|
|
74
97
|
}
|
|
75
98
|
|
|
76
99
|
module.exports.CacheRoutesHandler = CacheRoutesHandler;
|
|
@@ -76,9 +76,14 @@ class CmsPagesRouteManager
|
|
|
76
76
|
Logger.error('Routes repository not found.');
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
+
let path = sc.get(req.body, 'routePath', this.generateDefaultRoutePath(entityData));
|
|
80
|
+
if(!path || '' === path.trim()){
|
|
81
|
+
Logger.debug('No valid path available for CMS page route creation. Skipping route management.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
79
84
|
let cmsPageId = Number(entityData.id);
|
|
80
85
|
let routePatchData = {
|
|
81
|
-
path
|
|
86
|
+
path,
|
|
82
87
|
router: sc.get(req.body, 'routeRouter', 'cmsPages'),
|
|
83
88
|
cache_ttl_seconds: Number(sc.get(req.body, 'routeCacheTtl', 3600)),
|
|
84
89
|
enabled: Number(sc.get(req.body, 'routeEnabled', 1)),
|
|
@@ -91,9 +96,12 @@ class CmsPagesRouteManager
|
|
|
91
96
|
if(!routeResult){
|
|
92
97
|
routeResult = await routesRepository.create(routePatchData);
|
|
93
98
|
if(routeResult){
|
|
94
|
-
let pagesRepository = this.dataServer.getEntity('
|
|
99
|
+
let pagesRepository = this.dataServer.getEntity('cmsPages');
|
|
95
100
|
if(pagesRepository){
|
|
96
|
-
await pagesRepository.updateById(cmsPageId, {route_id: routeResult.id});
|
|
101
|
+
let pageResult = await pagesRepository.updateById(cmsPageId, {route_id: routeResult.id});
|
|
102
|
+
if(!pageResult){
|
|
103
|
+
Logger.error('Page could not be updated with route ID.', routeResult);
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
}
|
|
@@ -105,7 +113,11 @@ class CmsPagesRouteManager
|
|
|
105
113
|
|
|
106
114
|
generateDefaultRoutePath(pageData)
|
|
107
115
|
{
|
|
108
|
-
|
|
116
|
+
let title = sc.get(pageData, 'title', '');
|
|
117
|
+
if(!title || '' === title.trim()){
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
return '/' + title.toLowerCase()
|
|
109
121
|
.replace(/[^a-z0-9\s-]/g, '')
|
|
110
122
|
.replace(/\s+/g, '-')
|
|
111
123
|
.replace(/-+/g, '-')
|