@reldens/cms 0.18.0 → 0.20.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 (36) hide show
  1. package/README.md +260 -20
  2. package/admin/reldens-admin-client.css +127 -80
  3. package/admin/reldens-admin-client.js +24 -0
  4. package/admin/templates/clear-all-cache-button.html +18 -0
  5. package/lib/admin-manager/contents-builder.js +7 -6
  6. package/lib/admin-manager/router-contents.js +58 -16
  7. package/lib/admin-manager-validator.js +2 -1
  8. package/lib/admin-manager.js +4 -2
  9. package/lib/admin-translations.js +9 -1
  10. package/lib/cache/add-cache-button-subscriber.js +53 -5
  11. package/lib/cache/cache-manager.js +47 -8
  12. package/lib/cache/cache-routes-handler.js +23 -0
  13. package/lib/cms-pages-route-manager.js +16 -4
  14. package/lib/frontend.js +310 -119
  15. package/lib/manager.js +43 -3
  16. package/lib/pagination-handler.js +243 -0
  17. package/lib/search-renderer.js +116 -0
  18. package/lib/search.js +344 -0
  19. package/lib/template-engine/asset-transformer.js +41 -0
  20. package/lib/template-engine/collections-single-transformer.js +70 -0
  21. package/lib/template-engine/collections-transformer-base.js +84 -0
  22. package/lib/template-engine/collections-transformer.js +374 -0
  23. package/lib/template-engine/date-transformer.js +53 -0
  24. package/lib/template-engine/entities-transformer.js +67 -0
  25. package/lib/template-engine/partials-transformer.js +175 -0
  26. package/lib/template-engine/system-variables-provider.js +105 -0
  27. package/lib/template-engine/translate-transformer.js +98 -0
  28. package/lib/template-engine/translation-service.js +104 -0
  29. package/lib/template-engine/url-transformer.js +41 -0
  30. package/lib/template-engine.js +133 -438
  31. package/lib/templates-list.js +1 -0
  32. package/migrations/install.sql +18 -18
  33. package/package.json +4 -4
  34. package/templates/page.html +19 -2
  35. package/templates/partials/entriesListView.html +14 -0
  36. package/templates/partials/pagedCollection.html +33 -0
@@ -4,8 +4,17 @@
4
4
  *
5
5
  */
6
6
 
7
- const { Logger, sc } = require('@reldens/utils');
8
7
  const { JsonFieldsParser } = require('./json-fields-parser');
8
+ const { EntitiesTransformer } = require('./template-engine/entities-transformer');
9
+ const { CollectionsTransformer } = require('./template-engine/collections-transformer');
10
+ const { CollectionsSingleTransformer } = require('./template-engine/collections-single-transformer');
11
+ const { PartialsTransformer } = require('./template-engine/partials-transformer');
12
+ const { UrlTransformer } = require('./template-engine/url-transformer');
13
+ const { AssetTransformer } = require('./template-engine/asset-transformer');
14
+ const { DateTransformer } = require('./template-engine/date-transformer');
15
+ const { TranslateTransformer } = require('./template-engine/translate-transformer');
16
+ const { SystemVariablesProvider } = require('./template-engine/system-variables-provider');
17
+ const { Logger, sc } = require('@reldens/utils');
9
18
 
10
19
  class TemplateEngine
