@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.
- package/README.md +648 -12
- package/admin/reldens-admin-client.css +77 -141
- package/admin/reldens-admin-client.js +108 -133
- package/admin/templates/clear-all-cache-button.html +7 -7
- package/admin/templates/edit.html +7 -0
- package/admin/templates/fields/view/audio.html +7 -0
- package/admin/templates/fields/view/audios.html +8 -0
- package/admin/templates/layout.html +15 -9
- package/admin/templates/list-content.html +4 -2
- package/admin/templates/list.html +24 -8
- package/admin/templates/view.html +21 -0
- package/install/index.html +4 -0
- package/lib/admin-manager/admin-filters-manager.js +177 -0
- package/lib/admin-manager/contents-builder.js +1 -0
- package/lib/admin-manager/default-translations.js +38 -0
- package/lib/admin-manager/router-contents.js +64 -52
- package/lib/admin-manager/router.js +19 -0
- package/lib/dynamic-form-renderer.js +228 -0
- package/lib/dynamic-form-request-handler.js +135 -0
- package/lib/dynamic-form.js +310 -0
- package/lib/frontend/content-renderer.js +178 -0
- package/lib/frontend/entity-access-manager.js +63 -0
- package/lib/frontend/request-processor.js +128 -0
- package/lib/frontend/response-manager.js +54 -0
- package/lib/frontend/template-cache.js +102 -0
- package/lib/frontend/template-resolver.js +111 -0
- package/lib/frontend.js +122 -629
- package/lib/installer.js +2 -1
- package/lib/manager.js +25 -12
- package/lib/search-renderer.js +15 -7
- package/lib/search-request-handler.js +67 -0
- package/lib/search.js +13 -1
- package/lib/template-engine/collections-single-transformer.js +11 -5
- package/lib/template-engine/collections-transformer.js +47 -34
- package/lib/template-engine/entities-transformer.js +3 -2
- package/lib/template-engine/forms-transformer.js +187 -0
- package/lib/template-engine/partials-transformer.js +5 -6
- package/lib/template-engine/system-variables-provider.js +4 -1
- package/lib/template-engine.js +28 -5
- package/lib/template-reloader.js +307 -0
- package/lib/templates-list.js +2 -0
- package/migrations/default-forms.sql +22 -0
- package/package.json +5 -5
- package/templates/{browserconfig.xml → assets/favicons/default/browserconfig.xml} +1 -1
- package/templates/assets/favicons/default/favicon.ico +0 -0
- package/templates/{site.webmanifest → assets/favicons/default/site.webmanifest} +3 -3
- package/templates/cms_forms/field_email.html +14 -0
- package/templates/cms_forms/field_number.html +17 -0
- package/templates/cms_forms/field_select.html +15 -0
- package/templates/cms_forms/field_text.html +16 -0
- package/templates/cms_forms/field_textarea.html +13 -0
- package/templates/cms_forms/form.html +22 -0
- package/templates/css/styles.css +4 -0
- package/templates/js/functions.js +144 -0
- package/templates/js/scripts.js +5 -0
- package/templates/page.html +11 -5
- package/templates/partials/pagedCollection.html +1 -1
- package/lib/admin-translations.js +0 -56
- package/templates/favicon.ico +0 -0
- /package/templates/assets/favicons/{android-icon-144x144.png → default/android-icon-144x144.png} +0 -0
- /package/templates/assets/favicons/{android-icon-192x192.png → default/android-icon-192x192.png} +0 -0
- /package/templates/assets/favicons/{android-icon-512x512.png → default/android-icon-512x512.png} +0 -0
- /package/templates/assets/favicons/{apple-touch-icon.png → default/apple-touch-icon.png} +0 -0
- /package/templates/assets/favicons/{favicon-16x16.png → default/favicon-16x16.png} +0 -0
- /package/templates/assets/favicons/{favicon-32x32.png → default/favicon-32x32.png} +0 -0
- /package/templates/assets/favicons/{mstile-150x150.png → default/mstile-150x150.png} +0 -0
- /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
|
|
35
|
-
let
|
|
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
|
-
|
|
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
|
|
package/lib/template-engine.js
CHANGED
|
@@ -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(
|
|
91
|
-
|
|
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;
|
package/lib/templates-list.js
CHANGED
|
@@ -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.
|
|
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.
|
|
37
|
-
"@reldens/storage": "^0.
|
|
38
|
-
"@reldens/utils": "^0.
|
|
39
|
-
"dotenv": "^17.
|
|
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
|
}
|
|
Binary file
|