@reldens/cms 0.18.0 → 0.19.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 CHANGED
@@ -157,18 +157,18 @@ Templates support dynamic content blocks, entity rendering, and collections with
157
157
  ```html
158
158
  <!-- Loop through records with full template rendering -->
159
159
  <collection name="cmsBlocks" filters="{status: 'active'}">
160
- <div class="block">
161
- <h3>{{row.title}}</h3>
162
- <div class="content">{{row.content}}</div>
163
- </div>
160
+ <div class="block">
161
+ <h3>{{row.title}}</h3>
162
+ <div class="content">{{row.content}}</div>
163
+ </div>
164
164
  </collection>
165
165
 
166
166
  <collection name="articles" filters="{category: 'technology'}">
167
- <div class="article">
168
- <h4>{{row.title}}</h4>
169
- <p>{{row.summary}}</p>
170
- <img src="{{row.featured_image}}" alt="{{row.title}}">
171
- </div>
167
+ <div class="article">
168
+ <h4>{{row.title}}</h4>
169
+ <p>{{row.summary}}</p>
170
+ <img src="{{row.featured_image}}" alt="{{row.title}}">
171
+ </div>
172
172
  </collection>
173
173
 
174
174
  <!-- With pagination and sorting -->
@@ -178,8 +178,91 @@ Templates support dynamic content blocks, entity rendering, and collections with
178
178
  <p>{{row.summary}}</p>
179
179
  </div>
180
180
  </collection>
181
+
182
+ <!-- Paginated collections with navigation -->
183
+ <collection name="articles"
184
+ filters="{featured: true}"
185
+ data="{limit: 10, sortBy: 'created_at', sortDirection: 'desc'}"
186
+ pagination="articles-1"
187
+ container="pagedCollection"
188
+ prevPages="2"
189
+ nextPages="2">
190
+ <div class="article-card">
191
+ <h4>{{row.title}}</h4>
192
+ <p>{{row.summary}}</p>
193
+ <span class="date">{{row.created_at}}</span>
194
+ </div>
195
+ </collection>
196
+
197
+ <!-- Multiple paginated collections on the same page -->
198
+ <collection name="news"
199
+ filters="{category: 'technology'}"
200
+ data="{limit: 5, sortBy: 'published_at'}"
201
+ pagination="news-tech"
202
+ container="customPager">
203
+ <article>{{row.title}}</article>
204
+ </collection>
205
+
206
+ <collection name="events"
207
+ filters="{upcoming: true}"
208
+ data="{limit: 8}"
209
+ pagination="events-upcoming"
210
+ prevPages="3"
211
+ nextPages="1">
212
+ <div class="event">{{row.title}} - {{row.date}}</div>
213
+ </collection>
214
+ ```
215
+
216
+ **Pagination Attributes:**
217
+ - `pagination="collection-id"` - Enables pagination with unique identifier
218
+ - `container="templateName"` - Custom pagination template (defaults to "pagedCollection")
219
+ - `prevPages="2"` - Number of previous page links to show (default: 2)
220
+ - `nextPages="2"` - Number of next page links to show (default: 2)
221
+
222
+ **Pagination URL Parameters:**
223
+ Pagination state is managed via URL query parameters:
224
+ ```
225
+ /articles?articles-1-key={"page":2,"limit":10,"sortBy":"created_at","sortDirection":"desc"}
226
+ /news?news-tech-key={"page":3,"limit":5,"category":"technology"}
227
+ ```
228
+
229
+ **Custom Pagination Template:**
230
+ Create `templates/partials/pagedCollection.html`:
231
+ ```html
232
+ <div class="row paginated-contents">
233
+ <div class="collection-content col-lg-12 mt-2 mb-2">
234
+ {{&collectionContentForCurrentPage}}
235
+ </div>
236
+ <div class="pagination col-lg-12 mt-2 mb-2">
237
+ <ul class="pagination-list">
238
+ {{#prevPageUrl}}
239
+ <li><a href="{{prevPageUrl}}" class="page-link">{{&prevPageLabel}}</a></li>
240
+ {{/prevPageUrl}}
241
+ {{#prevPages}}
242
+ <li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
243
+ {{/prevPages}}
244
+ <li class="current">{{&currentPage}}</li>
245
+ {{#nextPages}}
246
+ <li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
247
+ {{/nextPages}}
248
+ {{#nextPageUrl}}
249
+ <li><a href="{{nextPageUrl}}" class="page-link">{{&nextPageLabel}}</a></li>
250
+ {{/nextPageUrl}}
251
+ </ul>
252
+ </div>
253
+ </div>
181
254
  ```