11
20
  {
@@ -15,45 +24,144 @@ class TemplateEngine
15
24
  this.renderEngine = sc.get(props, 'renderEngine', false);
16
25
  this.dataServer = sc.get(props, 'dataServer', false);
17
26
  this.getPartials = sc.get(props, 'getPartials', false);
18
- this.currentDomain = '';
19
- this.jsonFieldsParser = new JsonFieldsParser({
20
- entitiesConfig: sc.get(props, 'entitiesConfig', {})
27
+ this.events = sc.get(props, 'events', false);
28
+ this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
29
+ this.projectRoot = sc.get(props, 'projectRoot', './');
30
+ this.publicPath = sc.get(props, 'publicPath', './public');
31
+ this.jsonFieldsParser = new JsonFieldsParser({entitiesConfig: sc.get(props, 'entitiesConfig', {})});
32
+ this.systemVariablesProvider = new SystemVariablesProvider({
33
+ defaultDomain: this.defaultDomain,
34
+ projectRoot: this.projectRoot,
35
+ publicPath: this.publicPath
21
36
  });
37
+ this.entitiesTransformer = new EntitiesTransformer({
38
+ dataServer: this.dataServer,
39
+ jsonFieldsParser: this.jsonFieldsParser,
40
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this)
41
+ });
42
+ this.collectionsSingleTransformer = new CollectionsSingleTransformer({
43
+ dataServer: this.dataServer,
44
+ jsonFieldsParser: this.jsonFieldsParser,
45
+ renderEngine: this.renderEngine,
46
+ getPartials: this.getPartials,
47
+ findAllPartialTags: this.findAllPartialTags.bind(this),
48
+ loadPartialTemplate: this.loadPartialTemplate.bind(this),
49
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this)
50
+ });
51
+ this.collectionsTransformer = new CollectionsTransformer({
52
+ dataServer: this.dataServer,
53
+ jsonFieldsParser: this.jsonFieldsParser,
54
+ renderEngine: this.renderEngine,
55
+ getPartials: this.getPartials,
56
+ findAllPartialTags: this.findAllPartialTags.bind(this),
57
+ loadPartialTemplate: this.loadPartialTemplate.bind(this),
58
+ processAllTemplateFunctions: this.processAllTemplateFunctions.bind(this)
59
+ });
60
+ this.partialsTransformer = new PartialsTransformer({
61
+ renderEngine: this.renderEngine,
62
+ getPartials: this.getPartials
63
+ });
64
+ this.urlTransformer = new UrlTransformer();
65
+ this.assetTransformer = new AssetTransformer();
66
+ this.dateTransformer = new DateTransformer({
67
+ defaultFormat: sc.get(props, 'defaultDateFormat', 'Y-m-d H:i:s')
68
+ });
69
+ this.translateTransformer = new TranslateTransformer({
70
+ projectRoot: this.projectRoot,
71
+ defaultLocale: sc.get(props, 'defaultLocale', 'en'),
72
+ fallbackLocale: sc.get(props, 'fallbackLocale', 'en')
73
+ });
74
+ this.transformers = [
75
+ this.entitiesTransformer,
76
+ this.collectionsSingleTransformer,
77
+ this.collectionsTransformer,
78
+ this.partialsTransformer,
79
+ this.urlTransformer,
80
+ this.assetTransformer,
81
+ this.dateTransformer,
82
+ this.translateTransformer
83
+ ];
22
84
  }
23
85
 
24
- setCurrentDomain(domain)
25
- {
26
- this.currentDomain = domain;
27
- }
28
-
29
- async processAllTemplateFunctions(template)
86
+ async processAllTemplateFunctions(template, domain, req, systemVariables)
30
87
  {
31
- return await this.processCustomPartials(
32
- await this.processLoopCollections(
33
- await this.processSingleFieldCollections(
34
- await this.processEntityFunctions(template)
35
- )
36
- )
37
- );
88
+ let processedTemplate = template;
89
+ for(let transformer of this.transformers){
90
+ if(sc.isFunction(transformer.transform)){
91
+ processedTemplate = await transformer.transform(processedTemplate, domain, req, systemVariables);
92
+ }
93
+ }
94
+ return processedTemplate;
38
95
  }
39
96
 
