@reldens/cms 0.20.0 → 0.21.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.
Files changed (50) hide show
  1. package/README.md +236 -11
  2. package/admin/reldens-admin-client.css +75 -21
  3. package/admin/reldens-admin-client.js +108 -133
  4. package/admin/templates/clear-all-cache-button.html +7 -7
  5. package/admin/templates/edit.html +7 -0
  6. package/admin/templates/layout.html +15 -9
  7. package/admin/templates/list-content.html +4 -2
  8. package/admin/templates/list.html +24 -8
  9. package/admin/templates/view.html +21 -0
  10. package/lib/admin-manager/admin-filters-manager.js +177 -0
  11. package/lib/admin-manager/contents-builder.js +1 -0
  12. package/lib/admin-manager/default-translations.js +38 -0
  13. package/lib/admin-manager/router-contents.js +50 -45
  14. package/lib/admin-manager/router.js +19 -0
  15. package/lib/frontend/content-renderer.js +178 -0
  16. package/lib/frontend/entity-access-manager.js +63 -0
  17. package/lib/frontend/request-processor.js +128 -0
  18. package/lib/frontend/response-manager.js +54 -0
  19. package/lib/frontend/template-cache.js +102 -0
  20. package/lib/frontend/template-resolver.js +111 -0
  21. package/lib/frontend.js +89 -630
  22. package/lib/manager.js +25 -12
  23. package/lib/search-renderer.js +15 -7
  24. package/lib/search-request-handler.js +67 -0
  25. package/lib/search.js +13 -1
  26. package/lib/template-engine/collections-single-transformer.js +11 -5
  27. package/lib/template-engine/collections-transformer.js +47 -34
  28. package/lib/template-engine/entities-transformer.js +3 -2
  29. package/lib/template-engine/partials-transformer.js +5 -6
  30. package/lib/template-engine/system-variables-provider.js +4 -1
  31. package/lib/template-engine.js +11 -5
  32. package/lib/template-reloader.js +307 -0
  33. package/package.json +4 -4
  34. package/templates/{browserconfig.xml → assets/favicons/default/browserconfig.xml} +1 -1
  35. package/templates/assets/favicons/default/favicon.ico +0 -0
  36. package/templates/{site.webmanifest → assets/favicons/default/site.webmanifest} +3 -3
  37. package/templates/js/functions.js +144 -0
  38. package/templates/js/scripts.js +5 -0
  39. package/templates/page.html +11 -5
  40. package/templates/partials/pagedCollection.html +1 -1
  41. package/lib/admin-translations.js +0 -56
  42. package/templates/favicon.ico +0 -0
  43. /package/templates/assets/favicons/{android-icon-144x144.png → default/android-icon-144x144.png} +0 -0
  44. /package/templates/assets/favicons/{android-icon-192x192.png → default/android-icon-192x192.png} +0 -0
  45. /package/templates/assets/favicons/{android-icon-512x512.png → default/android-icon-512x512.png} +0 -0
  46. /package/templates/assets/favicons/{apple-touch-icon.png → default/apple-touch-icon.png} +0 -0
  47. /package/templates/assets/favicons/{favicon-16x16.png → default/favicon-16x16.png} +0 -0
  48. /package/templates/assets/favicons/{favicon-32x32.png → default/favicon-32x32.png} +0 -0
  49. /package/templates/assets/favicons/{mstile-150x150.png → default/mstile-150x150.png} +0 -0
  50. /package/templates/assets/favicons/{safari-pinned-tab.svg → default/safari-pinned-tab.svg} +0 -0
package/lib/manager.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const { TemplatesList } = require('./templates-list');
8
- const { AdminTranslations } = require('./admin-translations');
8
+ const { DefaultTranslations } = require('./admin-manager/default-translations');
9
9
  const { AdminTemplatesLoader } = require('./admin-templates-loader');
10
10
  const { AdminManagerValidator } = require('./admin-manager-validator');
11
11
  const { MimeTypes } = require('./mime-types');
@@ -18,6 +18,7 @@ const { CmsPagesRouteManager } = require('./cms-pages-route-manager');
18
18
  const { Installer } = require('./installer');
19
19
  const { Frontend } = require('./frontend');
20
20
  const { CacheManager } = require('./cache/cache-manager');
21
+ const { TemplateReloader } = require('./template-reloader');
21
22
  const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils');
