@reldens/cms 0.16.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.
@@ -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
  {
@@ -15,6 +16,9 @@ class TemplateEngine
15
16
  this.dataServer = sc.get(props, 'dataServer', false);
16
17
  this.getPartials = sc.get(props, 'getPartials', false);
17
18
  this.currentDomain = '';
19
+ this.jsonFieldsParser = new JsonFieldsParser({
20
+ entitiesConfig: sc.get(props, 'entitiesConfig', {})
21
+ });
18
22
  }
19
23
 
20
24
  setCurrentDomain(domain)
@@ -70,12 +74,12 @@ class TemplateEngine
70
74
 
71
75
  getSingleFieldCollectionRegex()
72
76
  {
73
- return /<collection\s+name=(['"])([^'"]+)\1(?:\s+filters=(['"])([^'"]*)\3)?\s+field=(['"])([^'"]+)\5(?:\s+data=(['"])([^'"]*)\7)?\s*\/>/g;
77
+ return /<collection\s+([^>]+)\/>/g;
74
78
  }
75
79
 
76
80
  getLoopCollectionStartRegex()
77
81
  {
78
- return /<collection\s+name=(['"])([^'"]+)\1(?:\s+filters=(['"])([^'"]*)\3)?(?:\s+data=(['"])([^'"]*)\5)?\s*>/g;
82
+ return /<collection\s+([^>]+)>/g;
79
83
  }
80
84
 
81
85
  getLoopCollectionEndRegex()
@@ -83,6 +87,13 @@ class TemplateEngine
83
87
  return new RegExp('<\\/collection>');
84
88
  }
85
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
+
86
97
  async processEntityFunctions(template)
87
98
  {
88
99
  let processedTemplate = template;
@@ -108,14 +119,19 @@ class TemplateEngine
108
119
  {
109
120
  let processedTemplate = template;
110
121
  for(let match of template.matchAll(this.getSingleFieldCollectionRegex())){
111
- let tableName = match[2];
112
- let filtersJson = sc.get(match, '4', '{}');
113
- let fieldName = match[6];
114
- let queryOptionsJson = sc.get(match, '8', '{}');
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');
115
131
  processedTemplate = processedTemplate.replace(
116
132
  match[0],
117
133
  this.extractFieldValues(
118
- await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson),
134
+ await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson, relationsString),
119
135
  fieldName
120
136
  )
121
137
  );
@@ -129,12 +145,21 @@ class TemplateEngine
129
145
  let matches = [...template.matchAll(this.getLoopCollectionStartRegex())];
130
146
  for(let i = matches.length - 1; i >= 0; i--){
131
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');
132
156
  let loopResult = await this.processLoopCollection(
133
157
  processedTemplate,
134
158
  startMatch,
135
- startMatch[2],
136
- sc.get(startMatch, '4', '{}'),
137
- sc.get(startMatch, '6', '{}')
159
+ tableName,
160
+ filtersJson,
161
+ relationsString,
162
+ queryOptionsJson
138
163
  );
139
164
  if(loopResult){
140
165
  processedTemplate = loopResult;
@@ -152,7 +177,7 @@ class TemplateEngine
152
177
  let partialContent = this.loadPartialTemplate(tag.name);
153
178
  if(!partialContent){
154
179
  Logger.warning('Partial template not found: ' + tag.name);
155
- processedTemplate = processedTemplate.substring(0, tag.start) + '' + processedTemplate.substring(tag.end);
180
+ processedTemplate = processedTemplate.substring(0, tag.start)+''+processedTemplate.substring(tag.end);
156
181
  continue;
157
182
  }
158
183
  let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
@@ -168,18 +193,18 @@ class TemplateEngine
168
193
  findAllPartialTags(template)
169
194
  {
170
195
  let partialTags = [];
171
- let searchPos = 0;
172
- let partialTagName = '<partial';
173
- for(let tagStart = template.indexOf(partialTagName, searchPos); -1 !== tagStart; tagStart = template.indexOf(partialTagName, searchPos)){
196
+ let pos = 0;
197
+ let partial = '<partial';
198
+ for(let tagStart = template.indexOf(partial, pos); -1 !== tagStart; tagStart=template.indexOf(partial, pos)){
174
199
  let tagEnd = this.findPartialTagEnd(template, tagStart);
175
200
  if(-1 === tagEnd){
176
- searchPos = tagStart + partialTagName.length;
201
+ pos = tagStart + partial.length;
177
202
  continue;
178
203
  }
179
204
  let fullTag = template.substring(tagStart, tagEnd);
180
205
  let nameMatch = fullTag.match(/name=["']([^"']+)["']/);
181
206
  if(!nameMatch){
182
- searchPos = tagStart + partialTagName.length;
207
+ pos = tagStart + partial.length;
183
208
  continue;
184
209
  }
185
210
  let partialName = nameMatch[1];
@@ -191,7 +216,7 @@ class TemplateEngine
191
216
  attributes: attributes,
192
217
  fullTag: fullTag
193
218
  });
194
- searchPos = tagEnd;
219
+ pos = tagEnd;
195
220
  }
196
221
  return partialTags;
197
222
  }
@@ -294,7 +319,7 @@ class TemplateEngine
294
319
  return false;
295
320
  }
296
321
 
297
- async processLoopCollection(template, startMatch, tableName, filtersJson, queryOptionsJson)
322
+ async processLoopCollection(template, startMatch, tableName, filtersJson, relationsString, queryOptionsJson)
298
323
  {
299
324
  let startPos = startMatch.index;
300
325
  let startEnd = startPos + startMatch[0].length;
@@ -305,7 +330,12 @@ class TemplateEngine
305
330
  }
306
331
  let endPos = startEnd + endMatch.index;
307
332
  let loopContent = template.substring(startEnd, endPos);
308
- let collectionData = await this.fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson);
333
+ let collectionData = await this.fetchCollectionForTemplate(
334
+ tableName,
335
+ filtersJson,
336
+ queryOptionsJson,
337
+ relationsString
338
+ );
309
339
  let renderedContent = await this.renderCollectionLoop(loopContent, collectionData);
310
340
  return template.substring(0, startPos) + renderedContent + template.substring(endPos + endMatch[0].length);
311
341
  }