40
- async render(template, data, partials)
97
+ async render(template, data, partials, domain, req, route, currentEntityData)
41
98
  {
42
99
  if(!this.renderEngine){
43
- Logger.error('Render engine not provided');
100
+ Logger.critical('Render engine not provided');
44
101
  return '';
45
102
  }
46
103
  if(!sc.isFunction(this.renderEngine.render)){
47
- Logger.error('Render engine does not contain a render method');
104
+ Logger.critical('Render engine does not contain a render method');
48
105
  return '';
49
106
  }
50
- return this.renderEngine.render(
51
- this.unescapeHtml(await this.processAllTemplateFunctions(template)),
107
+ if(!this.events){
108
+ Logger.critical('Events manager not provided');
109
+ return '';
110
+ }
111
+ let systemVariables = this.systemVariablesProvider.buildSystemVariables(req, route, domain);
112
+ let renderContext = {
113
+ template,
52
114
  data,
115
+ partials,
116
+ domain,
117
+ req,
118
+ route,
119
+ currentEntityData
120
+ };
121
+ let eventData = {
122
+ variables: systemVariables,
123
+ renderContext
124
+ };
125
+ await this.events.emit('reldens.afterVariablesCreated', eventData);
126
+ let enhancedData = this.buildEnhancedRenderData(data, eventData.variables, currentEntityData);
127
+ let beforeProcessData = {
128
+ content: template,
129
+ variables: enhancedData,
130
+ renderContext
131
+ };
132
+ await this.events.emit('reldens.beforeContentProcess', beforeProcessData);
133
+ let processedTemplate = await this.processAllTemplateFunctions(
134
+ beforeProcessData.content,
135
+ domain,
136
+ req,
137
+ eventData.variables
138
+ );
139
+ let afterProcessData = {
140
+ processedContent: processedTemplate,
141
+ variables: enhancedData,
142
+ renderContext
143
+ };
144
+ await this.events.emit('reldens.afterContentProcess', afterProcessData);
145
+ return this.renderEngine.render(
146
+ this.unescapeHtml(afterProcessData.processedContent),
147
+ enhancedData,
53
148
  partials
54
149
  );
55
150
  }
56
151
 
152
+ buildEnhancedRenderData(originalData, systemVariables, currentEntityData)
153
+ {
154
+ let enhancedData = Object.assign({}, originalData);
155
+ enhancedData.currentRequest = systemVariables.currentRequest;
156
+ enhancedData.currentRoute = systemVariables.currentRoute;
157
+ enhancedData.currentDomain = systemVariables.currentDomain;
158
+ enhancedData.systemInfo = systemVariables.systemInfo;
159
+ if(currentEntityData){
160
+ enhancedData.currentEntity = currentEntityData;
161
+ }
162
+ return enhancedData;
163
+ }
164
+
57
165
  unescapeHtml(text)
58
166
  {
59
167
  return text
@@ -67,427 +175,14 @@ class TemplateEngine
67
175
  .replace(/&/g, '&');
68
176
  }
69
177
 
70
- getEntityRegex()
71
- {
72
- return /<entity\s+name="([^"]+)"(?:\s+field="([^"]+)"\s+value="([^"]+)"|\s+id="([^"]+)")?\s*\/?>/g;
73
- }
74
-
75
- getSingleFieldCollectionRegex()
76
- {
77
- return /<collection\s+([^>]+)\/>/g;
78
- }
79
-
80
- getLoopCollectionStartRegex()
81
- {
82
- return /<collection\s+([^>]+)>/g;
83
- }
84
-
85
- getLoopCollectionEndRegex()
86
- {
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] : '';
95
- }
96
-
97
- async processEntityFunctions(template)
98
- {
99
- let processedTemplate = template;
100
- for(let match of template.matchAll(this.getEntityRegex())){
101
- let tableName = match[1];
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
- );
114
- }
115
- return processedTemplate;
116
- }
117
-
118
- async processSingleFieldCollections(template)
119
- {
120
- let processedTemplate = template;
121
- for(let match of template.matchAll(this.getSingleFieldCollectionRegex())){
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');
131
- processedTemplate = processedTemplate.replace(
132
- match[0],
133
- this.extractFieldValues(
134
- await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString),
135
- fieldName
136
- )
137
- );
138
- }
139
- return processedTemplate;
140
- }
141
-
142
- async processLoopCollections(template)
143
- {
144
- let processedTemplate = template;
145
- let matches = [...template.matchAll(this.getLoopCollectionStartRegex())];
146
- for(let i = matches.length - 1; i >= 0; i--){
147
- let startMatch = matches[i];
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){
165
- processedTemplate = loopResult;
166
- }
167
- }
168
- return processedTemplate;
169
- }
170
-
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
178
  findAllPartialTags(template)
194
179
  {
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;
180
+ return this.partialsTransformer.findAllPartialTags(template);
222
181
  }
223
182
 