22
23
  const { DriversMap } = require('@reldens/storage');
23
24
  const { AppServerFactory, FileHandler, Encryptor } = require('@reldens/server-utils');
@@ -34,6 +35,7 @@ class Manager
34
35
  this.installLockPath = FileHandler.joinPaths(this.projectRoot, 'install.lock');
35
36
  dotenv.config({path: this.envFilePath});
36
37
  this.config = this.loadConfigFromEnv();
38
+ this.adminTranslations = sc.get(props, 'adminTranslations', {});
37
39
  this.adminEntities = sc.get(props, 'adminEntities', {});
38
40
  this.rawRegisteredEntities = sc.get(props, 'rawRegisteredEntities', {});
39
41
  this.entitiesTranslations = sc.get(props, 'entitiesTranslations', {});
@@ -60,6 +62,7 @@ class Manager
60
62
  this.siteKeyMapping = sc.get(props, 'siteKeyMapping', sc.toJson(process.env.RELDENS_SITE_KEY_MAPPING));
61
63
  this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.template']);
62
64
  this.cache = sc.get(props, 'cache', false);
65
+ this.reloadTime = sc.get(props, 'reloadTime', 0);
63
66
  this.app = sc.get(props, 'app', false);
64
67
  this.appServer = sc.get(props, 'appServer', false);
65
68
  this.dataServer = sc.get(props, 'dataServer', false);
@@ -67,14 +70,8 @@ class Manager
67
70
  this.frontend = sc.get(props, 'frontend', false);
68
71
  this.renderEngine = sc.get(props, 'renderEngine', mustache);
69
72
  this.prismaClient = sc.get(props, 'prismaClient', false);
70
- this.developmentPatterns = sc.get(props, 'developmentPatterns', [
71
- 'localhost',
72
- '127.0.0.1',
73
- '.local',
74
- '.test',
75
- '.dev',
76
- '.staging'
77
- ]);
73
+ this.defaultDevelopmentPatterns = ['localhost', '127.0.0.1', '.local', '.test', '.dev'];
74
+ this.developmentPatterns = sc.get(props, 'developmentPatterns', this.defaultDevelopmentPatterns);
78
75
  this.developmentEnvironments = sc.get(props, 'developmentEnvironments', ['development', 'dev', 'test']);
79
76
  this.developmentPorts = sc.get(props, 'developmentPorts', [3000, 8080, 8081]);
80
77
  this.developmentMultiplier = sc.get(props, 'developmentMultiplier', 10);
@@ -83,6 +80,14 @@ class Manager
83
80
  this.appServerFactory = new AppServerFactory();
84
81
  this.adminEntitiesGenerator = new AdminEntitiesGenerator();
85
82
  this.cacheManager = new CacheManager({projectRoot: this.projectRoot, enabled: this.cache});
83
+ this.templateReloader = new TemplateReloader({
84
+ reloadTime: this.reloadTime,
85
+ events: this.events,
86
+ adminTemplatesLoader: AdminTemplatesLoader,
87
+ mappedAdminTemplates: this.mappedAdminTemplates,
88
+ templatesPath: FileHandler.joinPaths(this.projectRoot, 'templates'),
89
+ templateExtensions: this.templateExtensions
90
+ });
86
91
  this.installer = new Installer({
87
92
  projectRoot: this.projectRoot,
88
93
  prismaClient: this.prismaClient,
@@ -411,8 +416,12 @@ class Manager
411
416
  return passwordResult;
412
417
  };
413
418
  }
419
+ this.templateReloader.trackTemplateFiles(this.mappedAdminTemplates);
414
420
  let adminFilesContents = await AdminTemplatesLoader.fetchAdminFilesContents(this.mappedAdminTemplates);
415
- let translations = AdminTranslations.appendTranslations(this.entitiesTranslations || {});
421
+ let translations = sc.deepMergeProperties(
422
+ sc.deepMergeProperties({}, DefaultTranslations),
423
+ sc.deepMergeProperties(this.entitiesTranslations, this.adminTranslations)
424
+ );
416
425
  this.events.emit('reldens.manager.initializeAdminManager', {
417
426
  manager: this,
418
427
  authenticationCallback,
@@ -430,7 +439,7 @@ class Manager
430
439
  renderCallback: this.renderCallback.bind(this),
431
440
  secret: this.config.adminSecret,
432
441
  rootPath: this.config.adminPath,
433
- translations: translations,
442
+ translations,
434
443
  adminFilesContents,
435
444
  mimeTypes: this.mimeTypes,
436
445
  allowedExtensions: this.allowedExtensions,
@@ -448,6 +457,9 @@ class Manager
448
457
  }
449
458
  };
