@reldens/cms 0.19.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 (55) hide show
  1. package/README.md +399 -17
  2. package/admin/reldens-admin-client.css +156 -99
  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 +111 -538
  22. package/lib/manager.js +26 -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/asset-transformer.js +41 -0
  27. package/lib/template-engine/collections-single-transformer.js +28 -5
  28. package/lib/template-engine/collections-transformer.js +66 -32
  29. package/lib/template-engine/date-transformer.js +53 -0
  30. package/lib/template-engine/entities-transformer.js +5 -2
  31. package/lib/template-engine/partials-transformer.js +8 -5
  32. package/lib/template-engine/system-variables-provider.js +108 -0
  33. package/lib/template-engine/translate-transformer.js +98 -0
  34. package/lib/template-engine/translation-service.js +104 -0
  35. package/lib/template-engine/url-transformer.js +41 -0
  36. package/lib/template-engine.js +99 -12
  37. package/lib/template-reloader.js +307 -0
  38. package/package.json +4 -4
  39. package/templates/{browserconfig.xml → assets/favicons/default/browserconfig.xml} +1 -1
  40. package/templates/assets/favicons/default/favicon.ico +0 -0
  41. package/templates/{site.webmanifest → assets/favicons/default/site.webmanifest} +3 -3
  42. package/templates/js/functions.js +144 -0
  43. package/templates/js/scripts.js +5 -0
  44. package/templates/page.html +11 -5
  45. package/templates/partials/pagedCollection.html +1 -1
  46. package/lib/admin-translations.js +0 -56
  47. package/templates/favicon.ico +0 -0
  48. /package/templates/assets/favicons/{android-icon-144x144.png → default/android-icon-144x144.png} +0 -0
  49. /package/templates/assets/favicons/{android-icon-192x192.png → default/android-icon-192x192.png} +0 -0
  50. /package/templates/assets/favicons/{android-icon-512x512.png → default/android-icon-512x512.png} +0 -0
  51. /package/templates/assets/favicons/{apple-touch-icon.png → default/apple-touch-icon.png} +0 -0
  52. /package/templates/assets/favicons/{favicon-16x16.png → default/favicon-16x16.png} +0 -0
  53. /package/templates/assets/favicons/{favicon-32x32.png → default/favicon-32x32.png} +0 -0
  54. /package/templates/assets/favicons/{mstile-150x150.png → default/mstile-150x150.png} +0 -0
  55. /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
  }
@@ -476,6 +488,7 @@ class Manager
476
488
  this.frontend = new Frontend({
477
489
  app: this.app,
478
490
  dataServer: this.dataServer,
491
+ events: this.events,
479
492
  renderEngine: this.renderEngine,
480
493
  projectRoot: this.projectRoot,
481
494
  appServerFactory: this.appServerFactory,
@@ -484,7 +497,8 @@ class Manager
484
497
  siteKeyMapping: this.siteKeyMapping,
485
498
  templateExtensions: this.templateExtensions,
486
499
  entitiesConfig: this.entitiesConfig,
487
- cacheManager: this.cacheManager
500
+ cacheManager: this.cacheManager,
501
+ handleFrontendTemplateReload: this.templateReloader.handleFrontendTemplateReload.bind(this.templateReloader)
488
502
  });
489
503
  return await this.frontend.initialize();
490
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
 
