@reldens/cms 0.15.0 → 0.16.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.
@@ -79,7 +79,7 @@ class AdminManager
79
79
  this.adminContents.login = await this.buildLogin();
80
80
  this.adminContents.dashboard = await this.buildDashboard();
81
81
  this.adminContents.entities = await this.buildEntitiesContents();
82
- this.events.emit('reldens.buildAdminContentsAfter', {adminManager: this});
82
+ await this.events.emit('reldens.buildAdminContentsAfter', {adminManager: this});
83
83
  }
84
84
 
85
85
  async buildLayout()
@@ -195,15 +195,18 @@ class AdminManager
195
195
  value: '{{&'+property+'}}'
196
196
  };
197
197
  });
198
- let extraContentForList = sc.get(this.adminFilesContents?.sections?.list, driverResource.entityPath, '');
198
+ let sectionsContents = this.adminFilesContents?.sections;
199
+ let extraContentForList = sc.get(sectionsContents?.list, driverResource.entityPath, '');
199
200
  let extraContentForView = await this.render(
200
- sc.get(this.adminFilesContents?.sections?.view, driverResource.entityPath, ''),
201
+ sc.get(sectionsContents?.view, driverResource.entityPath, ''),
201
202
  {
202
203
  id: '{{&id}}',
203
204
  entitySerializedData: '{{&entitySerializedData}}'
204
205
  }
205
206
  );
206
- let extraContentForEdit = sc.get(this.adminFilesContents?.sections?.edit, driverResource.entityPath, '');
207
+ let extraFormContentForView = sc.get(sectionsContents?.viewForm, driverResource.entityPath, '');
208
+ let extraContentForEdit = sc.get(sectionsContents?.edit, driverResource.entityPath, '');
209
+ let extraFormContentForEdit = sc.get(sectionsContents?.editForm, driverResource.entityPath, '');
207
210
  entitiesContents[entityName] = {
208
211
  list: await this.render(
209
212
  this.adminFilesContents.list,
@@ -230,6 +233,7 @@ class AdminManager
230
233
  entityEditRoute: '{{&entityEditRoute}}',
231
234
  entityNewRoute: '{{&entityNewRoute}}',
232
235
  extraContent: extraContentForView,
236
+ extraFormContent: extraFormContentForView
233
237
  }
234
238
  ),
235
239
  edit: await this.render(
@@ -244,6 +248,7 @@ class AdminManager
244
248
  templateTitle: '{{&templateTitle}}',
245
249
  entityViewRoute: '{{&entityViewRoute}}',
246
250
  extraContent: extraContentForEdit,
251
+ extraFormContent: extraFormContentForEdit
247
252
  }
248
253
  )
249
254
  };
@@ -439,6 +444,14 @@ class AdminManager
439
444
  return res.send(routeContents);
440
445
  });