450
459
  this.adminManager = new AdminManager(adminConfig);
460
+ this.adminManager.router.checkAndReloadAdminTemplates = async () => {
461
+ return await this.templateReloader.handleAdminTemplateReload(this.adminManager);
462
+ };
451
463
  await this.adminManager.setupAdmin();
452
464
  return true;
453
465
  }
@@ -485,7 +497,8 @@ class Manager
485
497
  siteKeyMapping: this.siteKeyMapping,
486
498
  templateExtensions: this.templateExtensions,
487
499
  entitiesConfig: this.entitiesConfig,
488
- cacheManager: this.cacheManager
500
+ cacheManager: this.cacheManager,
501
+ handleFrontendTemplateReload: this.templateReloader.handleFrontendTemplateReload.bind(this.templateReloader)
489
502
  });
490
503
  return await this.frontend.initialize();
491
504
  }
@@ -35,8 +35,9 @@ class SearchRenderer
35
35
  Logger.error('Search result partial template not found: ' + partialName);
36
36
  continue;
37
37
  }
38
+ let templateData = sc.get(config, 'render.templateData', {});
38
39
  if(sc.hasOwn(entityResult, 'pagination')){
39
- let entityContent = await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain);
40
+ let entityContent = await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain, templateData);
40
41
  let totalPages = sc.get(entityResult.pagination, 'totalPages', 1);
41
42
  if(1 < totalPages){
42
43
  let paginationContainer = sc.get(config, 'render.paginationContainer', 'pagedCollection');
@@ -46,22 +47,30 @@ class SearchRenderer
46
47
  hasResults: 0 < entityResult.results.length,
47
48
  noResultsMessage: entityResult.noResultsMessage || 'No results found.'
48
49
  });
49
- renderedContent += this.renderEngine.render(paginationTemplate, paginationData, this.getPartialsForDomain(domain));
50
+ renderedContent += this.renderEngine.render(
51
+ paginationTemplate,
52
+ paginationData,
53
+ this.getPartialsForDomain(domain)
54
+ );
50
55
  continue;
51
56
  }
52
57
  renderedContent += entityContent;
53
58
  continue;
54
59
  }
55
- renderedContent += await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain);
60
+ renderedContent += await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain, templateData);
56
61
  }
57
62
  return renderedContent;
58
63
  }
59
64
 
60
- async renderSearchEntityResults(results, partialTemplate, domain)
65
+ async renderSearchEntityResults(results, partialTemplate, domain, templateData = {})
61
66
  {
62
67
  let renderedContent = '';
68
+ if(!sc.hasOwn(templateData, 'columnsClass') || '' === templateData.columnsClass){
69
+ templateData.columnsClass = 'col-lg-6';
70
+ }
63
71
  for(let row of results){
64
- renderedContent += this.renderEngine.render(partialTemplate, {row}, this.getPartialsForDomain(domain));
72
+ let rowData = Object.assign({}, templateData, {row});
73
+ renderedContent += this.renderEngine.render(partialTemplate, rowData, this.getPartialsForDomain(domain));
65
74
  }
66
75
  return renderedContent;
67
76
  }
@@ -85,8 +94,7 @@ class SearchRenderer
85
94
 
86
95
  loadPartialTemplate(partialName, domain)
87
96
  {
88
- let partials = this.getPartialsForDomain(domain);
89
- return sc.get(partials, partialName, false);
97
+ return sc.get(this.getPartialsForDomain(domain), partialName, false);
90
98
  }
91
99
 
92
100
  loadPaginationTemplate(containerName, domain)
