@reldens/cms 0.20.0 → 0.23.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 (67) hide show
  1. package/README.md +648 -12
  2. package/admin/reldens-admin-client.css +77 -141
  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/fields/view/audio.html +7 -0
  7. package/admin/templates/fields/view/audios.html +8 -0
  8. package/admin/templates/layout.html +15 -9
  9. package/admin/templates/list-content.html +4 -2
  10. package/admin/templates/list.html +24 -8
  11. package/admin/templates/view.html +21 -0
  12. package/install/index.html +4 -0
  13. package/lib/admin-manager/admin-filters-manager.js +177 -0
  14. package/lib/admin-manager/contents-builder.js +1 -0
  15. package/lib/admin-manager/default-translations.js +38 -0
  16. package/lib/admin-manager/router-contents.js +64 -52
  17. package/lib/admin-manager/router.js +19 -0
  18. package/lib/dynamic-form-renderer.js +228 -0
  19. package/lib/dynamic-form-request-handler.js +135 -0
  20. package/lib/dynamic-form.js +310 -0
  21. package/lib/frontend/content-renderer.js +178 -0
  22. package/lib/frontend/entity-access-manager.js +63 -0
  23. package/lib/frontend/request-processor.js +128 -0
  24. package/lib/frontend/response-manager.js +54 -0
  25. package/lib/frontend/template-cache.js +102 -0
  26. package/lib/frontend/template-resolver.js +111 -0
  27. package/lib/frontend.js +122 -629
  28. package/lib/installer.js +2 -1
  29. package/lib/manager.js +25 -12
  30. package/lib/search-renderer.js +15 -7
  31. package/lib/search-request-handler.js +67 -0
  32. package/lib/search.js +13 -1
  33. package/lib/template-engine/collections-single-transformer.js +11 -5
  34. package/lib/template-engine/collections-transformer.js +47 -34
  35. package/lib/template-engine/entities-transformer.js +3 -2
  36. package/lib/template-engine/forms-transformer.js +187 -0
  37. package/lib/template-engine/partials-transformer.js +5 -6
  38. package/lib/template-engine/system-variables-provider.js +4 -1
  39. package/lib/template-engine.js +28 -5
  40. package/lib/template-reloader.js +307 -0
  41. package/lib/templates-list.js +2 -0
  42. package/migrations/default-forms.sql +22 -0
  43. package/package.json +5 -5
  44. package/templates/{browserconfig.xml → assets/favicons/default/browserconfig.xml} +1 -1
  45. package/templates/assets/favicons/default/favicon.ico +0 -0
  46. package/templates/{site.webmanifest → assets/favicons/default/site.webmanifest} +3 -3
  47. package/templates/cms_forms/field_email.html +14 -0
  48. package/templates/cms_forms/field_number.html +17 -0
  49. package/templates/cms_forms/field_select.html +15 -0
  50. package/templates/cms_forms/field_text.html +16 -0
  51. package/templates/cms_forms/field_textarea.html +13 -0
  52. package/templates/cms_forms/form.html +22 -0
  53. package/templates/css/styles.css +4 -0
  54. package/templates/js/functions.js +144 -0
  55. package/templates/js/scripts.js +5 -0
  56. package/templates/page.html +11 -5
  57. package/templates/partials/pagedCollection.html +1 -1
  58. package/lib/admin-translations.js +0 -56
  59. package/templates/favicon.ico +0 -0
  60. /package/templates/assets/favicons/{android-icon-144x144.png → default/android-icon-144x144.png} +0 -0
  61. /package/templates/assets/favicons/{android-icon-192x192.png → default/android-icon-192x192.png} +0 -0
  62. /package/templates/assets/favicons/{android-icon-512x512.png → default/android-icon-512x512.png} +0 -0
  63. /package/templates/assets/favicons/{apple-touch-icon.png → default/apple-touch-icon.png} +0 -0
  64. /package/templates/assets/favicons/{favicon-16x16.png → default/favicon-16x16.png} +0 -0
  65. /package/templates/assets/favicons/{favicon-32x32.png → default/favicon-32x32.png} +0 -0
  66. /package/templates/assets/favicons/{mstile-150x150.png → default/mstile-150x150.png} +0 -0
  67. /package/templates/assets/favicons/{safari-pinned-tab.svg → default/safari-pinned-tab.svg} +0 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - FormsTransformer
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class FormsTransformer
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.renderEngine = sc.get(props, 'renderEngine', false);
15
+ this.getPartials = sc.get(props, 'getPartials', false);
16
+ this.processAllTemplateFunctions = sc.get(props, 'processAllTemplateFunctions', false);
17
+ this.dynamicForm = sc.get(props, 'dynamicForm', false);
18
+ this.dynamicFormRenderer = sc.get(props, 'dynamicFormRenderer', false);
19
+ this.events = sc.get(props, 'events', false);
20
+ if(!this.events){
21
+ Logger.error('EventsManager not provided to FormsTransformer - forms functionality disabled');
22
+ this.isDisabled = true;
23
+ }
24
+ }
25
+
26
+ async transform(template, domain, req, systemVariables, enhancedData = {})
27
+ {
28
+ if(this.isDisabled){
29
+ return template;
30
+ }
31
+ let processedTemplate = template;
32
+ let formTags = this.findAllFormTags(template);
33
+ for(let i = formTags.length - 1; i >= 0; i--){
34
+ let tag = formTags[i];
35
+ let formKey = sc.get(tag.attributes, 'key', '');
36
+ if(!formKey){
37
+ Logger.warning('cmsForm tag missing key attribute');
38
+ processedTemplate = processedTemplate.substring(0, tag.start)+''+processedTemplate.substring(tag.end);
39
+ continue;
40
+ }
41
+ let formConfig = await this.dynamicForm.getFormConfig(formKey);
42
+ if(!formConfig){
43
+ Logger.warning('Form not found or disabled: '+formKey);
44
+ processedTemplate = processedTemplate.substring(0, tag.start)+''+processedTemplate.substring(tag.end);
45
+ continue;
46
+ }
47
+ let fieldsToRender = this.parseFieldsFilter(tag.attributes, formConfig);
48
+ if(!sc.isArray(fieldsToRender) || 0 === fieldsToRender.length){
49
+ processedTemplate = processedTemplate.substring(0, tag.start)+''+processedTemplate.substring(tag.end);
50
+ continue;
51
+ }
52
+ await this.events.emit('reldens.formsTransformer.beforeRender', {
53
+ formKey,
54
+ formConfig,
55
+ fieldsToRender,
56
+ formAttributes: tag.attributes,
57
+ domain,
58
+ req,
59
+ systemVariables,
60
+ enhancedData
61
+ });
62
+ let formContent = await this.dynamicFormRenderer.renderForm(
63
+ formConfig,
64
+ fieldsToRender,
65
+ domain,
66
+ req,
67
+ Object.assign({}, tag.attributes, {
68
+ successRedirect: sc.get(tag.attributes, 'successRedirect', req.path),
69
+ errorRedirect: sc.get(tag.attributes, 'errorRedirect', req.path)
70
+ }),
71
+ systemVariables,
72
+ enhancedData
73
+ );
74
+ await this.events.emit('reldens.formsTransformer.afterRender', {
75
+ formKey,
76
+ formConfig,
77
+ formContent,
78
+ domain,
79
+ req,
80
+ systemVariables,
81
+ enhancedData
82
+ });
83
+ processedTemplate = processedTemplate.substring(0, tag.start) +
84
+ formContent +
85
+ processedTemplate.substring(tag.end);
86
+ }
87
+ return processedTemplate;
88
+ }
89
+
90
+ findAllFormTags(template)
91
+ {
92
+ let formTags = [];
93
+ let pos = 0;
94
+ let cmsForm = '<cmsForm';
95
+ for(let tagStart = template.indexOf(cmsForm, pos); -1 !== tagStart; tagStart=template.indexOf(cmsForm, pos)){
96
+ let tagEnd = this.findFormTagEnd(template, tagStart);
97
+ if(-1 === tagEnd){
98
+ pos = tagStart + cmsForm.length;
99
+ continue;
100
+ }
101
+ let fullTag = template.substring(tagStart, tagEnd);
102
+ let attributes = this.parseFormAttributes(fullTag);
103
+ formTags.push({
104
+ start: tagStart,
105
+ end: tagEnd,
106
+ attributes: attributes,
107
+ fullTag: fullTag
108
+ });
109
+ pos = tagEnd;
110
+ }
111
+ return formTags;
112
+ }
113
+
114
+ findFormTagEnd(template, tagStart)
115
+ {
116
+ let inQuotes = false;
117
+ let quoteChar = '';
118
+ let selfCloseTag = '/>';
119
+ let openCloseTag = '</cmsForm>';
120
+ for(let i = tagStart; i < template.length; i++){
121
+ let char = template[i];
122
+ if(!inQuotes && ('"' === char || "'" === char)){
123
+ inQuotes = true;
124
+ quoteChar = char;
125
+ continue;
126
+ }
127
+ if(inQuotes && char === quoteChar && '\\' !== template[i - 1]){
128
+ inQuotes = false;
129
+ quoteChar = '';
130
+ continue;
131
+ }
132
+ if(!inQuotes){
133
+ if(template.substring(i, i + selfCloseTag.length) === selfCloseTag){
134
+ return i + selfCloseTag.length;
135
+ }
136
+ if('>' === char){
137
+ let closeIndex = template.indexOf(openCloseTag, i);
138
+ if(-1 !== closeIndex){
139
+ return closeIndex + openCloseTag.length;
140
+ }
141
+ return i + 1;
142
+ }
143
+ }
144
+ }
145
+ return -1;
146
+ }
147
+
148
+ parseFormAttributes(fullTag)
149
+ {
150
+ let attributes = {};
151
+ let valueRegex = /(\w+)=(['"])((?:(?!\2)[^\\]|\\.)*)(\2)/g;
152
+ for(let match of fullTag.matchAll(valueRegex)){
153
+ attributes[match[1]] = match[3];
154
+ }
155
+ let booleanRegex = /\b(\w+)(?!\s*=)/g;
156
+ for(let match of fullTag.matchAll(booleanRegex)){
157
+ if(!sc.hasOwn(attributes, match[1]) && 'cmsForm' !== match[1]){
158
+ attributes[match[1]] = true;
159
+ }
160
+ }
161
+ return attributes;
162
+ }
163
+
164
+ parseFieldsFilter(attributes, formConfig)
165
+ {
166
+ let fieldsFilter = sc.get(attributes, 'fields', '');
167
+ if(!fieldsFilter){
168
+ return formConfig.fields_schema;
169
+ }
170
+ let fieldsSchema = sc.isString(formConfig.fields_schema) ? sc.toJson(formConfig.fields_schema) : formConfig.fields_schema;
171
+ if(!sc.isArray(fieldsSchema)){
172
+ return [];
173
+ }
174
+ let requestedFields = fieldsFilter.split(',').map(f => f.trim()).filter(f => '' !== f);
175
+ let filteredFields = [];
176
+ for(let fieldName of requestedFields){
177
+ let field = sc.fetchByProperty(fieldsSchema, 'name', fieldName);
178
+ if(field){
179
+ filteredFields.push(field);
180
+ }
181
+ }
182
+ return filteredFields;
183
+ }
184
+
185
+ }
186
+
187
+ module.exports.FormsTransformer = FormsTransformer;
@@ -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
 
@@ -9,6 +9,7 @@ const { EntitiesTransformer } = require('./template-engine/entities-transformer'
9
9
  const { CollectionsTransformer } = require('./template-engine/collections-transformer');
10
10
  const { CollectionsSingleTransformer } = require('./template-engine/collections-single-transformer');
11
11
  const { PartialsTransformer } = require('./template-engine/partials-transformer');
12
+ const { FormsTransformer } = require('./template-engine/forms-transformer');
12
13
  const { UrlTransformer } = require('./template-engine/url-transformer');
13
14
  const { AssetTransformer } = require('./template-engine/asset-transformer');
14
15
  const { DateTransformer } = require('./template-engine/date-transformer');
@@ -28,6 +29,8 @@ class TemplateEngine
28
29
  this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
29
30
  this.projectRoot = sc.get(props, 'projectRoot', './');
30
31
  this.publicPath = sc.get(props, 'publicPath', './public');
32
+ this.dynamicForm = sc.get(props, 'dynamicForm', false);
33
+ this.dynamicFormRenderer = sc.get(props, 'dynamicFormRenderer', false);
31
34
  this.jsonFieldsParser = new JsonFieldsParser({entitiesConfig: sc.get(props, 'entitiesConfig', {})});
32
35
  this.systemVariablesProvider = new SystemVariablesProvider({
33
36
  defaultDomain: this.defaultDomain,
@@ -59,8 +62,20 @@ class TemplateEngine
59
62
  });
60
63
  this.partialsTransformer = new PartialsTransformer({
61
64
  renderEngine: this.renderEngine,
62
- getPartials: this.getPartials
65
+ getPartials: this.getPartials,
66
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this)
63
67
  });
68
+ this.formsTransformer = false;
69
+ if(this.dynamicForm && this.dynamicFormRenderer){
70
+ this.formsTransformer = new FormsTransformer({
71
+ renderEngine: this.renderEngine,
72
+ getPartials: this.getPartials,
73
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this),
74
+ dynamicForm: this.dynamicForm,
75
+ dynamicFormRenderer: this.dynamicFormRenderer,
76
+ events: this.events
77
+ });
78
+ }
64
79
  this.urlTransformer = new UrlTransformer();