@@ -0,0 +1,41 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - AssetTransformer
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+
9
+ class AssetTransformer
10
+ {
11
+
12
+ async transform(template, domain, req, systemVariables)
13
+ {
14
+ if(!template){
15
+ return template;
16
+ }
17
+ let currentRequest = sc.get(systemVariables, 'currentRequest', {});
18
+ let assetPattern = /\[asset\(([^)]+)\)\]/g;
19
+ let matches = [...template.matchAll(assetPattern)];
20
+ for(let i = matches.length - 1; i >= 0; i--){
21
+ let match = matches[i];
22
+ let assetPath = match[1].replace(/['"]/g, '');
23
+ let absoluteUrl = this.buildAssetUrl(assetPath, currentRequest);
24
+ template = template.substring(0, match.index) +
25
+ absoluteUrl +
26
+ template.substring(match.index + match[0].length);
27
+ }
28
+ return template;
29
+ }
30
+
31
+ buildAssetUrl(assetPath, currentRequest)
32
+ {
33
+ if(!assetPath || assetPath.startsWith('http')){
34
+ return assetPath;
35
+ }
36
+ return sc.get(currentRequest, 'baseUrl', '')+'/assets'+(assetPath.startsWith('/') ? assetPath : '/'+assetPath);
37
+ }
38
+
39
+ }
40
+
41
+ module.exports.AssetTransformer = AssetTransformer;
@@ -10,12 +10,22 @@ const { sc } = require('@reldens/utils');
10
10
  class CollectionsSingleTransformer extends CollectionsTransformerBase
11
11
  {
12
12
 
13
+ constructor(props)
14
+ {
15
+ super(props);
16
+ this.renderEngine = sc.get(props, 'renderEngine', false);
17
+ this.getPartials = sc.get(props, 'getPartials', false);
18
+ this.findAllPartialTags = sc.get(props, 'findAllPartialTags', false);
19
+ this.loadPartialTemplate = sc.get(props, 'loadPartialTemplate', false);
20
+ this.processAllTemplateFunctions = sc.get(props, 'processAllTemplateFunctions', false);
21
+ }
22
+
13
23
  getSingleFieldCollectionRegex()
14
24
  {
15
25
  return /<collection\s+([^>]+)\/>/g;
16
26
  }
17
27
 
18
- async transform(template, domain)
28
+ async transform(template, domain, req, systemVariables, enhancedData = {})
19
29
  {
20
30
  let processedTemplate = template;
21
31
  for(let match of template.matchAll(this.getSingleFieldCollectionRegex())){
@@ -30,20 +40,33 @@ class CollectionsSingleTransformer extends CollectionsTransformerBase
30
40
  let fieldName = this.extractAttributeValue(tagContent, 'field');
31
41
  processedTemplate = processedTemplate.replace(
32
42
  match[0],
33
- this.extractFieldValues(
43
+ await this.extractFieldValues(
34
44
  await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString),
35
- fieldName
45
+ fieldName,
46
+ domain,
47
+ req,
48
+ systemVariables,
49
+ enhancedData
36
50
  )
37
51
  );
38
52
  }
39
53
  return processedTemplate;
40
54
  }
41
55
 
42
- extractFieldValues(collectionData, fieldName)
56
+ async extractFieldValues(collectionData, fieldName, domain, req, systemVariables, enhancedData)
43
57
  {
44
58
  let fieldValues = '';
45
59
  for(let row of collectionData){
46
- fieldValues += sc.get(row, fieldName, '');
60
+ let fieldValue = sc.get(row, fieldName, '');
61
+ if(!fieldValue){
62
+ continue;
63
+ }
64
+ if(!this.processAllTemplateFunctions){
65
+ fieldValues += fieldValue;
66
+ continue;
67
+ }
68
+ fieldValue = await this.processAllTemplateFunctions(fieldValue, domain, req, systemVariables, enhancedData);
69
+ fieldValues += fieldValue;
47
70
  }
48
71
  return fieldValues;
49
72
  }
@@ -18,6 +18,7 @@ class CollectionsTransformer extends CollectionsTransformerBase
18
18
  this.getPartials = sc.get(props, 'getPartials', false);
19
19
  this.findAllPartialTags = sc.get(props, 'findAllPartialTags', false);
20
20
  this.loadPartialTemplate = sc.get(props, 'loadPartialTemplate', false);
21
+ this.processAllTemplateFunctions = sc.get(props, 'processAllTemplateFunctions', false);
21
22
  this.paginationHandler = new PaginationHandler();
22
23
  }
23
24
 
@@ -31,7 +32,7 @@ class CollectionsTransformer extends CollectionsTransformerBase
31
32
  return new RegExp('<\\/collection>');
32
33
  }
33
34
 
34
- async transform(template, domain, req)
35
+ async transform(template, domain, req, systemVariables, enhancedData = {})
35
36
  {
36
37
  let processedTemplate = template;
37
38
  let matches = [...template.matchAll(this.getLoopCollectionStartRegex())];
@@ -62,7 +63,9 @@ class CollectionsTransformer extends CollectionsTransformerBase
62
63
  prevPages,
63
64
  nextPages,
64
65
  domain,
65
- req
66
+ req,
67
+ systemVariables,
68
+ enhancedData
66
69
  );
67
70
  if(loopResult){
68
71
  processedTemplate = loopResult;
@@ -76,7 +79,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
76
79
  filtersJson,
77
80
  relationsString,
78
81
  queryOptionsJson,
79
- domain
82
+ domain,
83
+ req,
84
+ systemVariables,
85
+ enhancedData
80
86
  );