441
446
  this.adminRouter.get(entityRoute+this.editPath, this.isAuthenticated.bind(this), async (req, res) => {
447
+ let adminBeforeEntityEditEvent = {
448
+ adminManager: this,
449
+ req,
450
+ res,
451
+ driverResource,
452
+ entityPath
453
+ };
454
+ await this.events.emit('reldens.adminBeforeEntityEdit', adminBeforeEntityEditEvent);
442
455
  let routeContents = await this.generateEditRouteContent(req, driverResource, entityPath);
443
456
  if('' === routeContents){
444
457
  return res.redirect(this.rootPath+'/'+entityPath+'?result=errorEdit');
@@ -468,6 +481,13 @@ class AdminManager
468
481
  entityRoute+this.savePath,
469
482
  this.isAuthenticated.bind(this),
470
483
  async (req, res) => {
484
+ await this.events.emit('reldens.adminBeforeEntitySave', {
485
+ adminManager: this,
486
+ req,
487
+ res,
488
+ driverResource,
489
+ entityPath
490
+ });
471
491
  let redirectResult = await this.processSaveEntity(req, res, driverResource, entityPath);
472
492
  return res.redirect(redirectResult);
473
493
  }
@@ -491,6 +511,13 @@ class AdminManager
491
511
  this.isAuthenticated.bind(this),
492
512
  this.uploaderFactory.createUploader(fields, this.buckets, allowedFileTypes),
493
513
  async (req, res) => {
514
+ await this.events.emit('reldens.adminBeforeEntitySave', {
515
+ adminManager: this,
516
+ req,
517
+ res,
518
+ driverResource,
519
+ entityPath
520
+ });
494
521
  let redirectResult = await this.processSaveEntity(req, res, driverResource, entityPath);
495
522
  return res.redirect(redirectResult);
496
523
  }
@@ -554,13 +581,21 @@ class AdminManager
554
581
  Logger.error('Bad patch data.', entityDataPatch);
555
582
  return this.rootPath+'/'+entityPath+'?result=saveBadPatchData';
556
583
  }
557
- let editRoute = this.generateEntityRoute('editPath', driverResource, idProperty);
584
+ let editRoute = this.generateEntityRoute('editPath', driverResource, idProperty, null, id);
558
585
  try {
559
586
  let saveResult = await this.saveEntity(id, entityRepository, entityDataPatch);
560
587
  if(!saveResult){
561
588
  Logger.error('Save result error.', saveResult, entityDataPatch);
562
589
  return editRoute+'?result=saveEntityStorageError';
563
590
  }
591
+ await this.events.emit('reldens.adminAfterEntitySave', {
592
+ adminManager: this,
593
+ req,
594
+ res,
595
+ driverResource,
596
+ entityPath,
597
+ entityData: saveResult
598
+ });
564
599
  if(sc.isFunction(this.autoSyncDistCallback)){
565
600
  let uploadProperties = this.fetchUploadProperties(driverResource);
566
601
  if(0 < Object.keys(uploadProperties).length){
@@ -574,6 +609,10 @@ class AdminManager
574
609
  }
575
610
  }
576
611
  }
612
+ let saveAction = sc.get(req.body, 'saveAction', 'save');
613
+ if('saveAndContinue' === saveAction){
614
+ return this.generateEntityRoute('editPath', driverResource, idProperty, saveResult) +'&result=success';
615
+ }
577
616
  return this.generateEntityRoute('viewPath', driverResource, idProperty, saveResult) +'&result=success';
578
617
  } catch (error) {
579
618
  Logger.error('Save entity error.', error);
@@ -639,32 +678,12 @@ class AdminManager
639
678
  return fileNames.join(property.isArray);
640
679
  }
641
680
 
642
- formatDateTimeForEdit(dateValue, isDisabled)
643
- {
644
- if(!dateValue || '' === dateValue){
645
- return '';
646
- }
647
- let date = new Date(dateValue);
648
- if(isNaN(date.getTime())){
649
- return '';
650
- }
651
- if(isDisabled){
652
- return sc.formatDate(date);
653
- }
654
- let year = date.getFullYear();
655
- let month = String(date.getMonth() + 1).padStart(2, '0');
656
- let day = String(date.getDate()).padStart(2, '0');
657
- let hours = String(date.getHours()).padStart(2, '0');
658
- let minutes = String(date.getMinutes()).padStart(2, '0');
659
- return year+'-'+month+'-'+day+'T'+hours+':'+minutes;
660
- }
661
-
662
681
  async generateEditRouteContent(req, driverResource, entityPath)
663
682
  {
664
683
  let idProperty = this.fetchEntityIdPropertyKey(driverResource);
665
684
  let idValue = String(sc.get(req?.query, idProperty, ''));
666
685
  let templateTitle = (!idValue ? 'Create' : 'Edit')+' '+this.translations.labels[driverResource.id()];
667
- let loadedEntity = !idValue ? null :await this.loadEntityById(driverResource, idValue);
686
+ let loadedEntity = !idValue ? null : await this.loadEntityById(driverResource, idValue);
668
687
  let entityViewRoute = !idValue
669
688
  ? this.rootPath+'/'+driverResource.entityPath
670
689
  : this.generateEntityRoute('viewPath', driverResource, idProperty, loadedEntity);
@@ -675,6 +694,14 @@ class AdminManager
675
694
  templateTitle,
676
695
  entityViewRoute
677
696
  };
697
+ let editPropertiesEvent = {
698
+ adminManager: this,
699
+ req,
700
+ driverResource,
701
+ renderedEditProperties,
702
+ loadedEntity
703
+ };
704
+ await this.events.emit('reldens.adminEditPropertiesPopulation', editPropertiesEvent);
678
705
  let propertiesKeys = Object.keys(driverResource.options.properties);
679
706
  for(let propertyKey of propertiesKeys){
680
707
  let property = driverResource.options.properties[propertyKey];
@@ -719,6 +746,11 @@ class AdminManager
719
746
  if(!entityRepository){
720
747
  return false;
721
748
  }
749
+ await this.events.emit('reldens.adminBeforeEntityLoad', {
750
+ adminManager: this,
751
+ driverResource,
752
+ entityId: id
753
+ });
722
754
  return await entityRepository.loadByIdWithRelations(id);
723
755
  }
724
756
 
@@ -764,6 +796,15 @@ class AdminManager
764
796
  await this.events.emit('adminEntityExtraData', extraDataEvent);
765
797
  entitySerializedData = extraDataEvent.entitySerializedData;
766
798
  renderedViewProperties.entitySerializedData = JSON.stringify(entitySerializedData).replace(/"/g, '&quot;');
799
+ let viewPropertiesEvent = {
800
+ adminManager: this,
801
+ idProperty,
802
+ req,
803
+ driverResource,
804
+ loadedEntity,
805
+ renderedViewProperties
806
+ };
807
+ await this.events.emit('reldens.adminViewPropertiesPopulation', viewPropertiesEvent);
767
808
  return await this.renderRoute(
768
809
  await this.render(this.adminContents.entities[entityPath].view, renderedViewProperties),
769
810
  this.adminContents.sideBar
@@ -792,10 +833,7 @@ class AdminManager
792
833
  }
793
834
  }
794
835
  if('textarea' === propertyType){
795
- if('edit' === templateType){
796
- return 'textarea';
797
- }
798
- return 'text';
836
+ return 'textarea';
799
837
  }
800
838
  if(-1 !== ['reference', 'number', 'datetime'].indexOf(propertyType)){
801
839
  propertyType = 'text';
@@ -992,7 +1030,9 @@ class AdminManager
992
1030
  fieldValue = '1' === fieldValue || 'true' === fieldValue ? ' checked="checked"' : '';
993
1031
  }
994
1032
  if('datetime' === resourceProperty.type){
995
- fieldValue = this.formatDateTimeForEdit(entityPropertyValue, isFieldDisabled);
1033
+ fieldValue = !entityPropertyValue || '' === entityPropertyValue
1034
+ ? ''
1035
+ : sc.formatDate(new Date(entityPropertyValue), 'Y-m-d H:i:s');
996
1036
  }
997
1037
  if('reference' === resourceProperty.type){
998
1038
  let relationDriverResource = this.resourcesByReference[resourceProperty.reference];
@@ -1036,11 +1076,17 @@ class AdminManager
1036
1076
  return idProperty;
1037
1077
  }
1038
1078
 
1039
- generateEntityRoute(routeType, driverResource, idProperty, entity)
1079
+ generateEntityRoute(routeType, driverResource, idProperty, entity, entityId)
1040
1080
  {
1041
- let idParam = '';
1081
+ if(!idProperty || (!entity && !entityId)){
1082
+ return this.rootPath + '/' + driverResource.entityPath;
1083
+ }
1084
+ let idParam = '?' + idProperty + '=';
1042
1085
  if(entity){
1043
- idParam = '?' + idProperty + '=' + entity[idProperty];
1086
+ idParam = idParam + entity[idProperty];
1087
+ }
1088
+ if(entityId){
1089
+ idParam = idParam + entityId;
1044
1090
  }
1045
1091
  return this.rootPath + '/' + driverResource.entityPath + this[routeType] + idParam;
1046
1092
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS Pages Route Manager
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class CmsPagesRouteManager
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.dataServer = props.dataServer;
15
+ this.events = props.events;
16
+ this.setupEventListeners();
17
+ }
18
+
19
+ setupEventListeners()
20
+ {
21
+ this.events.on('reldens.adminViewPropertiesPopulation', this.populateViewFields.bind(this));
22
+ this.events.on('reldens.adminEditPropertiesPopulation', this.populateEditFields.bind(this));
23
+ this.events.on('reldens.adminAfterEntitySave', this.handleAfterEntitySave.bind(this));
24
+ }
25
+
26
+ async populateViewFields(event)
27
+ {
28
+ if('cms_pages' !== event.driverResource.id()){
29
+ return;
30
+ }
31
+ let routesRepository = this.dataServer.getEntity('routes');
32
+ if(!routesRepository){
33
+ Logger.error('Routes repository not found.');
34
+ return false;
35
+ }
36
+ let existingRoute = await routesRepository.loadOne({
37
+ router: 'cmsPages',
38
+ cms_page_id: Number(event.renderedViewProperties.id)
39
+ });
40
+ event.renderedViewProperties.routePath = sc.get(existingRoute, 'path', '');
41
+ event.renderedViewProperties.routeDomain = sc.get(existingRoute, 'domain', '');
42
+ return true;
43
+ }
44
+
45
+ async populateEditFields(event)
46
+ {
47
+ if('cms_pages' !== event.driverResource.id()){
48
+ return;
49
+ }
50
+ let routesRepository = this.dataServer.getEntity('routes');
51
+ if(!routesRepository){
52
+ Logger.error('Routes repository not found.');
53
+ return false;
54
+ }
55
+ let existingRoute = await routesRepository.loadOne({
56
+ router: 'cmsPages',
57
+ cms_page_id: Number(event.renderedEditProperties.idValue)
58
+ });
59
+ event.renderedEditProperties.routePath = sc.get(existingRoute, 'path', '');
60
+ event.renderedEditProperties.routeDomain = sc.get(existingRoute, 'domain', '');
61
+ return true;
62
+ }
63
+
64
+ async handleAfterEntitySave(event)
65
+ {
66
+ let {req, driverResource, entityData} = event;
67
+ if('cms_pages' !== driverResource.id()){
68
+ return;
69
+ }
70
+ let routesRepository = this.dataServer.getEntity('routes');
71
+ if(!routesRepository){
72
+ Logger.error('Routes repository not found.');
73
+ return;
74
+ }
75
+ let cmsPageId = Number(entityData.id);
76
+ let existingRoute = await routesRepository.loadOne({router: 'cmsPages', cms_page_id: cmsPageId});
77
+ let patchData = {
78
+ path: sc.get(req.body, 'routePath', this.generateDefaultRoutePath(entityData)),
79
+ router: sc.get(req.body, 'routeRouter', 'cmsPages'),
80
+ cms_page_id: cmsPageId,
81
+ cache_ttl_seconds: Number(sc.get(req.body, 'routeCacheTtl', 3600)),
82
+ enabled: Number(sc.get(req.body, 'routeEnabled', 1)),
83
+ domain: sc.get(req.body, 'routeDomain', null)
84
+ };
85
+ let result = existingRoute
86
+ ? await routesRepository.updateById(existingRoute.id, patchData)
87
+ : await routesRepository.create(patchData);
88
+ if (!result){
89
+ Logger.error('Route could not be saved.', patchData, existingRoute);
90
+ }
91
+ return result;
92
+ }
93
+
94
+ generateDefaultRoutePath(pageData)
95
+ {
96
+ return '/' + sc.get(pageData, 'title', 'page').toLowerCase()
97
+ .replace(/[^a-z0-9\s-]/g, '')
98
+ .replace(/\s+/g, '-')
99
+ .replace(/-+/g, '-')
100
+ .replace(/^-|-$/g, '');
101
+ }
102
+
103
+ }
104
+
105
+ module.exports.CmsPagesRouteManager = CmsPagesRouteManager;
package/lib/frontend.js CHANGED
@@ -55,7 +55,8 @@ class Frontend
55
55
  }