@@ -0,0 +1,67 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - SearchRequestHandler
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class SearchRequestHandler
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.search = sc.get(props, 'search', false);
15
+ this.searchRenderer = sc.get(props, 'searchRenderer', false);
16
+ this.contentRenderer = sc.get(props, 'contentRenderer', false);
17
+ this.requestProcessor = sc.get(props, 'requestProcessor', false);
18
+ this.cacheManager = sc.get(props, 'cacheManager', false);
19
+ }
20
+
21
+ async handleSearchRequest(req, res)
22
+ {
23
+ try {
24
+ let domain = this.requestProcessor.getDomainFromRequest(req);
25
+ let config = this.search.parseSearchParameters(req.query);
26
+ if(!config){
27
+ return res.redirect('/?error-message=searchInvalidParameters');
28
+ }
29
+ let cacheKey = this.requestProcessor.buildCacheKey(req.path, req);
30
+ if(this.cacheManager && this.cacheManager.isEnabled()){
31
+ let cachedContent = await this.cacheManager.get(domain, cacheKey);
32
+ if(cachedContent){
33
+ return res.send(cachedContent);
34
+ }
35
+ }
36
+ let searchResults = await this.search.executeSearch(config);
37
+ if(false === searchResults){
38
+ return res.redirect('/?error-message=searchExecutionFailed');
39
+ }
40
+ let content = await this.contentRenderer.renderWithTemplateContent(
41
+ {
42
+ template: config.render.page,
43
+ layout: config.render.layout,
44
+ content: await this.searchRenderer.renderSearchResults(searchResults, config, domain, req)
45
+ },
46
+ Object.assign({}, {
47
+ search_query: sc.get(req.query, 'search', ''),
48
+ searchConfig: config,
49
+ query: req.query
50
+ }),
51
+ domain,
52
+ req,
53
+ null
54
+ );
55
+ if(this.cacheManager && this.cacheManager.isEnabled()){
56
+ await this.cacheManager.set(domain, cacheKey, content);
57
+ }
58
+ return res.send(content);
59
+ } catch (error) {
60
+ Logger.error('Search request handling error: ' + error.message);
61
+ return res.redirect('/?error-message=searchError');
62
+ }
63
+ }
64
+
65
+ }
66
+
67
+ module.exports.SearchRequestHandler = SearchRequestHandler;
package/lib/search.js CHANGED
@@ -38,7 +38,8 @@ class Search
38
38
  page: 'page',
39
39
  layout: 'search',
40
40
  paginationContainer: 'pagedCollection',
41
- partial: 'entriesListView'
41
+ partial: 'entriesListView',
42
+ templateData: {}
42
43
  };
43
44
  }
44
45
 
@@ -184,6 +185,17 @@ class Search
184
185
  if(partial){
185
186
  config.render.partial = partial;
186
187
  }
188
+ for(let key of Object.keys(query)){
189
+ if(!key.startsWith('templateData[')){
190
+ continue;
191
+ }
192
+ let templateMatch = key.match(/templateData\[([^\]]+)\]/);
193
+ if(!templateMatch){
194
+ continue;
195
+ }
196
+ let templateKey = templateMatch[1];
197
+ config.render.templateData[templateKey] = query[key];
198
+ }
187
199
  return config;
188
200
  }
189
201
 
@@ -25,7 +25,7 @@ class CollectionsSingleTransformer extends CollectionsTransformerBase
25
25
  return /<collection\s+([^>]+)\/>/g;
26
26
  }
27
27
 
28
- async transform(template, domain, req, systemVariables)
28
+ async transform(template, domain, req, systemVariables, enhancedData = {})
29
29
  {
30
30
  let processedTemplate = template;
31
31
  for(let match of template.matchAll(this.getSingleFieldCollectionRegex())){
@@ -45,21 +45,27 @@ class CollectionsSingleTransformer extends CollectionsTransformerBase
45
45
  fieldName,
46
46
  domain,
47
47
  req,
48
- systemVariables
48
+ systemVariables,
49
+ enhancedData
49
50
  )
50
51
  );
51
52
  }
52
53
  return processedTemplate;
53
54
  }
54
55
 