65
80
  this.assetTransformer = new AssetTransformer();
66
81
  this.dateTransformer = new DateTransformer({
@@ -81,15 +96,22 @@ class TemplateEngine
81
96
  this.dateTransformer,
82
97
  this.translateTransformer
83
98
  ];
99
+ if(this.formsTransformer){
100
+ this.transformers.push(this.formsTransformer);
101
+ }
84
102
  }
85
103
 
86
- async processAllTemplateFunctions(template, domain, req, systemVariables)
104
+ async processAllTemplateFunctions(template, domain, req, systemVariables, enhancedData = {})
87
105
  {
88
106
  let processedTemplate = template;
89
107
  for(let transformer of this.transformers){
90
- if(sc.isFunction(transformer.transform)){
91
- processedTemplate = await transformer.transform(processedTemplate, domain, req, systemVariables);
108
+ if(!transformer){
109
+ continue;
110
+ }
111
+ if(!sc.isFunction(transformer.transform)){
112
+ continue;
92
113
  }
114
+ processedTemplate = await transformer.transform(processedTemplate, domain, req, systemVariables, enhancedData);
93
115
  }
94
116
  return processedTemplate;
95
117
  }
@@ -134,7 +156,8 @@ class TemplateEngine
134
156
  beforeProcessData.content,
135
157
  domain,
136
158
  req,
137
- eventData.variables
159
+ eventData.variables,
160
+ enhancedData
138
161
  );