56
56
  this.templateEngine = new TemplateEngine({
57
57
  renderEngine: this.renderEngine,
58
- dataServer: this.dataServer
58
+ dataServer: this.dataServer,
59
+ getPartials: this.getPartialsForDomain.bind(this)
59
60
  });
60
61
  await this.loadPartials();
61
62
  await this.setupDomainTemplates();
@@ -69,9 +70,9 @@ class Frontend
69
70
 
70
71
  async loadEntityAccessRules()
71
72
  {
72
- let accessEntity = this.dataServer.getEntity('cmsEntityAccess');
73
+ let accessEntity = this.dataServer.getEntity('entitiesAccess');
73
74
  if(!accessEntity){
74
- Logger.warning('CMS Entity Access not found.');
75
+ Logger.warning('Entities Access not found.');
75
76
  return;
76
77
  }
77
78
  let accessRules = await accessEntity.loadAll();
@@ -250,7 +251,9 @@ class Frontend
250
251
  try {
251
252
  let path = req.path;
252
253
  let domain = this.getDomainFromRequest(req);
253
- let route = await this.findRouteByPath(path);
254
+ console.log('---------------> ', {path, domain});
255
+ this.templateEngine.setCurrentDomain(domain);
256
+ let route = await this.findRouteByPath(path, domain);
254
257
  if(route){
255
258
  return await this.renderRoute(route, domain, res);
256
259
  }
@@ -269,19 +272,25 @@ class Frontend
269
272
  }
270
273
  }