55
- async extractFieldValues(collectionData, fieldName, domain, req, systemVariables)
56
+ async extractFieldValues(collectionData, fieldName, domain, req, systemVariables, enhancedData)
56
57
  {
57
58
  let fieldValues = '';
58
59
  for(let row of collectionData){
59
60
  let fieldValue = sc.get(row, fieldName, '');
60
- if(fieldValue && this.processAllTemplateFunctions){
61
- fieldValue = await this.processAllTemplateFunctions(fieldValue, domain, req, systemVariables);
61
+ if(!fieldValue){
62
+ continue;
63
+ }
64
+ if(!this.processAllTemplateFunctions){
65
+ fieldValues += fieldValue;
66
+ continue;
62
67
  }
68
+ fieldValue = await this.processAllTemplateFunctions(fieldValue, domain, req, systemVariables, enhancedData);
63
69
  fieldValues += fieldValue;
64
70
  }
65
71
  return fieldValues;
@@ -32,7 +32,7 @@ class CollectionsTransformer extends CollectionsTransformerBase
32
32
  return new RegExp('<\\/collection>');
33
33
  }
34
34
 
35
- async transform(template, domain, req, systemVariables)
35
+ async transform(template, domain, req, systemVariables, enhancedData = {})
36
36
  {
37
37
  let processedTemplate = template;
38
38
  let matches = [...template.matchAll(this.getLoopCollectionStartRegex())];
@@ -64,7 +64,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
64
64
  nextPages,
65
65
  domain,
66
66
  req,
67
- systemVariables
67
+ systemVariables,
68
+ enhancedData
68
69
  );
69
70
  if(loopResult){
70
71
  processedTemplate = loopResult;
@@ -80,7 +81,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
80
81
  queryOptionsJson,
81
82
  domain,
82
83
  req,
83
- systemVariables
84
+ systemVariables,
85
+ enhancedData
84
86
  );
85
87
  if(loopResult){
86
88
  processedTemplate = loopResult;
@@ -98,7 +100,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
98
100
  queryOptionsJson,
99
101
  domain,
100
102
  req,
101
- systemVariables
103
+ systemVariables,
104
+ enhancedData
102
105
  ){
103
106
  return await this.processCollectionBase(
104
107
  template,
@@ -110,7 +113,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
110
113
  domain,
111
114
  false,
112
115
  req,
113
- systemVariables
116
+ systemVariables,
117
+ enhancedData
114
118
  );
115
119
  }
116
120
 
@@ -127,7 +131,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
127
131
  nextPages,
128
132
  domain,
129
133
  req,
130
- systemVariables
134
+ systemVariables,
135
+ enhancedData
131
136
  ){
132
137
  if(!req){
133
138
  Logger.warning('No request provided for pagination, falling back to regular collection');
@@ -140,7 +145,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
140
145
  queryOptionsJson,
141
146
  domain,
142
147
  req,
143
- systemVariables
148
+ systemVariables,
149
+ enhancedData
144
150
  );
145
151
  }
146
152
  return await this.processCollectionBase(
@@ -159,7 +165,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
159
165
  req
160
166
  },
161
167
  req,
162
- systemVariables
168
+ systemVariables,
169
+ enhancedData
163
170
  );
164
171
  }
165
172
 
@@ -173,7 +180,8 @@ class CollectionsTransformer extends CollectionsTransformerBase
173
180
  domain,
174
181
  paginationOptions,
175
182
  req,
176
- systemVariables
183
+ systemVariables,
184
+ enhancedData
177
185
  ){
178
186
  let startPos = startMatch.index;
179
187
  let startEnd = startPos + startMatch[0].length;
@@ -226,13 +234,14 @@ class CollectionsTransformer extends CollectionsTransformerBase
226
234
  this.jsonFieldsParser.getJsonFieldsForEntity(tableName)
227
235
  );
228
236
  }
229
- let collectionContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables);
237
+ let collectionContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData);
230
238
  finalContent = this.renderEngine.render(
231
239
  1 < paginationData.totalPages
232
240
  ? this.loadPaginationTemplate(paginationOptions.containerName, domain)
233
241
  : '{{&collectionContentForCurrentPage}}',
234
242
  Object.assign(
235
243
  {},
244
+ enhancedData,
236
245
  paginationData,
237
246
  {
238
247
  collectionContentForCurrentPage: collectionContent,
@@ -250,7 +259,7 @@ class CollectionsTransformer extends CollectionsTransformerBase
250
259
  queryOptionsJson,
251
260
  relationsString
252
261
  );
253
- finalContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables);
262
+ finalContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData);
254
263
  }
255
264
  return template.substring(0, startPos) + finalContent + template.substring(endPos + endMatch[0].length);
256
265
  }
@@ -296,36 +305,40 @@ class CollectionsTransformer extends CollectionsTransformerBase
296
305
  return '{{&collectionContentForCurrentPage}}';
297
306
  }