@@ -328,12 +358,13 @@ class TemplateEngine
328
358
  let partialContent = this.loadPartialTemplate(tag.name);
329
359
  if(!partialContent){
330
360
  Logger.warning('Partial template not found: ' + tag.name);
331
- processedContent = processedContent.substring(0, tag.start) + '' + processedContent.substring(tag.end);
361
+ processedContent = processedContent.substring(0, tag.start)+''+processedContent.substring(tag.end);
332
362
  continue;
333
363
  }
334
364
  if(sc.hasOwn(tag.attributes, 'row')){
335
- let renderedPartial = this.renderEngine.render(partialContent, {row: rowData}, this.getPartials(this.currentDomain));
336
- processedContent = processedContent.substring(0, tag.start) + renderedPartial + processedContent.substring(tag.end);
365
+ processedContent = processedContent.substring(0, tag.start)
366
+ +this.renderEngine.render(partialContent, {row: rowData}, this.getPartials(this.currentDomain))
367
+ +processedContent.substring(tag.end);
337
368
  continue;
338
369
  }
339
370
  let wrapperTemplate = '{{#vars}}{{> ' + tag.name + '}}{{/vars}}';
@@ -362,7 +393,14 @@ class TemplateEngine
362
393
  Logger.warning('Entity not found in dataServer: '+tableName);
363
394
  return false;
364
395
  }
365
- return await entity.loadOneBy(identifierField, identifier);
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
+ );
366
404
  }
367
405
 
368
406
  convertJsObjectToJson(jsObjectString)
@@ -373,7 +411,15 @@ class TemplateEngine
373
411
  return jsObjectString.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":');
374
412
  }
375
413
 
376
- async fetchCollectionForTemplate(tableName, filtersJson, queryOptionsJson)
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)
377
423
  {
378
424
  let entity = this.dataServer.getEntity(tableName);
379
425
  if(!entity){
@@ -399,9 +445,15 @@ class TemplateEngine
399
445
  }
400
446
  }
401
447
  this.applyQueryOptions(entity, queryOptions);
402
- let result = filters ? await entity.load(filters) : await entity.loadAll();
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();
403
452
  this.restoreEntityState(entity, originalState);