139
162
  let afterProcessData = {
140
163
  processedContent: processedTemplate,
@@ -0,0 +1,307 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - TemplateReloader
4
+ *
5
+ */
6
+
7
+ const { FileHandler } = require('@reldens/server-utils');
8
+ const { sc } = require('@reldens/utils');
9
+
10
+ class TemplateReloader
11
+ {
12
+
13
+ constructor(props = {})
14
+ {
15
+ this.reloadTime = sc.get(props, 'reloadTime', 0);
16
+ this.events = sc.get(props, 'events', false);
17
+ this.fileTracking = {};
18
+ this.reloadInterval = false;
19
+ this.isActive = -1 === this.reloadTime || 0 < this.reloadTime;
20
+ this.adminTemplatesLoader = sc.get(props, 'adminTemplatesLoader', false);
21
+ this.mappedAdminTemplates = sc.get(props, 'mappedAdminTemplates', {});
22
+ this.templatesPath = sc.get(props, 'templatesPath', '');
23
+ this.templateExtensions = sc.get(props, 'templateExtensions', []);
24
+ this.setupReloadInterval();
25
+ }
26
+
27
+ setupReloadInterval()
28
+ {
29
+ if(0 >= this.reloadTime){
30
+ return true;
31
+ }
32
+ if(this.reloadInterval){
33
+ clearInterval(this.reloadInterval);
34
+ }
35
+ this.reloadInterval = setInterval(() => {
36
+ this.checkAndReloadChangedTemplates();
37
+ }, this.reloadTime);
38
+ }
39
+
40
+ iterateTemplateFiles(templatesPaths, callback)
41
+ {
42
+ if(!templatesPaths){
43
+ return false;
44
+ }
45
+ if(sc.isString(templatesPaths)){
46
+ return callback(templatesPaths);
47
+ }
48
+ if(Array.isArray(templatesPaths)){
49
+ let result = false;
50
+ for(let filePath of templatesPaths){
51
+ if(callback(filePath)){
52
+ result = true;
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ if(sc.isObject(templatesPaths)){
58
+ let result = false;
59
+ for(let key of Object.keys(templatesPaths)){
60
+ let templateData = templatesPaths[key];
61
+ if(sc.isObject(templateData)){
62
+ if(this.iterateTemplateFiles(templateData, callback)){
63
+ result = true;
64
+ }
65
+ continue;
66
+ }
67
+ if(callback(templateData)){
68
+ result = true;
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ return false;
74
+ }
75
+
76
+ trackTemplateFiles(templatesPaths)
77
+ {
78
+ if(0 === this.reloadTime){
79
+ return true;
80
+ }
81
+ return this.iterateTemplateFiles(templatesPaths, (filePath) => {
82
+ if(!FileHandler.exists(filePath)){
83
+ return false;
84
+ }
85
+ let modTime = FileHandler.getFileModificationTime(filePath);
86
+ if(!modTime){
87
+ return false;
88
+ }
89
+ this.fileTracking[filePath] = {
90
+ lastModified: modTime,
91
+ isChanged: false
92
+ };
93
+ return true;
94
+ });
95
+ }
96
+
97
+ checkTemplateChanges(templatesPaths)
98
+ {
99
+ if(0 === this.reloadTime){
100
+ return false;
101
+ }
102
+ return this.iterateTemplateFiles(templatesPaths, (filePath) => {
103
+ if(!FileHandler.exists(filePath)){
104
+ return false;
105
+ }
106
+ let trackedFile = this.fileTracking[filePath];
107
+ if(!trackedFile){
108
+ this.trackTemplateFiles(filePath);
109
+ return false;
110
+ }
111
+ let currentModTime = FileHandler.getFileModificationTime(filePath);
112
+ if(!currentModTime){
113
+ return false;
114
+ }
115
+ if(currentModTime.getTime() > trackedFile.lastModified.getTime()){
116
+ if(-1 !== this.reloadTime){
117
+ trackedFile.lastModified = currentModTime;
118
+ }
119
+ trackedFile.isChanged = true;
120
+ return true;
121
+ }
122
+ return false;
123
+ });
124
+ }
125
+
126
+ hasChangedTemplates(templatesPaths)
127
+ {
128
+ return this.iterateTemplateFiles(templatesPaths, (filePath) => {
129
+ let trackedFile = this.fileTracking[filePath];
130
+ return trackedFile ? trackedFile.isChanged : false;
131
+ });
132
+ }
133
+
134
+ markTemplatesAsReloaded(templatesPaths)
135
+ {
136
+ if(0 === this.reloadTime){
137
+ return true;
138
+ }
139
+ return this.iterateTemplateFiles(templatesPaths, (filePath) => {
140
+ let trackedFile = this.fileTracking[filePath];
141
+ if(trackedFile){
142
+ trackedFile.isChanged = false;
143
+ }
144
+ return true;
145
+ });
146
+ }
147
+
148
+ shouldReloadAdminTemplates(mappedAdminTemplates)
149
+ {
150
+ if(0 === this.reloadTime){
151
+ return false;
152
+ }
153
+ if(-1 === this.reloadTime){
154
+ return this.checkTemplateChanges(mappedAdminTemplates);
155
+ }
156
+ return this.hasChangedTemplates(mappedAdminTemplates);
157
+ }
158
+
159
+ shouldReloadFrontendTemplates(templatesPath, templateExtensions)
160
+ {
161
+ if(0 === this.reloadTime){
162
+ return false;
163
+ }
164
+ let templateFiles = this.collectTemplateFiles(templatesPath, templateExtensions);
165
+ if(-1 === this.reloadTime){
166
+ return this.checkTemplateChanges(templateFiles);
167
+ }
168
+ return this.hasChangedTemplates(templateFiles);
169
+ }
170
+
171
+ collectTemplateFiles(templatesPath, templateExtensions)
172
+ {
173
+ let templateFiles = [];
174
+ if(!FileHandler.exists(templatesPath)){
175
+ return templateFiles;
176
+ }
177
+ let partialsPath = FileHandler.joinPaths(templatesPath, 'partials');
178
+ if(FileHandler.exists(partialsPath)){
179
+ let partialFiles = FileHandler.getFilesInFolder(partialsPath, templateExtensions);
180
+ for(let file of partialFiles){
181
+ templateFiles.push(FileHandler.joinPaths(partialsPath, file));
182
+ }
183
+ }
184
+ let domainsPath = FileHandler.joinPaths(templatesPath, 'domains');
185
+ if(FileHandler.exists(domainsPath)){
186
+ let domainFolders = FileHandler.fetchSubFoldersList(domainsPath);
187
+ for(let domain of domainFolders){
188
+ let domainPath = FileHandler.joinPaths(domainsPath, domain);
189
+ let domainPartialsPath = FileHandler.joinPaths(domainPath, 'partials');
190
+ if(FileHandler.exists(domainPartialsPath)){
191
+ let domainPartialFiles = FileHandler.getFilesInFolder(domainPartialsPath, templateExtensions);
192
+ for(let file of domainPartialFiles){
193
+ templateFiles.push(FileHandler.joinPaths(domainPartialsPath, file));
194
+ }
195
+ }
196
+ }
197
+ }
198
+ return templateFiles;
199
+ }
200
+
201
+ checkAndReloadChangedTemplates()
202
+ {
203
+ if(0 === this.reloadTime){
204
+ return false;
205
+ }
206
+ let hasChanges = false;
207
+ for(let filePath of Object.keys(this.fileTracking)){
208
+ if(this.checkTemplateChanges(filePath)){
209
+ hasChanges = true;
210
+ }
211
+ }
212
+ if(hasChanges && this.events){
213
+ this.events.emit('reldens.templateReloader.templatesChanged', {
214
+ reloader: this,
215
+ changedFiles: this.getChangedFiles()
216
+ });
217
+ }
218
+ return hasChanges;
219
+ }
220
+
221
+ getChangedFiles()
222
+ {
223
+ let changedFiles = [];
224
+ for(let filePath of Object.keys(this.fileTracking)){
225
+ let trackedFile = this.fileTracking[filePath];
226
+ if(trackedFile && trackedFile.isChanged){
227
+ changedFiles.push(filePath);
228
+ }
229
+ }
230
+ return changedFiles;
231
+ }
232
+
233
+ destroy()
234
+ {
235
+ if(this.reloadInterval){
236
+ clearInterval(this.reloadInterval);
237
+ this.reloadInterval = false;
238
+ }
239
+ this.fileTracking = {};
240
+ }
241
+
242
+ async checkAndReloadAdminTemplates()
243
+ {
244
+ if(!this.adminTemplatesLoader){
245
+ return false;
246
+ }
247
+ if(!this.shouldReloadAdminTemplates(this.mappedAdminTemplates)){
248
+ return false;
249
+ }
250
+ let newAdminFilesContents = await this.adminTemplatesLoader.fetchAdminFilesContents(this.mappedAdminTemplates);
251
+ if(!newAdminFilesContents){
252
+ return false;
253
+ }
254
+ this.markTemplatesAsReloaded(this.mappedAdminTemplates);
255
+ return newAdminFilesContents;
256
+ }
257
+
258
+ async checkAndReloadFrontendTemplates()
259
+ {
260
+ if(!this.shouldReloadFrontendTemplates(this.templatesPath, this.templateExtensions)){
261
+ return false;
262
+ }
263
+ let templateFiles = this.collectTemplateFiles(this.templatesPath, this.templateExtensions);
264
+ this.markTemplatesAsReloaded(templateFiles);
265
+ return true;
266
+ }
267
+
268
+ async updateAdminContentsAfterReload(newAdminFilesContents, adminManager)
269
+ {
270
+ if(!newAdminFilesContents || !adminManager){
271
+ return false;
272
+ }
273
+ adminManager.adminFilesContents = newAdminFilesContents;
274
+ adminManager.contentsBuilder.adminFilesContents = newAdminFilesContents;
275
+ adminManager.routerContents.adminFilesContents = newAdminFilesContents;
276
+ adminManager.addCacheButtonSubscriber.cacheCleanButton = newAdminFilesContents.cacheCleanButton;
277
+ adminManager.addCacheButtonSubscriber.clearAllCacheButton = newAdminFilesContents.clearAllCacheButton;
278
+ await adminManager.contentsBuilder.buildAdminContents();
279
+ return true;
280
+ }
281
+
282
+ async handleAdminTemplateReload(adminManager)
283
+ {
284
+ if(0 === this.reloadTime){
285
+ return false;
286
+ }
287
+ let reloadedContents = await this.checkAndReloadAdminTemplates();
288
+ if(reloadedContents){
289
+ await this.updateAdminContentsAfterReload(reloadedContents, adminManager);
290
+ }
291
+ return reloadedContents;
292
+ }
293
+
294
+ async handleFrontendTemplateReload(templateCache, templateResolver)
295
+ {
296
+ let reloadResult = await this.checkAndReloadFrontendTemplates();
297
+ if(reloadResult){
298
+ await templateCache.loadPartials();
299
+ await templateCache.setupDomainTemplates();
300
+ templateResolver.domainTemplatesMap = templateCache.getDomainTemplatesMap();
301
+ }
302
+ return reloadResult;
303
+ }
304
+
305
+ }
306
+
307
+ module.exports.TemplateReloader = TemplateReloader;
@@ -22,6 +22,8 @@ module.exports.TemplatesList = {
22
22
  clearAllCacheButton: 'clear-all-cache-button.html',
23
23
  fields: {
24
24
  view: {
25
+ audio: 'audio.html',
26
+ audios: 'audios.html',
25
27
  text: 'text.html',
26
28
  textarea: 'textarea.html',
27
29
  image: 'image.html',
@@ -0,0 +1,22 @@
1
+
2
+ -- Default CMS Forms
3
+
4
+ CREATE TABLE `cms_forms` (
5
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
6
+ `form_key` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',
7
+ `fields_schema` JSON NOT NULL,
8
+ `enabled` TINYINT UNSIGNED NOT NULL DEFAULT '0',
9
+ `created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
10
+ `updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
11
+ PRIMARY KEY (`id`) USING BTREE,
12
+ UNIQUE INDEX `form_key` (`form_key`) USING BTREE
13
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
14
+
15
+
16
+ CREATE TABLE `cms_forms_submitted` (
17
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
18
+ `form_id` INT UNSIGNED NOT NULL,
19
+ `submitted_values` JSON NOT NULL,
20
+ PRIMARY KEY (`id`) USING BTREE,
21
+ INDEX `FK__cms_forms` (`form_id`) USING BTREE
22
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/cms",
3
3
  "scope": "@reldens",
4
- "version": "0.20.0",
4
+ "version": "0.23.0",
5
5
  "description": "Reldens - CMS",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -33,10 +33,10 @@
33
33
  "url": "https://github.com/damian-pastorini/reldens-cms/issues"
34
34
  },
35
35
  "dependencies": {
36
- "@reldens/server-utils": "^0.21.0",
37
- "@reldens/storage": "^0.61.0",
38
- "@reldens/utils": "^0.50.0",
39
- "dotenv": "^17.0.1",
36
+ "@reldens/server-utils": "^0.24.0",
37
+ "@reldens/storage": "^0.65.0",
38
+ "@reldens/utils": "^0.52.0",
39
+ "dotenv": "^17.2.1",
40
40
  "mustache": "^4.2.0"
41
41
  }
42
42
  }
@@ -2,7 +2,7 @@
2
2
  <browserconfig>
3
3
  <msapplication>
4
4
  <tile>
5
- <square150x150logo src="/assets/favicons/mstile-150x150.png"/>
5
+ <square150x150logo src="/assets/favicons/default/mstile-150x150.png"/>
6
6
  <TileColor>#000000</TileColor>
7
7
  </tile>
8
8
  </msapplication>