182
255
 
256
+ **Available Pagination Template Variables:**
257
+ - `{{&collectionContentForCurrentPage}}` - Rendered collection items for current page
258
+ - `{{currentPage}}` - Current page number
259
+ - `{{totalPages}}` - Total number of pages
260
+ - `{{totalRecords}}` - Total number of records
261
+ - `{{prevPageUrl}}` / `{{nextPageUrl}}` - Previous/next page URLs
262
+ - `{{&prevPageLabel}}` / `{{&nextPageLabel}}` - Previous/next link labels ("Previous"/"Next")
263
+ - `{{#prevPages}}` / `{{#nextPages}}` - Arrays of page objects with `pageUrl` and `pageLabel`
264
+ - `{{hasNextPage}}` / `{{hasPrevPage}}` - Boolean flags for navigation availability
265
+
183
266
  **Custom Partials with Variables:**
184
267
 
185
268
  *New HTML-style partial tags:*
@@ -818,9 +818,10 @@
818
818
  }
819
819
 
820
820
  .extra-actions {
821
- display: block;
821
+ display: flex;
822
822
  width: 100%;
823
- margin-top: 2rem;
823
+ justify-content: end;
824
+ margin: 1rem 0;
824
825
  }
825
826
 
826
827
  .cache-clean-form {
@@ -847,4 +848,47 @@
847
848
  overflow: auto;
848
849
  }
849
850
 
851
+ .cache-confirm-dialog {
852
+ border: none;
853
+ border-radius: 8px;
854
+ padding: 0;
855
+ max-width: 500px;
856
+ width: 90%;
857
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
858
+ }
859
+
860
+ .cache-confirm-dialog::backdrop {
861
+ background-color: rgba(0, 0, 0, 0.5);
862
+ }
863
+
864
+ .cache-dialog-content {
865
+ padding: 1rem;
866
+ }
867
+
868
+ .cache-dialog-content h5 {
869
+ margin: 0 0 1rem 0;
870
+ font-size: 18px;
871
+ color: var(--darkGrey);
872
+ }
873
+
874
+ .cache-dialog-content p {
875
+ margin: 0 0 1rem 0;
876
+ color: var(--darkGrey);
877
+ }
878
+
879
+ .cache-warning {
880
+ padding: 1rem;
881
+ background-color: #fff3cd;
882
+ border: 1px solid #ffeaa7;
883
+ border-radius: 4px;
884
+ color: #856404;
885
+ margin-bottom: 1rem;
886
+ }
887
+
888
+ .cache-dialog-actions {
889
+ display: flex;
890
+ justify-content: flex-end;
891
+ gap: 1rem;
892
+ }
893
+
850
894
  }
@@ -276,4 +276,28 @@ window.addEventListener('DOMContentLoaded', () => {
276
276
  }
277
277
  }
278
278
 
