@reldens/cms 0.15.0 → 0.18.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 +150 -34
- package/admin/reldens-admin-client.css +39 -23
- package/admin/reldens-admin-client.js +7 -0
- package/admin/templates/cache-clean-button.html +4 -0
- package/admin/templates/edit.html +3 -1
- package/admin/templates/fields/view/textarea.html +1 -0
- package/admin/templates/sections/editForm/cms-pages.html +15 -0
- package/admin/templates/sections/viewForm/cms-pages.html +15 -0
- package/admin/templates/view.html +1 -0
- package/bin/reldens-cms-generate-entities.js +116 -5
- package/bin/reldens-cms.js +26 -8
- package/install/js/installer.js +5 -0
- package/install/success.html +1 -1
- package/lib/admin-manager/contents-builder.js +256 -0
- package/lib/admin-manager/router-contents.js +576 -0
- package/lib/admin-manager/router.js +208 -0
- package/lib/admin-manager.js +114 -944
- package/lib/cache/add-cache-button-subscriber.js +101 -0
- package/lib/cache/cache-manager.js +129 -0
- package/lib/cache/cache-routes-handler.js +76 -0
- package/lib/cms-pages-route-manager.js +117 -0
- package/lib/frontend.js +207 -64
- package/lib/installer.js +44 -20
- package/lib/json-fields-parser.js +74 -0
- package/lib/manager.js +55 -10
- package/lib/template-engine.js +361 -41
- package/lib/templates-list.js +10 -0
- package/migrations/default-blocks.sql +1 -1
- package/migrations/default-entity-access.sql +2 -2
- package/migrations/default-homepage.sql +27 -7
- package/migrations/install.sql +33 -36
- package/package.json +3 -3
- package/templates/index.js.dist +3 -3
- package/templates/js/scripts.js +5 -0
- package/templates/layouts/default.html +4 -4
- package/templates/page.html +14 -10
- package/templates/partials/footer.html +2 -23
- package/templates/partials/header.html +2 -35
package/lib/template-engine.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
const { JsonFieldsParser } = require('./json-fields-parser');
|
|
8
9
|
|
|
9
10
|
class TemplateEngine
|
|
10
11
|
{
|
|
@@ -13,6 +14,27 @@ class TemplateEngine
|
|
|
13
14
|
{
|
|
14
15
|
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
15
16
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
17
|
+
this.getPartials = sc.get(props, 'getPartials', false);
|
|
18
|
+
this.currentDomain = '';
|
|
19
|
+
this.jsonFieldsParser = new JsonFieldsParser({
|
|
20
|
+
entitiesConfig: sc.get(props, 'entitiesConfig', {})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setCurrentDomain(domain)
|
|
25
|
+
{
|
|
26
|
+
this.currentDomain = domain;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async processAllTemplateFunctions(template)
|
|
30
|
+
{
|
|
31
|
+
return await this.processCustomPartials(
|
|
32
|
+
await this.processLoopCollections(
|
|
33
|
+
await this.processSingleFieldCollections(
|
|
34
|
+
await this.processEntityFunctions(template)
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
);
|
|
16
38
|
}
|
|
17
39
|
|
|
18
40
|
async render(template, data, partials)
|
|
@@ -26,37 +48,50 @@ class TemplateEngine
|
|
|
26
48
|
return '';
|
|
27
49
|
}
|
|
28
50
|
return this.renderEngine.render(
|
|
29
|
-
await this.
|
|
30
|
-
await this.processSingleFieldCollections(
|
|
31
|
-
await this.processEntityFunctions(template)
|
|
32
|
-
)
|
|
33
|
-
),
|
|
51
|
+
this.unescapeHtml(await this.processAllTemplateFunctions(template)),
|
|
34
52
|
data,
|
|
35
53
|
partials
|
|
36
54
|
);
|
|
37
55
|
}
|
|
38
56
|
|
|
57
|
+
unescapeHtml(text)
|
|
58
|
+
{
|
|
59
|
+
return text
|
|
60
|
+
.replace(/"/g, '"')
|
|
61
|
+
.replace(/'/g, "'")
|
|
62
|
+
.replace(/'/g, "'")
|
|
63
|
+
.replace(/=/g, '=')
|
|
64
|
+
.replace(///g, '/')
|
|
65
|
+
.replace(/</g, '<')
|
|
66
|
+
.replace(/>/g, '>')
|
|
67
|
+
.replace(/&/g, '&');
|
|
68
|
+
}
|
|
69
|
+
|
|
39
70
|
getEntityRegex()
|
|
40
71
|
{
|
|
41
|
-
return
|
|
72
|
+
return /<entity\s+name="([^"]+)"(?:\s+field="([^"]+)"\s+value="([^"]+)"|\s+id="([^"]+)")?\s*\/?>/g;
|
|
42
73
|
}
|
|
43
74
|
|
|
44
75
|
getSingleFieldCollectionRegex()
|
|
45
76
|
{
|
|
46
|
-
return
|
|
77
|
+
return /<collection\s+([^>]+)\/>/g;
|
|
47
78
|
}
|
|
48
79
|
|
|
49
80
|
getLoopCollectionStartRegex()
|
|
50
81
|
{
|
|
51
|
-
return
|
|
82
|
+
return /<collection\s+([^>]+)>/g;
|
|
52
83
|
}
|
|
53
84
|
|
|
54
|
-
getLoopCollectionEndRegex(
|
|
85
|
+
getLoopCollectionEndRegex()
|
|
55
86
|
{
|
|
56
|
-
return new RegExp('
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
return new RegExp('<\\/collection>');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
extractAttributeValue(tagContent, attributeName)
|
|
91
|
+
{
|
|
92
|
+
let regex = new RegExp(attributeName + '=([\'"])([^\'"]*)\\1');
|
|
93
|
+
let match = tagContent.match(regex);
|
|
94
|
+
return match ? match[2] : '';
|
|
60
95
|
}
|
|
61
96
|
|
|
62
97
|
async processEntityFunctions(template)
|
|
@@ -64,10 +99,18 @@ class TemplateEngine
|
|
|
64
99
|
let processedTemplate = template;
|
|
65
100
|
for(let match of template.matchAll(this.getEntityRegex())){
|
|
66
101
|
let tableName = match[1];
|
|
67
|
-
let
|
|
68
|
-
let
|
|
69
|
-
|
|
70
|
-
|
|
102
|
+
let field = sc.get(match, '2', 'id');
|
|
103
|
+
let value = sc.get(match, '3', sc.get(match, '4', ''));
|
|
104
|
+
if(!value){
|
|
105
|
+
Logger.warning('Entity tag missing value: '+match[0]);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
processedTemplate = processedTemplate.replace(
|
|
109
|
+
match[0],
|
|
110
|
+
await this.processAllTemplateFunctions(
|
|
111
|
+
sc.get(await this.fetchEntityForTemplate(tableName, value, field), 'content', '')
|
|
112
|
+
)
|
|
113
|
+
);
|
|
71
114
|
}
|
|
72
115
|
return processedTemplate;
|
|
73
116
|
}
|
|
@@ -76,13 +119,19 @@ class TemplateEngine
|
|
|
76
119
|
{
|
|
77
120
|
let processedTemplate = template;
|
|
78
121
|
for(let match of template.matchAll(this.getSingleFieldCollectionRegex())){
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
122
|
+
let tagContent = match[1];
|
|
123
|
+
if(!tagContent.includes('field=')){
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
let tableName = this.extractAttributeValue(tagContent, 'name');
|
|
127
|
+
let filtersJson = this.extractAttributeValue(tagContent, 'filters');
|
|
128
|
+
let queryOptionsJson = this.extractAttributeValue(tagContent, 'data');
|
|
129
|
+
let relationsString = this.extractAttributeValue(tagContent, 'relations');
|
|
130
|
+
let fieldName = this.extractAttributeValue(tagContent, 'field');
|
|
82
131
|
processedTemplate = processedTemplate.replace(
|
|
83
132
|
match[0],
|
|
84
133
|
this.extractFieldValues(
|
|
85
|
-
await this.fetchCollectionForTemplate(tableName, filtersJson),
|
|
134
|
+
await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString),
|
|
86
135
|
fieldName
|
|
87
136
|
)
|
|
88
137
|
);
|
|
@@ -96,17 +145,181 @@ class TemplateEngine
|
|
|
96
145
|
let matches = [...template.matchAll(this.getLoopCollectionStartRegex())];
|
|
97
146
|
for(let i = matches.length - 1; i >= 0; i--){
|
|
98
147
|
let startMatch = matches[i];
|
|
99
|
-
let
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
148
|
+
let tagContent = startMatch[1];
|
|
149
|
+
if(tagContent.includes('field=')){
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let tableName = this.extractAttributeValue(tagContent, 'name');
|
|
153
|
+
let filtersJson = this.extractAttributeValue(tagContent, 'filters');
|
|
154
|
+
let queryOptionsJson = this.extractAttributeValue(tagContent, 'data');
|
|
155
|
+
let relationsString = this.extractAttributeValue(tagContent, 'relations');
|
|
156
|
+
let loopResult = await this.processLoopCollection(
|
|
157
|
+
processedTemplate,
|
|
158
|
+
startMatch,
|
|
159
|
+
tableName,
|
|
160
|
+
filtersJson,
|
|
161
|
+
relationsString,
|
|
162
|
+
queryOptionsJson
|
|
163
|
+
);
|
|
164
|
+
if(loopResult){
|
|
103
165
|
processedTemplate = loopResult;
|
|
104
166
|
}
|
|
105
167
|
}
|
|
106
168
|
return processedTemplate;
|
|
107
169
|
}
|
|
108
170
|
|
|
109
|
-
async
|
|
171
|
+
async processCustomPartials(template)
|
|
172
|
+
{
|
|
173
|
+
let processedTemplate = template;
|
|
174
|
+
let partialTags = this.findAllPartialTags(template);
|
|
175
|
+
for(let i = partialTags.length - 1; i >= 0; i--){
|
|
176
|
+
let tag = partialTags[i];
|
|
177
|
+
let partialContent = this.loadPartialTemplate(tag.name);
|
|
178
|
+
if(!partialContent){
|
|
179
|
+
Logger.warning('Partial template not found: ' + tag.name);
|
|
180
|
+
processedTemplate = processedTemplate.substring(0, tag.start)+''+processedTemplate.substring(tag.end);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
|
|
184
|
+
let renderData = { vars: tag.attributes };
|
|
185
|
+
let partials = {[tag.name]: partialContent};
|
|
186
|
+
processedTemplate = processedTemplate.substring(0, tag.start) +
|
|
187
|
+
this.renderEngine.render(wrapperTemplate, renderData, partials) +
|
|
188
|
+
processedTemplate.substring(tag.end);
|
|
189
|
+
}
|
|
190
|
+
return processedTemplate;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
findAllPartialTags(template)
|
|
194
|
+
{
|
|
195
|
+
let partialTags = [];
|
|
196
|
+
let pos = 0;
|
|
197
|
+
let partial = '<partial';
|
|
198
|
+
for(let tagStart = template.indexOf(partial, pos); -1 !== tagStart; tagStart=template.indexOf(partial, pos)){
|
|
199
|
+
let tagEnd = this.findPartialTagEnd(template, tagStart);
|
|
200
|
+
if(-1 === tagEnd){
|
|
201
|
+
pos = tagStart + partial.length;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
let fullTag = template.substring(tagStart, tagEnd);
|
|
205
|
+
let nameMatch = fullTag.match(/name=["']([^"']+)["']/);
|
|
206
|
+
if(!nameMatch){
|
|
207
|
+
pos = tagStart + partial.length;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
let partialName = nameMatch[1];
|
|
211
|
+
let attributes = this.parsePartialAttributes(fullTag, partialName);
|
|
212
|
+
partialTags.push({
|
|
213
|
+
start: tagStart,
|
|
214
|
+
end: tagEnd,
|
|
215
|
+
name: partialName,
|
|
216
|
+
attributes: attributes,
|
|
217
|
+
fullTag: fullTag
|
|
218
|
+
});
|
|
219
|
+
pos = tagEnd;
|
|
220
|
+
}
|
|
221
|
+
return partialTags;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
findPartialTagEnd(template, tagStart)
|
|
225
|
+
{
|
|
226
|
+
let inQuotes = false;
|
|
227
|
+
let quoteChar = '';
|
|
228
|
+
let selfCloseTag = '/>';
|
|
229
|
+
let openCloseTag = '</partial>';
|
|
230
|
+
for(let i = tagStart; i < template.length; i++){
|
|
231
|
+
let char = template[i];
|
|
232
|
+
if(!inQuotes && ('"' === char || "'" === char)){
|
|
233
|
+
inQuotes = true;
|
|
234
|
+
quoteChar = char;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if(inQuotes && char === quoteChar && '\\' !== template[i - 1]){
|
|
238
|
+
inQuotes = false;
|
|
239
|
+
quoteChar = '';
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if(!inQuotes){
|
|
243
|
+
if(template.substring(i, i + selfCloseTag.length) === selfCloseTag){
|
|
244
|
+
return i + selfCloseTag.length;
|
|
245
|
+
}
|
|
246
|
+
if('>' === char){
|
|
247
|
+
let closeIndex = template.indexOf(openCloseTag, i);
|
|
248
|
+
if(-1 !== closeIndex){
|
|
249
|
+
return closeIndex + openCloseTag.length;
|
|
250
|
+
}
|
|
251
|
+
return i + 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
parsePartialAttributes(fullTag, partialName)
|
|
259
|
+
{
|
|
260
|
+
let namePattern = 'name=' + this.getQuotePattern(fullTag, partialName);
|
|
261
|
+
let nameIndex = fullTag.indexOf(namePattern);
|
|
262
|
+
if(-1 === nameIndex){
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
let attributesStart = nameIndex + namePattern.length;
|
|
266
|
+
let attributesEnd = fullTag.lastIndexOf('/>');
|
|
267
|
+
if(-1 === attributesEnd){
|
|
268
|
+
attributesEnd = fullTag.lastIndexOf('</partial>');
|
|
269
|
+
}
|
|
270
|
+
if(-1 === attributesEnd){
|
|
271
|
+
attributesEnd = fullTag.lastIndexOf('>');
|
|
272
|
+
}
|
|
273
|
+
if(-1 === attributesEnd || attributesEnd <= attributesStart){
|
|
274
|
+
return {};
|
|
275
|
+
}
|
|
276
|
+
let attributesString = fullTag.substring(attributesStart, attributesEnd).trim();
|
|
277
|
+
return this.extractAttributesObject(attributesString);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
getQuotePattern(fullTag, partialName)
|
|
281
|
+
{
|
|
282
|
+
if(fullTag.includes('name="' + partialName + '"')){
|
|
283
|
+
return '"' + partialName + '"';
|
|
284
|
+
}
|
|
285
|
+
if(fullTag.includes("name='" + partialName + "'")){
|
|
286
|
+
return "'" + partialName + "'";
|
|
287
|
+
}
|
|
288
|
+
return '"' + partialName + '"';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
extractAttributesObject(attributesString)
|
|
292
|
+
{
|
|
293
|
+
if(!attributesString){
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
let attributes = {};
|
|
297
|
+
let valueRegex = /(\w+)=(['"])((?:(?!\2)[^\\]|\\.)*)(\2)/g;
|
|
298
|
+
for(let match of attributesString.matchAll(valueRegex)){
|
|
299
|
+
attributes[match[1]] = match[3];
|
|
300
|
+
}
|
|
301
|
+
let booleanRegex = /\b(\w+)(?!\s*=)/g;
|
|
302
|
+
for(let match of attributesString.matchAll(booleanRegex)){
|
|
303
|
+
if(!sc.hasOwn(attributes, match[1])){
|
|
304
|
+
attributes[match[1]] = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return attributes;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
loadPartialTemplate(partialName)
|
|
311
|
+
{
|
|
312
|
+
if(!this.getPartials){
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
let partials = this.getPartials(this.currentDomain);
|
|
316
|
+
if(sc.hasOwn(partials, partialName)){
|
|
317
|
+
return partials[partialName];
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async processLoopCollection(template, startMatch, tableName, filtersJson, relationsString, queryOptionsJson)
|
|
110
323
|
{
|
|
111
324
|
let startPos = startMatch.index;
|
|
112
325
|
let startEnd = startPos + startMatch[0].length;
|
|
@@ -116,10 +329,14 @@ class TemplateEngine
|
|
|
116
329
|
return false;
|
|
117
330
|
}
|
|
118
331
|
let endPos = startEnd + endMatch.index;
|
|
119
|
-
let
|
|
120
|
-
|
|
121
|
-
|
|
332
|
+
let loopContent = template.substring(startEnd, endPos);
|
|
333
|
+
let collectionData = await this.fetchCollectionForTemplate(
|
|
334
|
+
tableName,
|
|
335
|
+
filtersJson,
|
|
336
|
+
queryOptionsJson,
|
|
337
|
+
relationsString
|
|
122
338
|
);
|
|
339
|
+
let renderedContent = await this.renderCollectionLoop(loopContent, collectionData);
|
|
123
340
|
return template.substring(0, startPos) + renderedContent + template.substring(endPos + endMatch[0].length);
|
|
124
341
|
}
|
|
125
342
|
|
|
@@ -127,11 +344,39 @@ class TemplateEngine
|
|
|
127
344
|
{
|
|
128
345
|
let renderedContent = '';
|
|
129
346
|
for(let row of collectionData){
|
|
130
|
-
renderedContent += this.
|
|
347
|
+
renderedContent += await this.processPartialsInLoop(loopContent, row);
|
|
131
348
|
}
|
|
132
349
|
return renderedContent;
|
|
133
350
|
}
|
|
134
351
|
|
|
352
|
+
async processPartialsInLoop(content, rowData)
|
|
353
|
+
{
|
|
354
|
+
let processedContent = content;
|
|
355
|
+
let partialTags = this.findAllPartialTags(content);
|
|
356
|
+
for(let i = partialTags.length - 1; i >= 0; i--){
|
|
357
|
+
let tag = partialTags[i];
|
|
358
|
+
let partialContent = this.loadPartialTemplate(tag.name);
|
|
359
|
+
if(!partialContent){
|
|
360
|
+
Logger.warning('Partial template not found: ' + tag.name);
|
|
361
|
+
processedContent = processedContent.substring(0, tag.start)+''+processedContent.substring(tag.end);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if(sc.hasOwn(tag.attributes, 'row')){
|
|
365
|
+
processedContent = processedContent.substring(0, tag.start)
|
|
366
|
+
+this.renderEngine.render(partialContent, {row: rowData}, this.getPartials(this.currentDomain))
|
|
367
|
+
+processedContent.substring(tag.end);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
|
|
371
|
+
let renderData = { vars: tag.attributes };
|
|
372
|
+
let partials = {[tag.name]: partialContent};
|
|
373
|
+
processedContent = processedContent.substring(0, tag.start) +
|
|
374
|
+
this.renderEngine.render(wrapperTemplate, renderData, partials) +
|
|
375
|
+
processedContent.substring(tag.end);
|
|
376
|
+
}
|
|
377
|
+
return this.renderEngine.render(processedContent, {row: rowData}, this.getPartials(this.currentDomain));
|
|
378
|
+
}
|
|
379
|
+
|
|
135
380
|
extractFieldValues(collectionData, fieldName)
|
|
136
381
|
{
|
|
137
382
|
let fieldValues = '';
|
|
@@ -141,11 +386,6 @@ class TemplateEngine
|
|
|
141
386
|
return fieldValues;
|
|
142
387
|
}
|
|
143
388
|
|
|
144
|
-
escapeRegex(string)
|
|
145
|
-
{
|
|
146
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
389
|
async fetchEntityForTemplate(tableName, identifier, identifierField)
|
|
150
390
|
{
|
|
151
391
|
let entity = this.dataServer.getEntity(tableName);
|
|
@@ -153,21 +393,101 @@ class TemplateEngine
|
|
|
153
393
|
Logger.warning('Entity not found in dataServer: '+tableName);
|
|
154
394
|
return false;
|
|
155
395
|
}
|
|
156
|
-
|
|
396
|
+
let result = await entity.loadOneBy(identifierField, identifier);
|
|
397
|
+
if(!result){
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
return this.jsonFieldsParser.parseJsonFields(
|
|
401
|
+
result,
|
|
402
|
+
this.jsonFieldsParser.getJsonFieldsForEntity(tableName)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
convertJsObjectToJson(jsObjectString)
|
|
407
|
+
{
|
|
408
|
+
if(!jsObjectString || '' === jsObjectString.trim()){
|
|
409
|
+
return '';
|
|
410
|
+
}
|
|
411
|
+
return jsObjectString.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
parseRelationsString(relationsString)
|
|
415
|
+
{
|
|
416
|
+
if(!relationsString || '' === relationsString.trim()){
|
|
417
|
+
return [];
|
|
418
|
+
}
|
|
419
|
+
return relationsString.split(',').map(relation => relation.trim()).filter(relation => '' !== relation);
|
|
157
420
|
}
|
|
158
421
|
|
|
159
|
-
async fetchCollectionForTemplate(tableName, filtersJson)
|
|
422
|
+
async fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString)
|
|
160
423
|
{
|
|
161
424
|
let entity = this.dataServer.getEntity(tableName);
|
|
162
425
|
if(!entity){
|
|
163
426
|
Logger.warning('Entity not found in dataServer: '+tableName);
|
|
164
427
|
return [];
|
|
165
428
|
}
|
|
166
|
-
let filters =
|
|
167
|
-
if(
|
|
168
|
-
|
|
429
|
+
let filters = false;
|
|
430
|
+
if(filtersJson && '' !== filtersJson.trim()){
|
|
431
|
+
let convertedFiltersJson = this.convertJsObjectToJson(filtersJson);
|
|
432
|
+
filters = sc.parseJson(convertedFiltersJson, false);
|
|
433
|
+
if(!filters){
|
|
434
|
+
Logger.warning('Invalid filters JSON: '+filtersJson);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
let originalState = this.preserveEntityState(entity);
|
|
438
|
+
let queryOptions = {};
|
|
439
|
+
if(queryOptionsJson && '' !== queryOptionsJson.trim()){
|
|
440
|
+
let convertedOptionsJson = this.convertJsObjectToJson(queryOptionsJson);
|
|
441
|
+
queryOptions = sc.parseJson(convertedOptionsJson, {});
|
|
442
|
+
if(!queryOptions){
|
|
443
|
+
Logger.warning('Invalid query options JSON: '+queryOptionsJson);
|
|
444
|
+
queryOptions = {};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
this.applyQueryOptions(entity, queryOptions);
|
|
448
|
+
let relations = this.parseRelationsString(relationsString);
|
|
449
|
+
let result = 0 < relations.length
|
|
450
|
+
? await entity.loadWithRelations(filters || {}, relations)
|
|
451
|
+
: filters ? await entity.load(filters) : await entity.loadAll();
|
|
452
|
+
this.restoreEntityState(entity, originalState);
|
|
453
|
+
return this.jsonFieldsParser.parseJsonFields(
|
|
454
|
+
result,
|
|
455
|
+
this.jsonFieldsParser.getJsonFieldsForEntity(tableName)
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
preserveEntityState(entity)
|
|
460
|
+
{
|
|
461
|
+
return {
|
|
462
|
+
limit: entity.limit,
|
|
463
|
+
offset: entity.offset,
|
|
464
|
+
sortBy: entity.sortBy,
|
|
465
|
+
sortDirection: entity.sortDirection
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
restoreEntityState(entity, originalState)
|
|
470
|
+
{
|
|
471
|
+
entity.limit = originalState.limit;
|
|
472
|
+
entity.offset = originalState.offset;
|
|
473
|
+
entity.sortBy = originalState.sortBy;
|
|
474
|
+
entity.sortDirection = originalState.sortDirection;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
applyQueryOptions(entity, queryOptions)
|
|
478
|
+
{
|
|
479
|
+
if(sc.hasOwn(queryOptions, 'limit')){
|
|
480
|
+
entity.limit = queryOptions.limit;
|
|
481
|
+
}
|
|
482
|
+
if(sc.hasOwn(queryOptions, 'offset')){
|
|
483
|
+
entity.offset = queryOptions.offset;
|
|
484
|
+
}
|
|
485
|
+
if(sc.hasOwn(queryOptions, 'sortBy')){
|
|
486
|
+
entity.sortBy = queryOptions.sortBy;
|
|
487
|
+
}
|
|
488
|
+
if(sc.hasOwn(queryOptions, 'sortDirection')){
|
|
489
|
+
entity.sortDirection = queryOptions.sortDirection;
|
|
169
490
|
}
|
|
170
|
-
return await entity.load(filters);
|
|
171
491
|
}
|
|
172
492
|
|
|
173
493
|
}
|
package/lib/templates-list.js
CHANGED
|
@@ -18,9 +18,11 @@ module.exports.TemplatesList = {
|
|
|
18
18
|
sideBarItem: 'sidebar-item.html',
|
|
19
19
|
paginationLink: 'pagination-link.html',
|
|
20
20
|
defaultCopyRight: 'default-copyright.html',
|
|
21
|
+
cacheCleanButton: 'cache-clean-button.html',
|
|
21
22
|
fields: {
|
|
22
23
|
view: {
|
|
23
24
|
text: 'text.html',
|
|
25
|
+
textarea: 'textarea.html',
|
|
24
26
|
image: 'image.html',
|
|
25
27
|
images: 'images.html',
|
|
26
28
|
link: 'link.html',
|
|
@@ -37,5 +39,13 @@ module.exports.TemplatesList = {
|
|
|
37
39
|
button: 'button.html',
|
|
38
40
|
file: 'file.html'
|
|
39
41
|
}
|
|
42
|
+
},
|
|
43
|
+
sections: {
|
|
44
|
+
viewForm: {
|
|
45
|
+
'cms-pages': 'cms-pages.html',
|
|
46
|
+
},
|
|
47
|
+
editForm: {
|
|
48
|
+
'cms-pages': 'cms-pages.html',
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
-- Default CMS blocks:
|
|
3
3
|
|
|
4
|
-
REPLACE INTO `cms_blocks` (`name`, `title`, `content`, `
|
|
4
|
+
REPLACE INTO `cms_blocks` (`name`, `title`, `content`, `enabled`) VALUES
|
|
5
5
|
('header-main', 'Main Header', '{{>header}}', 1),
|
|
6
6
|
('sidebar-left', 'Left Sidebar', '<aside class="sidebar-left">{{>sidebar}}</aside>', 1),
|
|
7
7
|
('sidebar-right', 'Right Sidebar', '<aside class="sidebar-right">{{>sidebar}}</aside>', 1),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
-- Default entity access rules:
|
|
3
3
|
|
|
4
|
-
REPLACE INTO `
|
|
4
|
+
REPLACE INTO `entities_access` (`entity_name`, `is_public`, `allowed_operations`) VALUES
|
|
5
5
|
('cms_pages', TRUE, '["read"]'),
|
|
6
6
|
('routes', FALSE, '[]'),
|
|
7
7
|
('users', FALSE, '[]'),
|
|
8
8
|
('cms_blocks', FALSE, '[]'),
|
|
9
|
-
('
|
|
9
|
+
('entities_access', FALSE, '[]');
|
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
|
|
2
1
|
-- Default homepage:
|
|
3
2
|
|
|
4
|
-
-- Create a default
|
|
5
|
-
REPLACE INTO `
|
|
6
|
-
(1, 'Home', '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>', NULL, NOW());
|
|
3
|
+
-- Create a default route first
|
|
4
|
+
REPLACE INTO `routes` (`id`, `path`, `router`, `cache_ttl_seconds`, `enabled`, `created_at`) VALUES (1, '/home', 'cmsPages', 3600, 1, NOW());
|
|
7
5
|
|
|
8
|
-
-- Create a default
|
|
9
|
-
REPLACE INTO `
|
|
10
|
-
|
|
6
|
+
-- Create a default homepage with route_id reference
|
|
7
|
+
REPLACE INTO `cms_pages` (
|
|
8
|
+
`id`, `title`, `content`, `template`, `route_id`, `meta_title`, `meta_description`,
|
|
9
|
+
`canonical_url`, `meta_robots`, `meta_og_title`, `meta_og_description`,
|
|
10
|
+
`meta_og_image`, `meta_twitter_card_type`, `status`, `locale`, `publish_date`, `expire_date`, `created_at`
|
|
11
|
+
) VALUES (
|
|
12
|
+
1,
|
|
13
|
+
'Home',
|
|
14
|
+
'<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>',
|
|
15
|
+
NULL,
|
|
16
|
+
1,
|
|
17
|
+
'Home - Reldens CMS',
|
|
18
|
+
'Welcome to Reldens CMS',
|
|
19
|
+
NULL,
|
|
20
|
+
'index,follow',
|
|
21
|
+
'Home - Reldens CMS',
|
|
22
|
+
'Welcome to the Reldens CMS homepage',
|
|
23
|
+
NULL,
|
|
24
|
+
'summary',
|
|
25
|
+
'published',
|
|
26
|
+
'en',
|
|
27
|
+
NOW(),
|
|
28
|
+
NULL,
|
|
29
|
+
NOW()
|
|
30
|
+
);
|