404
- return result;
453
+ return this.jsonFieldsParser.parseJsonFields(
454
+ result,
455
+ this.jsonFieldsParser.getJsonFieldsForEntity(tableName)
456
+ );
405
457
  }
406
458
 
407
459
  preserveEntityState(entity)
@@ -440,4 +492,4 @@ class TemplateEngine
440
492
 
441
493
  }
442
494
 
443
- module.exports.TemplateEngine = TemplateEngine;
495
+ module.exports.TemplateEngine = TemplateEngine;
@@ -18,6 +18,7 @@ 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',
@@ -1,9 +1,11 @@
1
-
2
1
  -- Default homepage:
3
2
 
4
- -- Create a default homepage route if not exists
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());
5
+
6
+ -- Create a default homepage with route_id reference
5
7
  REPLACE INTO `cms_pages` (
6
- `id`, `title`, `content`, `template`, `meta_title`, `meta_description`,
8
+ `id`, `title`, `content`, `template`, `route_id`, `meta_title`, `meta_description`,
7
9
  `canonical_url`, `meta_robots`, `meta_og_title`, `meta_og_description`,
8
10
  `meta_og_image`, `meta_twitter_card_type`, `status`, `locale`, `publish_date`, `expire_date`, `created_at`
9
11
  ) VALUES (
@@ -11,6 +13,7 @@ REPLACE INTO `cms_pages` (
11
13
  'Home',
12
14
  '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>',
13
15
  NULL,
16
+ 1,
14
17
  'Home - Reldens CMS',
15
18
  'Welcome to Reldens CMS',
16
19
  NULL,
@@ -25,6 +28,3 @@ REPLACE INTO `cms_pages` (
25
28
  NULL,
26
29
  NOW()
27
30
  );
28
-
29
- -- Create a default route to the homepage
30
- REPLACE INTO `routes` (`id`, `path`, `router`, `cms_page_id`, `cache_ttl_seconds`, `enabled`, `created_at`) VALUES (1, '/home', 'cmsPages', 1, 3600, 1, NOW());
@@ -5,7 +5,6 @@ CREATE TABLE IF NOT EXISTS `routes` (
5
5
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
6
6
  `path` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
7
7
  `router` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
8
- `cms_page_id` INT UNSIGNED NOT NULL,
9
8
  `cache_ttl_seconds` INT UNSIGNED NULL DEFAULT 3600,
10
9
  `enabled` TINYINT UNSIGNED NOT NULL DEFAULT '1',
11
10
  `domain` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
@@ -32,11 +31,11 @@ CREATE TABLE IF NOT EXISTS `cms_pages` (
32
31
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
33
32
  `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
34
33
  `content` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
35
- `markdown` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
36
34
  `json_data` JSON NULL,
37
35
  `template` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
38
36
  `layout` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'default',
39
37
  `category_id` INT UNSIGNED NULL DEFAULT NULL,
38
+ `route_id` INT UNSIGNED NULL DEFAULT NULL,
40
39
  `enabled` TINYINT UNSIGNED NOT NULL DEFAULT '1',
41
40
  `meta_title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
42
41
  `meta_description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
@@ -55,7 +54,9 @@ CREATE TABLE IF NOT EXISTS `cms_pages` (
55
54
  `updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
56
55
  PRIMARY KEY (`id`) USING BTREE,
57
56
  INDEX `FK_cms_pages_cms_categories` (`category_id`) USING BTREE,
58
- CONSTRAINT `FK_cms_pages_cms_categories` FOREIGN KEY (`category_id`) REFERENCES `cms_categories` (`id`) ON UPDATE CASCADE ON DELETE NO ACTION
57
+ INDEX `FK_cms_pages_routes` (`route_id`) USING BTREE,
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
60
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
60
61
 
61
62
  CREATE TABLE IF NOT EXISTS `cms_blocks` (
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/cms",
3
3
  "scope": "@reldens",
4
- "version": "0.16.0",
4
+ "version": "0.18.0",
5
5
  "description": "Reldens - CMS",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@reldens/server-utils": "^0.19.0",
37
- "@reldens/storage": "^0.56.0",
37
+ "@reldens/storage": "^0.59.0",
38
38
  "@reldens/utils": "^0.50.0",
39
39
  "dotenv": "^16.5.0",
40
40
  "mustache": "^4.2.0"