279
+ // cache clear all functionality:
280
+ let cacheClearAllButton = document.querySelector('.cache-clear-all-button');
281
+ let cacheConfirmDialog = document.querySelector('.cache-confirm-dialog');
282
+ let cacheDialogCancel = document.querySelector('.cache-dialog-cancel');
283
+ let cacheClearForm = document.querySelector('.cache-clear-form');
284
+ if(cacheClearAllButton && cacheConfirmDialog){
285
+ cacheClearAllButton.addEventListener('click', () => {
286
+ cacheConfirmDialog.showModal();
287
+ });
288
+ }
289
+ if(cacheDialogCancel && cacheConfirmDialog){
290
+ cacheDialogCancel.addEventListener('click', () => {
291
+ cacheConfirmDialog.close();
292
+ });
293
+ }
294
+ if(cacheClearForm){
295
+ cacheClearForm.addEventListener('submit', (event) => {
296
+ let submitButton = cacheClearForm.querySelector('button[type="submit"]');
297
+ if(submitButton){
298
+ submitButton.disabled = true;
299
+ }
300
+ });
301
+ }
302
+
279
303
  });
@@ -0,0 +1,18 @@
1
+ <button type="button" class="button button-warning cache-clear-all-button">
2
+ {{buttonText}}
3
+ </button>
4
+ <dialog class="cache-confirm-dialog">
5
+ <div class="cache-dialog-content">
6
+ <h5>{{confirmTitle}}</h5>
7
+ <p>{{confirmMessage}}</p>
8
+ <div class="alert cache-warning">
9
+ <strong>{{warningText}}</strong> {{warningMessage}}
10
+ </div>
11
+ <div class="cache-dialog-actions">
12
+ <button type="button" class="button button-secondary cache-dialog-cancel">{{cancelText}}</button>
13
+ <form method="post" action="{{clearAllCacheRoute}}" class="cache-clear-form">
14
+ <button type="submit" class="button button-danger">{{confirmText}}</button>
15
+ </form>
16
+ </div>
17
+ </div>
18
+ </dialog>
@@ -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(this.adminFilesContents.dashboard, this.adminContents.sideBar);
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()] || this.translations.labels[driverResource.entityKey];
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(fieldValue, fieldName, resourceProperties[property]),
278
- viewLink: '' !== idProperty ? this.generateEntityRoute('viewPath', driverResource, idProperty, entity) : ''
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 ? this.generateEntityRoute('editPath', driverResource, idProperty, entity) : '',
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([(property.bucket || ''), entity[propertyKey]]);
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([(property.bucket || ''), entityFile]);
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
- let propertyType = property.type || 'string';
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
- if('number' === propertyType && null !== propertyUpdateValue && '' !== propertyUpdateValue){
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 && null !== propertyUpdateValue && '' !== propertyUpdateValue){
374
+ if('string' === propertyType && !isNull && !isEmpty){
352
375
  propertyUpdateValue = String(propertyUpdateValue);
353
376
  }
354
- if('' === propertyUpdateValue && !property.isRequired){
377
+ if(isEmpty && !property.isRequired){
355
378
  propertyUpdateValue = null;
356
379
  }
357
380
  let isUploadCreate = property.isUpload && !id;
358
- if(property.isRequired && (null === propertyUpdateValue || '' === propertyUpdateValue) && (!property.isUpload || isUploadCreate)){
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 && null !== propertyUpdateValue)){
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] || '1' === entity[propertyKey] || true === entity[propertyKey] ? 'Yes' : 'No';
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 || '1' === entityPropertyValue || true === entityPropertyValue ? ' checked="checked"' : '';
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 ? relation[relationKey] : this.fetchEntityIdPropertyKey(relationDriverResource);
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: (!entity || null === entity[propertyKey] || '' === entity[propertyKey]) ? ' selected="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
  }
@@ -115,7 +115,7 @@ class AdminManager
115
115
  viewPath: this.viewPath,
116
116
  editPath: this.editPath,
117
117
  deletePath: this.deletePath,
118
- emitEvent: (eventName, eventData = {}) => this.events.emit(eventName, {adminManager: this, ...eventData}),
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.warning('Missing cache button contents on AddCacheButtonSubscriber.');
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.warning('Missing cache button contents on AddCacheButtonSubscriber.');
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 || !event.loadedEntity.id){
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', 'Clean Cache')
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
  }