298
307
 
299
- async renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables)
308
+ async renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData)
300
309
  {
301
310
  if(!collectionData || 0 === collectionData.length){
302
- return this.renderNoResultsMessage(domain);
311
+ return this.renderNoResultsMessage(domain, enhancedData);
303
312
  }
304
313
  let renderedContent = '';
305
314
  for(let row of collectionData){
306
- renderedContent += await this.processPartialsInLoop(loopContent, row, domain, req, systemVariables);
315
+ renderedContent += await this.processPartialsInLoop(loopContent, row, domain, req, systemVariables, enhancedData);
307
316
  }
308
317
  return renderedContent;
309
318
  }
310
319
 
311
- renderNoResultsMessage(domain)
320
+ renderNoResultsMessage(domain, enhancedData)
312
321
  {
313
322
  let noResultsTemplate = this.loadPartialTemplate('noResults', domain);
314
323
  if(noResultsTemplate){
315
324
  return this.renderEngine.render(
316
325
  noResultsTemplate,
317
- {
318
- message: 'No results found.',
319
- cssClass: 'no-results',
320
- alertClass: 'alert-info'
321
- },
326
+ Object.assign(
327
+ {},
328
+ enhancedData,
329
+ {
330
+ message: 'No results found.',
331
+ cssClass: 'no-results',
332
+ alertClass: 'alert-info'
333
+ }
334
+ ),
322
335
  this.getPartials(domain)
323
336
  );
324
337
  }
325
338
  return '';
326
339
  }
327
340
 
328
- async processPartialsInLoop(content, rowData, domain, req, systemVariables)
341
+ async processPartialsInLoop(content, rowData, domain, req, systemVariables, enhancedData)
329
342
  {
330
343
  let processedContent = content;
331
344
  let partialTags = this.findAllPartialTags(content);
@@ -338,25 +351,25 @@ class CollectionsTransformer extends CollectionsTransformerBase
338
351
  continue;
339
352
  }
340
353
  if(this.processAllTemplateFunctions){
341
- partialContent = await this.processAllTemplateFunctions(partialContent, domain, req, systemVariables);
354
+ partialContent = await this.processAllTemplateFunctions(partialContent, domain, req, systemVariables, enhancedData);
342
355
  }
356
+ let renderData = Object.assign({}, enhancedData);
343
357
  if(sc.hasOwn(tag.attributes, 'row')){
344
- processedContent = processedContent.substring(0, tag.start)
345
- +this.renderEngine.render(partialContent, {row: rowData}, this.getPartials(domain))
346
- +processedContent.substring(tag.end);
347
- continue;
358
+ renderData.row = rowData;
359
+ }
360
+ for(let key of Object.keys(tag.attributes)){
361
+ if('row' !== key){
362
+ renderData[key] = tag.attributes[key];
363
+ }
348
364
  }
349
- let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
350
- let renderData = { vars: tag.attributes };
351
- let partials = {[tag.name]: partialContent};
352
- processedContent = processedContent.substring(0, tag.start) +
353
- this.renderEngine.render(wrapperTemplate, renderData, partials) +
354
- processedContent.substring(tag.end);
365
+ processedContent = processedContent.substring(0, tag.start)
366
+ +this.renderEngine.render(partialContent, renderData, this.getPartials(domain))
367
+ +processedContent.substring(tag.end);
355
368
  }
356
369
  if(this.processAllTemplateFunctions){
357
- processedContent = await this.processAllTemplateFunctions(processedContent, domain, req, systemVariables);
370
+ processedContent = await this.processAllTemplateFunctions(processedContent, domain, req, systemVariables, enhancedData);
358
371
  }
359
- return this.renderEngine.render(processedContent, {row: rowData}, this.getPartials(domain));
372
+ return this.renderEngine.render(processedContent, Object.assign({}, enhancedData, {row: rowData}), this.getPartials(domain));
360
373
  }
361
374
 
362
375
  getCurrentUrl(req)
@@ -21,7 +21,7 @@ class EntitiesTransformer
21
21
  return /<entity\s+name="([^"]+)"(?:\s+field="([^"]+)"\s+value="([^"]+)"|\s+id="([^"]+)")?\s*\/?>/g;
22
22
  }
23
23
 