271
274
 
272
- async findRouteByPath(path)
275
+ async findRouteByPath(path, domain)
273
276
  {
274
277
  let routesEntity = this.dataServer.getEntity('routes');
275
278
  if(!routesEntity){
276
279
  Logger.error('Routes entity not found in dataServer');
277
280
  return false;
278
281
  }
279
- let route = await routesEntity.loadOneBy('path', path);
280
- if(route){
281
- return route;
282
+ let domainFilter = domain || null;
283
+ let routeFilters = {path, enabled: 1};
284
+ let routes = await routesEntity.load(routeFilters);
285
+ let matchingRoute = false;
286
+ for(let route of routes){
287
+ if(!route.domain || route.domain === domainFilter){
288
+ matchingRoute = route;
289
+ break;
290
+ }
282
291
  }
283
- if('/' === path){
284
- return await routesEntity.loadOneBy('path', '/home');
292
+ if(matchingRoute){
293
+ return matchingRoute;
285
294
  }
286
295
  return false;
287
296
  }
@@ -331,20 +340,20 @@ class Frontend
331
340
 
332
341
  async renderRoute(route, domain, res)
333
342
  {
334
- if(!route.router || !route.content_id){
343
+ if(!route.router || !route.cms_page_id){
335
344
  return await this.renderNotFound(domain, res);
336
345
  }
337
346
  let entity = this.dataServer.getEntity(route.router);
338
347
  if(!entity){
339
348
  return await this.renderNotFound(domain, res);
340
349
  }
341
- let content = await entity.loadById(route.content_id);
350
+ let content = await entity.loadById(route.cms_page_id);
342
351
  if(!content){
343
352
  return await this.renderNotFound(domain, res);
344
353
  }
345
354
  return await this.renderWithLayout(
346
355
  content,
347
- Object.assign({}, route, content, {currentYear: new Date().getFullYear()}),
356
+ Object.assign({}, route, content),
348
357
  sc.get(content, 'layout', 'default'),
349
358
  domain,
350
359
  res
@@ -355,7 +364,7 @@ class Frontend
355
364
  {
356
365
  return await this.renderWithLayout(
357
366
  entityResult.entity,
358
- Object.assign({}, entityResult.entity, {currentYear: new Date().getFullYear()}),
367
+ Object.assign({}, entityResult.entity),
359
368
  sc.get(entityResult.entity, 'layout', 'default'),
360
369
  domain,
361
370
  res
@@ -364,12 +373,11 @@ class Frontend
364
373
 
365
374
  async renderTemplate(templatePath, domain, res)
366
375
  {
367
- let data = { currentYear: new Date().getFullYear() };
368
- let content = await this.renderContentWithTemplate(templatePath, data, domain);
376
+ let content = await this.renderContentWithTemplate(templatePath, {}, domain);
369
377
  if(!content){
370
378
  return res.status(500).send('Template error: '+templatePath);
371
379
  }
372
- return await this.renderWithLayout({content}, data, 'default', domain, res);
380
+ return await this.renderWithLayout({content}, {}, 'default', domain, res);
373
381
  }
374
382
 
375
383
  async renderContentWithTemplate(templatePath, data, domain)
@@ -389,51 +397,46 @@ class Frontend
389
397
  if(layoutPath){
390
398
  let layoutTemplate = FileHandler.readFile(layoutPath);
391
399
  if(layoutTemplate){
400
+ let processedContent = sc.get(content, 'content', '');
401
+ if(processedContent){
402
+ processedContent = await this.templateEngine.render(
403
+ processedContent,
404
+ data,
405
+ this.getPartialsForDomain(domain)
406
+ );
407
+ }
392
408
  layoutContent = await this.templateEngine.render(
393
409
  layoutTemplate,
394
- Object.assign({}, data, {
395
- content: sc.get(content, 'content', '')
396
- }),
410
+ Object.assign({}, data, {content: processedContent}),
397
411
  this.getPartialsForDomain(domain)
398
412
  );
399
413
  }
400
414
  }
401
415
  if('' === layoutContent){
402
- layoutContent = sc.get(content, 'content', '');
403
- }
404
- let pagePath = this.findTemplatePath('page', domain);
405
- if(!pagePath){
406
- return res.send(layoutContent);
407
- }
408
- let pageTemplate = FileHandler.readFile(pagePath);
409
- if(!pageTemplate){
410
- return res.send(layoutContent);
411
- }
412
- return res.send(
413
- await this.templateEngine.render(
414
- pageTemplate,
415
- Object.assign({}, data, {
416
- content: layoutContent,
417
- siteHandle: this.resolveDomainToSiteKey(domain)
418
- }),
419
- this.getPartialsForDomain(domain)
420
- )
421
- );
416
+ let processedContent = sc.get(content, 'content', '');
417
+ if(processedContent){
418
+ layoutContent = await this.templateEngine.render(
419
+ processedContent,
420
+ data,
421
+ this.getPartialsForDomain(domain)
422
+ );
423
+ }
424
+ }
425
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
426
+ return res.send(layoutContent);
422
427
  }
423
428
 
424
429
  async renderNotFound(domain, res)
425
430
  {
426
- let templatePath = this.findTemplatePath('404', domain);
427
- if(!templatePath){
428
- return res.status(404).send('Page not found');
429
- }
430
- let data = {title: '404 - Page Not Found', currentYear: new Date().getFullYear()};
431
- let content = await this.renderContentWithTemplate(templatePath, data, domain);
432
- if(!content){
433
- return res.status(404).send('Page not found');
431
+ let notFoundPath = this.findTemplatePath('404', domain);
432
+ if(notFoundPath){
433
+ let content = await this.renderContentWithTemplate(notFoundPath, {}, domain);
434
+ if(content){
435
+ res.status(404);
436
+ return await this.renderWithLayout({content}, {}, 'default', domain, res);
437
+ }
434
438
  }
435
- res.status(404);
436
- return await this.renderWithLayout({content}, data, 'default', domain, res);
439
+ return res.status(404).send('Page not found');
437
440
  }
438
441
 
439
442
  }