224
- findPartialTagEnd(template, tagStart)
183
+ loadPartialTemplate(partialName, domain)
225
184
  {
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)
323
- {
324
- let startPos = startMatch.index;
325
- let startEnd = startPos + startMatch[0].length;
326
- let endMatch = this.getLoopCollectionEndRegex(tableName).exec(template.substring(startEnd));
327
- if(!endMatch){
328
- Logger.warning('No matching end tag found for collection: '+tableName);
329
- return false;
330
- }
331
- let endPos = startEnd + endMatch.index;
332
- let loopContent = template.substring(startEnd, endPos);
333
- let collectionData = await this.fetchCollectionForTemplate(
334
- tableName,
335
- filtersJson,
336
- queryOptionsJson,
337
- relationsString
338
- );
339
- let renderedContent = await this.renderCollectionLoop(loopContent, collectionData);
340
- return template.substring(0, startPos) + renderedContent + template.substring(endPos + endMatch[0].length);
341
- }
342
-
343
- async renderCollectionLoop(loopContent, collectionData)
344
- {
345
- let renderedContent = '';
346
- for(let row of collectionData){
347
- renderedContent += await this.processPartialsInLoop(loopContent, row);
348
- }
349
- return renderedContent;
350
- }
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
-
380
- extractFieldValues(collectionData, fieldName)
381
- {
382
- let fieldValues = '';
383
- for(let row of collectionData){
384
- fieldValues += sc.get(row, fieldName, '');
385
- }
386
- return fieldValues;
387
- }
388
-
389
- async fetchEntityForTemplate(tableName, identifier, identifierField)
390
- {
391
- let entity = this.dataServer.getEntity(tableName);
392
- if(!entity){
393
- Logger.warning('Entity not found in dataServer: '+tableName);
394
- return false;
395
- }
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);
420
- }
421
-
422
- async fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString)
423
- {
424
- let entity = this.dataServer.getEntity(tableName);
425
- if(!entity){
426
- Logger.warning('Entity not found in dataServer: '+tableName);
427
- return [];
428
- }
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;
490
- }
185
+ return this.partialsTransformer.loadPartialTemplate(partialName, domain);
491
186
  }
492
187
 
493
188
  }
@@ -19,6 +19,7 @@ module.exports.TemplatesList = {
19
19
  paginationLink: 'pagination-link.html',
20
20
  defaultCopyRight: 'default-copyright.html',
21
21
  cacheCleanButton: 'cache-clean-button.html',
22
+ clearAllCacheButton: 'clear-all-cache-button.html',
22
23
  fields: {
23
24
  view: {
24
25
  text: 'text.html',
@@ -29,34 +29,34 @@ CREATE TABLE IF NOT EXISTS `cms_categories` (
29
29
 
30
30
  CREATE TABLE IF NOT EXISTS `cms_pages` (
31
31
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
32
- `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
33
- `content` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
34
- `json_data` JSON NULL,
35
- `template` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
36
- `layout` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'default',
32
+ `title` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_ci',
33
+ `content` LONGTEXT NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
34
+ `json_data` JSON NULL DEFAULT NULL,
35
+ `template` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
36
+ `layout` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
37
37
  `category_id` INT UNSIGNED NULL DEFAULT NULL,
38
38
  `route_id` INT UNSIGNED NULL DEFAULT NULL,
39
39
  `enabled` TINYINT UNSIGNED NOT NULL DEFAULT '1',
40
- `meta_title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
41
- `meta_description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
42
- `meta_robots` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'index,follow',
40
+ `meta_title` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_ci',
41
+ `meta_description` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
42
+ `meta_robots` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
43
43
  `meta_theme_color` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
44
- `meta_og_title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
45
- `meta_og_description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
46
- `meta_og_image` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
47
- `meta_twitter_card_type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'summary',
48
- `canonical_url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
49
- `status` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'published',
50
- `locale` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'en',
51
- `publish_date` TIMESTAMP NULL,
52
- `expire_date` TIMESTAMP NULL,
44
+ `meta_og_title` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
45
+ `meta_og_description` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
46
+ `meta_og_image` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
47
+ `meta_twitter_card_type` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
48
+ `canonical_url` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
49
+ `status` VARCHAR(20) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
50
+ `locale` VARCHAR(10) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
51
+ `publish_date` TIMESTAMP NULL DEFAULT (NOW()),
52
+ `expire_date` TIMESTAMP NULL DEFAULT NULL,
53
53
  `created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
54
54
  `updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
55
55
  PRIMARY KEY (`id`) USING BTREE,
56
56
  INDEX `FK_cms_pages_cms_categories` (`category_id`) USING BTREE,
57
57
  INDEX `FK_cms_pages_routes` (`route_id`) USING BTREE,
58
58
  CONSTRAINT `FK_cms_pages_cms_categories` FOREIGN KEY (`category_id`) REFERENCES `cms_categories` (`id`) ON UPDATE CASCADE ON DELETE NO ACTION,
59
- CONSTRAINT `FK_cms_pages_routes` FOREIGN KEY (`route_id`) REFERENCES `routes` (`id`) ON UPDATE CASCADE ON DELETE SET NULL
59
+ CONSTRAINT `FK_cms_pages_routes` FOREIGN KEY (`route_id`) REFERENCES `routes` (`id`) ON UPDATE CASCADE ON DELETE NO ACTION
60
60
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
61
61
 
62
62
  CREATE TABLE IF NOT EXISTS `cms_blocks` (