81
87
  if(loopResult){
82
88
  processedTemplate = loopResult;
@@ -92,7 +98,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
92
98
  filtersJson,
93
99
  relationsString,
94
100
  queryOptionsJson,
95
- domain
101
+ domain,
102
+ req,
103
+ systemVariables,
104
+ enhancedData
96
105
  ){
97
106
  return await this.processCollectionBase(
98
107
  template,
@@ -102,7 +111,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
102
111
  relationsString,
103
112
  queryOptionsJson,
104
113
  domain,
105
- false
114
+ false,
115
+ req,
116
+ systemVariables,
117
+ enhancedData
106
118
  );
107
119
  }
108
120
 
@@ -118,7 +130,9 @@ class CollectionsTransformer extends CollectionsTransformerBase
118
130
  prevPages,
119
131
  nextPages,
120
132
  domain,
121
- req
133
+ req,
134
+ systemVariables,
135
+ enhancedData
122
136
  ){
123
137
  if(!req){
124
138
  Logger.warning('No request provided for pagination, falling back to regular collection');
@@ -129,7 +143,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
129
143
  filtersJson,
130
144
  relationsString,
131
145
  queryOptionsJson,
132
- domain
146
+ domain,
147
+ req,
148
+ systemVariables,
149
+ enhancedData
133
150
  );
134
151
  }
135
152
  return await this.processCollectionBase(
@@ -146,7 +163,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
146
163
  prevPages,
147
164
  nextPages,
148
165
  req
149
- }
166
+ },
167
+ req,
168
+ systemVariables,
169
+ enhancedData
150
170
  );
151
171
  }
152
172
 
@@ -158,7 +178,10 @@ class CollectionsTransformer extends CollectionsTransformerBase
158
178
  relationsString,
159
179
  queryOptionsJson,
160
180
  domain,
161
- paginationOptions
181
+ paginationOptions,
182
+ req,
183
+ systemVariables,
184
+ enhancedData
162
185
  ){
163
186
  let startPos = startMatch.index;
164
187
  let startEnd = startPos + startMatch[0].length;
@@ -211,13 +234,14 @@ class CollectionsTransformer extends CollectionsTransformerBase
211
234
  this.jsonFieldsParser.getJsonFieldsForEntity(tableName)
212
235
  );
213
236
  }
214
- let collectionContent = await this.renderCollectionLoop(loopContent, collectionData, domain);
237
+ let collectionContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData);
215
238
  finalContent = this.renderEngine.render(
216
239
  1 < paginationData.totalPages
217
240
  ? this.loadPaginationTemplate(paginationOptions.containerName, domain)
218
241
  : '{{&collectionContentForCurrentPage}}',
219
242
  Object.assign(
220
243
  {},
244
+ enhancedData,
221
245
  paginationData,
222
246
  {
223
247
  collectionContentForCurrentPage: collectionContent,
@@ -235,7 +259,7 @@ class CollectionsTransformer extends CollectionsTransformerBase
235
259
  queryOptionsJson,
236
260
  relationsString
237
261
  );
238
- finalContent = await this.renderCollectionLoop(loopContent, collectionData, domain);
262
+ finalContent = await this.renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData);
239
263
  }
240
264
  return template.substring(0, startPos) + finalContent + template.substring(endPos + endMatch[0].length);
241
265
  }
@@ -281,36 +305,40 @@ class CollectionsTransformer extends CollectionsTransformerBase
281
305
  return '{{&collectionContentForCurrentPage}}';
282
306
  }
283
307
 
284
- async renderCollectionLoop(loopContent, collectionData, domain)
308
+ async renderCollectionLoop(loopContent, collectionData, domain, req, systemVariables, enhancedData)
285
309
  {
286
310
  if(!collectionData || 0 === collectionData.length){
287
- return this.renderNoResultsMessage(domain);
311
+ return this.renderNoResultsMessage(domain, enhancedData);
288
312
  }
289
313
  let renderedContent = '';
290
314
  for(let row of collectionData){
291
- renderedContent += await this.processPartialsInLoop(loopContent, row, domain);
315
+ renderedContent += await this.processPartialsInLoop(loopContent, row, domain, req, systemVariables, enhancedData);
292
316
  }
293
317
  return renderedContent;
294
318
  }
295
319
 
296
- renderNoResultsMessage(domain)
320
+ renderNoResultsMessage(domain, enhancedData)
297
321
  {
298
322
  let noResultsTemplate = this.loadPartialTemplate('noResults', domain);
299
323
  if(noResultsTemplate){
300
324
  return this.renderEngine.render(
301
325
  noResultsTemplate,
302
- {
303
- message: 'No results found.',
304
- cssClass: 'no-results',
305
- alertClass: 'alert-info'
306
- },
326
+ Object.assign(
327
+ {},
328
+ enhancedData,
329
+ {
330
+ message: 'No results found.',
331
+ cssClass: 'no-results',
332
+ alertClass: 'alert-info'
333
+ }
334
+ ),
307
335
  this.getPartials(domain)
308
336
  );
309
337
  }
310
338
  return '';
311
339
  }