24
- async transform(template, domain, req, systemVariables)
24
+ async transform(template, domain, req, systemVariables, enhancedData = {})
25
25
  {
26
26
  let processedTemplate = template;
27
27
  for(let match of template.matchAll(this.getEntityRegex())){
@@ -38,7 +38,8 @@ class EntitiesTransformer
38
38
  sc.get(await this.fetchEntityForTemplate(tableName, value, field), 'content', ''),
39
39
  domain,
40
40
  req,
41
- systemVariables
41
+ systemVariables,
42
+ enhancedData
42
43
  )
43
44
  );
44
45
  }
@@ -16,7 +16,7 @@ class PartialsTransformer
16
16
  this.processAllTemplateFunctions = sc.get(props, 'processAllTemplateFunctions', false);
17
17
  }
18
18
 
19
- async transform(template, domain, req, systemVariables)
19
+ async transform(template, domain, req, systemVariables, enhancedData = {})
20
20
  {
21
21
  let processedTemplate = template;
22
22
  let partialTags = this.findAllPartialTags(template);
@@ -29,13 +29,12 @@ class PartialsTransformer
29
29
  continue;
30
30
  }
31
31
  if(this.processAllTemplateFunctions){
32
- partialContent = await this.processAllTemplateFunctions(partialContent, domain, req, systemVariables);
32
+ partialContent = await this.processAllTemplateFunctions(partialContent, domain, req, systemVariables, enhancedData);
33
33
  }
34
- let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
35
- let renderData = { vars: tag.attributes };
36
- let partials = {[tag.name]: partialContent};
34
+ let partialData = Object.assign({}, enhancedData, tag.attributes);
35
+ let renderedPartial = this.renderEngine.render(partialContent, partialData, {});
37
36
  processedTemplate = processedTemplate.substring(0, tag.start) +
38
- this.renderEngine.render(wrapperTemplate, renderData, partials) +
37
+ renderedPartial +
39
38
  processedTemplate.substring(tag.end);
40
39
  }
41
40
  return processedTemplate;
@@ -89,6 +89,7 @@ class SystemVariablesProvider
89
89
 
90
90
  buildSystemInfo()
91
91
  {
92
+ let date = new Date();
92
93
  return {
93
94
  projectRoot: this.projectRoot,
94
95
  publicPath: this.publicPath,
@@ -96,7 +97,9 @@ class SystemVariablesProvider
96
97
  platform: process.platform,
97
98
  environment: process.env.NODE_ENV || 'development',
98
99
  timestamp: sc.getCurrentDate(),
99
- uptime: process.uptime()
100
+ uptime: process.uptime(),
101
+ currentDate: sc.formatDate(date),
102
+ currentYear: sc.formatDate(date, 'Y')
100
103
  };
101
104
  }
102
105
 
@@ -59,7 +59,8 @@ class TemplateEngine
59
59
  });
60
60
  this.partialsTransformer = new PartialsTransformer({
61
61
  renderEngine: this.renderEngine,
62
- getPartials: this.getPartials
62
+ getPartials: this.getPartials,
63
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this)
63
64
  });
64
65
  this.urlTransformer = new UrlTransformer();
65
66
  this.assetTransformer = new AssetTransformer();
@@ -83,13 +84,17 @@ class TemplateEngine
83
84
  ];
84
85
  }
85
86
 
86
- async processAllTemplateFunctions(template, domain, req, systemVariables)
87
+ async processAllTemplateFunctions(template, domain, req, systemVariables, enhancedData = {})
87
88
  {
88
89
  let processedTemplate = template;
89
90
  for(let transformer of this.transformers){
90
- if(sc.isFunction(transformer.transform)){
91
- processedTemplate = await transformer.transform(processedTemplate, domain, req, systemVariables);
91
+ if(!transformer){
92
+ continue;
93
+ }
94
+ if(!sc.isFunction(transformer.transform)){
95
+ continue;
92
96
  }
97
+ processedTemplate = await transformer.transform(processedTemplate, domain, req, systemVariables, enhancedData);
93
98
  }
94
99
  return processedTemplate;
95
100
  }
@@ -134,7 +139,8 @@ class TemplateEngine
134
139
  beforeProcessData.content,
135
140
  domain,
136
141
  req,
137
- eventData.variables
142
+ eventData.variables,
143
+ enhancedData
138
144
  );
139
145
  let afterProcessData = {
140
146
  processedContent: processedTemplate,