312
340
 
313
- async processPartialsInLoop(content, rowData, domain)
341
+ async processPartialsInLoop(content, rowData, domain, req, systemVariables, enhancedData)
314
342
  {
315
343
  let processedContent = content;
316
344
  let partialTags = this.findAllPartialTags(content);
@@ -322,20 +350,26 @@ class CollectionsTransformer extends CollectionsTransformerBase
322
350
  processedContent = processedContent.substring(0, tag.start)+''+processedContent.substring(tag.end);
323
351
  continue;
324
352
  }
353
+ if(this.processAllTemplateFunctions){
354
+ partialContent = await this.processAllTemplateFunctions(partialContent, domain, req, systemVariables, enhancedData);
355
+ }
356
+ let renderData = Object.assign({}, enhancedData);
325
357
  if(sc.hasOwn(tag.attributes, 'row')){
326
- processedContent = processedContent.substring(0, tag.start)
327
- +this.renderEngine.render(partialContent, {row: rowData}, this.getPartials(domain))
328
- +processedContent.substring(tag.end);
329
- continue;
358
+ renderData.row = rowData;
330
359
  }
331
- let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
332
- let renderData = { vars: tag.attributes };
333
- let partials = {[tag.name]: partialContent};
334
- processedContent = processedContent.substring(0, tag.start) +
335
- this.renderEngine.render(wrapperTemplate, renderData, partials) +
336
- processedContent.substring(tag.end);
360
+ for(let key of Object.keys(tag.attributes)){
361
+ if('row' !== key){
362
+ renderData[key] = tag.attributes[key];
363
+ }
364
+ }
365
+ processedContent = processedContent.substring(0, tag.start)
366
+ +this.renderEngine.render(partialContent, renderData, this.getPartials(domain))
367
+ +processedContent.substring(tag.end);
368
+ }
369
+ if(this.processAllTemplateFunctions){
370
+ processedContent = await this.processAllTemplateFunctions(processedContent, domain, req, systemVariables, enhancedData);
337
371
  }
338
- return this.renderEngine.render(processedContent, {row: rowData}, this.getPartials(domain));
372
+ return this.renderEngine.render(processedContent, Object.assign({}, enhancedData, {row: rowData}), this.getPartials(domain));
339
373
  }
340
374
 
341
375
  getCurrentUrl(req)
@@ -0,0 +1,53 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - DateTransformer
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+
9
+ class DateTransformer
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.defaultFormat = sc.get(props, 'defaultFormat', 'Y-m-d H:i:s');
15
+ }
16
+
17
+ async transform(template, domain, req, systemVariables)
18
+ {
19
+ if(!template){
20
+ return template;
21
+ }
22
+ let datePattern = /\[date\(([^)]*)\)\]/g;
23
+ let matches = [...template.matchAll(datePattern)];
24
+ for(let i = matches.length - 1; i >= 0; i--){
25
+ let match = matches[i];
26
+ let args = match[1] ? match[1].split(',').map(arg => arg.trim().replace(/['"]/g, '')) : [];
27
+ let dateValue = args[0] || '';
28
+ let formatValue = args[1] || this.defaultFormat;
29
+ let formattedDate = this.formatDate(dateValue, formatValue);
30
+ template = template.substring(0, match.index) +
31
+ formattedDate +
32
+ template.substring(match.index + match[0].length);
33
+ }
34
+ return template;
35
+ }
36
+
37
+ formatDate(dateValue, format)
38
+ {
39
+ let date;
40
+ if(!dateValue || '' === dateValue || 'now' === dateValue.toLowerCase()){
41
+ date = new Date();
42
+ } else {
43
+ date = new Date(dateValue);
44
+ if(isNaN(date.getTime())){
45
+ date = new Date();
46
+ }
47
+ }
48
+ return sc.formatDate(date, format);
49
+ }
50
+
51
+ }
52
+
53
+ module.exports.DateTransformer = DateTransformer;
@@ -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)
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())){
@@ -36,7 +36,10 @@ class EntitiesTransformer
36
36
  match[0],
37
37
  await this.processAllTemplateFunctions(
38
38
  sc.get(await this.fetchEntityForTemplate(tableName, value, field), 'content', ''),
39
- domain
39
+ domain,
40
+ req,
41
+ systemVariables,
42
+ enhancedData
40
43
  )
41
44
  );
42
45
  }