@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.
Files changed (67) hide show
  1. package/README.md +648 -12
  2. package/admin/reldens-admin-client.css +77 -141
  3. package/admin/reldens-admin-client.js +108 -133
  4. package/admin/templates/clear-all-cache-button.html +7 -7
  5. package/admin/templates/edit.html +7 -0
  6. package/admin/templates/fields/view/audio.html +7 -0
  7. package/admin/templates/fields/view/audios.html +8 -0
  8. package/admin/templates/layout.html +15 -9
  9. package/admin/templates/list-content.html +4 -2
  10. package/admin/templates/list.html +24 -8
  11. package/admin/templates/view.html +21 -0
  12. package/install/index.html +4 -0
  13. package/lib/admin-manager/admin-filters-manager.js +177 -0
  14. package/lib/admin-manager/contents-builder.js +1 -0
  15. package/lib/admin-manager/default-translations.js +38 -0
  16. package/lib/admin-manager/router-contents.js +64 -52
  17. package/lib/admin-manager/router.js +19 -0
  18. package/lib/dynamic-form-renderer.js +228 -0
  19. package/lib/dynamic-form-request-handler.js +135 -0
  20. package/lib/dynamic-form.js +310 -0
  21. package/lib/frontend/content-renderer.js +178 -0
  22. package/lib/frontend/entity-access-manager.js +63 -0
  23. package/lib/frontend/request-processor.js +128 -0
  24. package/lib/frontend/response-manager.js +54 -0
  25. package/lib/frontend/template-cache.js +102 -0
  26. package/lib/frontend/template-resolver.js +111 -0
  27. package/lib/frontend.js +122 -629
  28. package/lib/installer.js +2 -1
  29. package/lib/manager.js +25 -12
  30. package/lib/search-renderer.js +15 -7
  31. package/lib/search-request-handler.js +67 -0
  32. package/lib/search.js +13 -1
  33. package/lib/template-engine/collections-single-transformer.js +11 -5
  34. package/lib/template-engine/collections-transformer.js +47 -34
  35. package/lib/template-engine/entities-transformer.js +3 -2
  36. package/lib/template-engine/forms-transformer.js +187 -0
  37. package/lib/template-engine/partials-transformer.js +5 -6
  38. package/lib/template-engine/system-variables-provider.js +4 -1
  39. package/lib/template-engine.js +28 -5
  40. package/lib/template-reloader.js +307 -0
  41. package/lib/templates-list.js +2 -0
  42. package/migrations/default-forms.sql +22 -0
  43. package/package.json +5 -5
  44. package/templates/{browserconfig.xml → assets/favicons/default/browserconfig.xml} +1 -1
  45. package/templates/assets/favicons/default/favicon.ico +0 -0
  46. package/templates/{site.webmanifest → assets/favicons/default/site.webmanifest} +3 -3
  47. package/templates/cms_forms/field_email.html +14 -0
  48. package/templates/cms_forms/field_number.html +17 -0
  49. package/templates/cms_forms/field_select.html +15 -0
  50. package/templates/cms_forms/field_text.html +16 -0
  51. package/templates/cms_forms/field_textarea.html +13 -0
  52. package/templates/cms_forms/form.html +22 -0
  53. package/templates/css/styles.css +4 -0
  54. package/templates/js/functions.js +144 -0
  55. package/templates/js/scripts.js +5 -0
  56. package/templates/page.html +11 -5
  57. package/templates/partials/pagedCollection.html +1 -1
  58. package/lib/admin-translations.js +0 -56
  59. package/templates/favicon.ico +0 -0
  60. /package/templates/assets/favicons/{android-icon-144x144.png → default/android-icon-144x144.png} +0 -0
  61. /package/templates/assets/favicons/{android-icon-192x192.png → default/android-icon-192x192.png} +0 -0
  62. /package/templates/assets/favicons/{android-icon-512x512.png → default/android-icon-512x512.png} +0 -0
  63. /package/templates/assets/favicons/{apple-touch-icon.png → default/apple-touch-icon.png} +0 -0
  64. /package/templates/assets/favicons/{favicon-16x16.png → default/favicon-16x16.png} +0 -0
  65. /package/templates/assets/favicons/{favicon-32x32.png → default/favicon-32x32.png} +0 -0
  66. /package/templates/assets/favicons/{mstile-150x150.png → default/mstile-150x150.png} +0 -0
  67. /package/templates/assets/favicons/{safari-pinned-tab.svg → default/safari-pinned-tab.svg} +0 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - ContentRenderer
4
+ *
5
+ */
6
+
7
+ const { FileHandler } = require('@reldens/server-utils');
8
+ const { Logger, sc } = require('@reldens/utils');
9
+
10
+ class ContentRenderer
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.dataServer = sc.get(props, 'dataServer', false);
16
+ this.templateEngine = sc.get(props, 'templateEngine', false);
17
+ this.templateResolver = sc.get(props, 'templateResolver', false);
18
+ this.templateCache = sc.get(props, 'templateCache', false);
19
+ this.metaDefaults = sc.get(props, 'metaDefaults', {});
20
+ }
21
+
22
+ fetchMetaFields(data)
23
+ {
24
+ if(!sc.isObject(data) || 0 === Object.keys(data).length){
25
+ return this.metaDefaults;
26
+ }
27
+ let result = Object.assign({}, this.metaDefaults);
28
+ for(let key of Object.keys(data)){
29
+ let value = data[key];
30
+ if(null !== value && '' !== value && 'undefined' !== typeof value){
31
+ result[key] = value;
32
+ }
33
+ }
34
+ let titleValue = sc.get(result, 'title', '');
35
+ let metaTitleValue = sc.get(result, 'meta_title', titleValue);
36
+ if(metaTitleValue && '' !== metaTitleValue){
37
+ result.meta_title = metaTitleValue;
38
+ }
39
+ let metaOgTitleValue = sc.get(result, 'meta_og_title', metaTitleValue);
40
+ if(metaOgTitleValue && '' !== metaOgTitleValue){
41
+ result.meta_og_title = metaOgTitleValue;
42
+ }
43
+ let metaDescValue = sc.get(result, 'meta_description', sc.get(result, 'description', ''));
44
+ if(metaDescValue && '' !== metaDescValue){
45
+ result.meta_description = metaDescValue;
46
+ }
47
+ let metaOgDescValue = sc.get(result, 'meta_og_description', metaDescValue);
48
+ if(metaOgDescValue && '' !== metaOgDescValue){
49
+ result.meta_og_description = metaOgDescValue;
50
+ }
51
+ let jsonData = sc.get(result, 'json_data', null);
52
+ if(sc.isString(jsonData)){
53
+ jsonData = sc.toJson(jsonData, {});
54
+ }
55
+ if(!sc.isObject(jsonData)){
56
+ jsonData = {};
57
+ }
58
+ let viewportValue = sc.get(jsonData, 'viewport', this.metaDefaults.viewport);
59
+ if(viewportValue && '' !== viewportValue){
60
+ jsonData.viewport = viewportValue;
61
+ }
62
+ result.json_data = jsonData;
63
+ return result;
64
+ }
65
+
66
+ async generateRouteContent(route, domain, req)
67
+ {
68
+ if(!route.router){
69
+ return false;
70
+ }
71
+ let entity = this.dataServer.getEntity(route.router);
72
+ if(!entity){
73
+ return false;
74
+ }
75
+ let content = await entity.loadOneWithRelations({route_id: route.id});
76
+ if(!content){
77
+ return false;
78
+ }
79
+ return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain, req, route);
80
+ }
81
+
82
+ async generateTemplateContent(templatePath, domain, req, data = {})
83
+ {
84
+ let template = FileHandler.readFile(templatePath);
85
+ if(!template){
86
+ Logger.error('Failed to read template: ' + templatePath);
87
+ return false;
88
+ }
89
+ return await this.templateEngine.render(
90
+ template,
91
+ data,
92
+ this.templateCache.getPartialsForDomain(domain),
93
+ domain,
94
+ req,
95
+ null,
96
+ null
97
+ );
98
+ }
99
+
100
+ async renderWithTemplateContent(content, data, domain, req, route)
101
+ {
102
+ let templateName = sc.get(content, 'template', 'page');
103
+ if(!templateName){
104
+ templateName = 'page';
105
+ }
106
+ let layoutName = sc.get(content, 'layout', '');
107
+ if(!layoutName){
108
+ layoutName = 'default';
109
+ }
110
+ let currentEntityData = Object.assign({}, content, data);
111
+ let layoutContent = await this.processContentWithLayout(
112
+ content,
113
+ data,
114
+ layoutName,
115
+ domain,
116
+ req,
117
+ route,
118
+ currentEntityData
119
+ );
120
+ let templatePath = this.templateResolver.findTemplatePath(templateName, domain);
121
+ if(!templatePath){
122
+ return layoutContent;
123
+ }
124
+ let routerKey = route?.router;
125
+ let categoryName = currentEntityData?.cms_categories?.name;
126
+ let siteHandle = this.templateResolver.resolveDomainToSiteKey(domain)
127
+ + (routerKey ? ' '+routerKey : '')
128
+ + (categoryName ? ' cat-'+categoryName : '');
129
+ return await this.generateTemplateContent(
130
+ templatePath,
131
+ domain,
132
+ req,
133
+ Object.assign(
134
+ {},
135
+ this.fetchMetaFields(data),
136
+ {
137
+ content: layoutContent,
138
+ siteHandle
139
+ }
140
+ )
141
+ );
142
+ }
143
+
144
+ async processContentWithLayout(content, data, layoutName, domain, req, route, currentEntityData)
145
+ {
146
+ let processedContent = await this.processContent(content, data, domain, req, route, currentEntityData);
147
+ let layoutPath = this.templateResolver.findLayoutPath(layoutName, domain);
148
+ if(!layoutPath){
149
+ return processedContent;
150
+ }
151
+ return await this.generateTemplateContent(
152
+ layoutPath,
153
+ domain,
154
+ req,
155
+ Object.assign({}, data, {content: processedContent})
156
+ );
157
+ }
158
+
159
+ async processContent(content, data, domain, req, route, currentEntityData)
160
+ {
161
+ let contentText = sc.get(content, 'content', '');
162
+ if(!contentText){
163
+ return '';
164
+ }
165
+ return await this.templateEngine.render(
166
+ contentText,
167
+ data,
168
+ this.templateCache.getPartialsForDomain(domain),
169
+ domain,
170
+ req,
171
+ route,
172
+ currentEntityData
173
+ );
174
+ }
175
+
176
+ }
177
+
178
+ module.exports.ContentRenderer = ContentRenderer;
@@ -0,0 +1,63 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - EntityAccessManager
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class EntityAccessManager
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.dataServer = sc.get(props, 'dataServer', false);
15
+ this.entityAccessCache = new Map();
16
+ }
17
+
18
+ async loadEntityAccessRules()
19
+ {
20
+ let accessEntity = this.dataServer.getEntity('entitiesAccess');
21
+ if(!accessEntity){
22
+ Logger.warning('Entities Access not found.');
23
+ return;
24
+ }
25
+ let accessRules = await accessEntity.loadAll();
26
+ for(let rule of accessRules){
27
+ this.entityAccessCache.set(rule.entity_name, rule.is_public);
28
+ }
29
+ }
30
+
31
+ async isEntityAccessible(entityName)
32
+ {
33
+ if(this.entityAccessCache.has(entityName)){
34
+ return this.entityAccessCache.get(entityName);
35
+ }
36
+ return false;
37
+ }
38
+
39
+ async findEntityByPath(path)
40
+ {
41
+ let pathSegments = path.split('/').filter(segment => '' !== segment);
42
+ if(2 > pathSegments.length){
43
+ return false;
44
+ }
45
+ let entityName = pathSegments[0];
46
+ if(!await this.isEntityAccessible(entityName)){
47
+ return false;
48
+ }
49
+ let entityId = pathSegments[1];
50
+ let entity = this.dataServer.getEntity(entityName);
51
+ if(!entity){
52
+ return false;
53
+ }
54
+ let loadedEntity = await entity.loadByIdWithRelations(entityId);
55
+ if(!loadedEntity){
56
+ return false;
57
+ }
58
+ return {entity: loadedEntity, entityName};
59
+ }
60
+
61
+ }
62
+
63
+ module.exports.EntityAccessManager = EntityAccessManager;
@@ -0,0 +1,128 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - RequestProcessor
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class RequestProcessor
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.dataServer = sc.get(props, 'dataServer', false);
15
+ this.templateResolver = sc.get(props, 'templateResolver', false);
16
+ this.domainMapping = sc.get(props, 'domainMapping', {});
17
+ }
18
+
19
+ getDomainFromRequest(req)
20
+ {
21
+ let host = req.get('host');
22
+ if(!host){
23
+ return false;
24
+ }
25
+ return host.split(':')[0];
26
+ }
27
+
28
+ resolveDomainMapping(domain)
29
+ {
30
+ if(!domain){
31
+ return domain;
32
+ }
33
+ return sc.get(this.domainMapping, domain, domain);
34
+ }
35
+
36
+ normalizePathForRouteSearch(path)
37
+ {
38
+ if(!path || '/' === path){
39
+ return '/';
40
+ }
41
+ return path.endsWith('/') ? path.slice(0, -1) : path;
42
+ }
43
+
44
+ async handleRouteRedirect(route, res)
45
+ {
46
+ let redirectUrl = sc.get(route, 'redirect_url', null);
47
+ if(!redirectUrl){
48
+ return false;
49
+ }
50
+ let redirectType = sc.get(route, 'redirect_type', '301');
51
+ let statusCode = '301' === redirectType ? 301 : 302;
52
+ res.redirect(statusCode, redirectUrl);
53
+ return true;
54
+ }
55
+
56
+ async findRouteByPath(path, domain)
57
+ {
58
+ let routesEntity = this.dataServer.getEntity('routes');
59
+ if(!routesEntity){
60
+ Logger.error('Routes entity not found in dataServer.');
61
+ return false;
62
+ }
63
+ let normalizedPath = this.normalizePathForRouteSearch(path);
64
+ let resolvedDomain = this.resolveDomainMapping(domain);
65
+ let domainFilter = resolvedDomain || null;
66
+ let routeFilters = {path: normalizedPath, enabled: 1};
67
+ let routes = await routesEntity.load(routeFilters);
68
+ let matchingRoute = false;
69
+ let nullDomain = false;
70
+ for(let route of routes){
71
+ if(route.domain === domainFilter){
72
+ matchingRoute = route;
73
+ break;
74
+ }
75
+ if(!route.domain){
76
+ nullDomain = route;
77
+ }
78
+ }
79
+ if(matchingRoute){
80
+ return matchingRoute;
81
+ }
82
+ if(nullDomain){
83
+ return nullDomain;
84
+ }
85
+ if(normalizedPath !== path){
86
+ let routeFiltersWithSlash = {path: path, enabled: 1};
87
+ let routesWithSlash = await routesEntity.load(routeFiltersWithSlash);
88
+ for(let route of routesWithSlash){
89
+ if(route.domain === domainFilter){
90
+ return route;
91
+ }
92
+ if(!route.domain){
93
+ nullDomain = route;
94
+ }
95
+ }
96
+ if(nullDomain){
97
+ return nullDomain;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+
103
+ buildCacheKey(path, req)
104
+ {
105
+ if(!req || !req.query){
106
+ return path;
107
+ }
108
+ for(let key of Object.keys(req.query)){
109
+ if(key.endsWith('-key')){
110
+ let queryString = '';
111
+ for(let qKey of Object.keys(req.query)){
112
+ queryString += (queryString ? '&' : '') + qKey + '=' + req.query[qKey];
113
+ }
114
+ let hash = 0;
115
+ for(let i = 0; i < queryString.length; i++){
116
+ let char = queryString.charCodeAt(i);
117
+ hash = ((hash << 5) - hash) + char;
118
+ hash = hash & hash;
119
+ }
120
+ return path + '_' + Math.abs(hash);
121
+ }
122
+ }
123
+ return path;
124
+ }
125
+
126
+ }
127
+
128
+ module.exports.RequestProcessor = RequestProcessor;
@@ -0,0 +1,54 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - ResponseManager
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+
9
+ class ResponseManager
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.cacheManager = sc.get(props, 'cacheManager', false);
15
+ this.contentRenderer = sc.get(props, 'contentRenderer', false);
16
+ this.templateResolver = sc.get(props, 'templateResolver', false);
17
+ this.requestProcessor = sc.get(props, 'requestProcessor', false);
18
+ }
19
+
20
+ async renderWithCacheHandler(contentGenerator, errorHandler, responseHandler, domain, res, path, req)
21
+ {
22
+ let renderedContent = await contentGenerator();
23
+ if(!renderedContent){
24
+ return await errorHandler();
25
+ }
26
+ if(this.cacheManager && this.cacheManager.isEnabled()){
27
+ let cacheKey = this.requestProcessor.buildCacheKey(path, req);
28
+ await this.cacheManager.set(domain, cacheKey, renderedContent);
29
+ }
30
+ return await responseHandler(renderedContent);
31
+ }
32
+
33
+ async renderNotFound(domain, res, req)
34
+ {
35
+ let notFoundPath = this.templateResolver.findTemplatePath('404', domain);
36
+ if(notFoundPath){
37
+ let content = await this.contentRenderer.generateTemplateContent(notFoundPath, domain, req, {});
38
+ if(content){
39
+ res.status(404);
40
+ return res.send(await this.contentRenderer.renderWithTemplateContent(
41
+ {content},
42
+ {meta_title: 'Page not found'},
43
+ domain,
44
+ req,
45
+ null
46
+ ));
47
+ }
48
+ }
49
+ return res.status(404).send('Page not found');
50
+ }
51
+
52
+ }
53
+
54
+ module.exports.ResponseManager = ResponseManager;
@@ -0,0 +1,102 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - TemplateCache
4
+ *
5
+ */
6
+
7
+ const { FileHandler } = require('@reldens/server-utils');
8
+ const { Logger, sc } = require('@reldens/utils');
9
+
10
+ class TemplateCache
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.templatesPath = sc.get(props, 'templatesPath', '');
16
+ this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
17
+ this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
18
+ this.partialsCache = {};
19
+ this.domainPartialsCache = new Map();
20
+ this.domainTemplatesMap = new Map();
21
+ this.templateResolver = sc.get(props, 'templateResolver', false);
22
+ }
23
+
24
+ async loadPartials()
25
+ {
26
+ let partialsPath = FileHandler.joinPaths(this.templatesPath, 'partials');
27
+ FileHandler.createFolder(partialsPath);
28
+ let partialFiles = FileHandler.getFilesInFolder(partialsPath, this.templateExtensions);
29
+ for(let file of partialFiles){
30
+ let partialName = this.templateResolver.extractTemplateName(file);
31
+ if(!partialName){
32
+ continue;
33
+ }
34
+ let partialPath = FileHandler.joinPaths(partialsPath, file);
35
+ let partialContent = FileHandler.readFile(partialPath);
36
+ if(!partialContent){
37
+ Logger.error('Failed to read partial: '+partialPath);
38
+ continue;
39
+ }
40
+ this.partialsCache[partialName] = partialContent;
41
+ }
42
+ }
43
+
44
+ async loadDomainPartials(domain, domainPath)
45
+ {
46
+ let domainPartialsPath = FileHandler.joinPaths(domainPath, 'partials');
47
+ if(!FileHandler.exists(domainPartialsPath)){
48
+ return;
49
+ }
50
+ let domainPartials = {};
51
+ let partialFiles = FileHandler.getFilesInFolder(domainPartialsPath, this.templateExtensions);
52
+ for(let file of partialFiles){
53
+ let partialName = this.templateResolver.extractTemplateName(file);
54
+ if(!partialName){
55
+ continue;
56
+ }
57
+ let partialPath = FileHandler.joinPaths(domainPartialsPath, file);
58
+ let partialContent = FileHandler.readFile(partialPath);
59
+ if(!partialContent){
60
+ Logger.error('Failed to read domain partial: '+partialPath);
61
+ continue;
62
+ }
63
+ domainPartials[partialName] = partialContent;
64
+ }
65
+ this.domainPartialsCache.set(domain, domainPartials);
66
+ }
67
+
68
+ async setupDomainTemplates()
69
+ {
70
+ let domainsPath = FileHandler.joinPaths(this.templatesPath, 'domains');
71
+ if(!FileHandler.exists(domainsPath)){
72
+ return;
73
+ }
74
+ let domainFolders = FileHandler.fetchSubFoldersList(domainsPath);
75
+ for(let domain of domainFolders){
76
+ let domainPath = FileHandler.joinPaths(domainsPath, domain);
77
+ this.domainTemplatesMap.set(domain, domainPath);
78
+ await this.loadDomainPartials(domain, domainPath);
79
+ }
80
+ }
81
+
82
+ getPartialsForDomain(domain)
83
+ {
84
+ let resolvedDomain = this.templateResolver.resolveDomainToFolder(domain);
85
+ let domainPartials = this.domainPartialsCache.get(resolvedDomain);
86
+ if(!domainPartials && this.defaultDomain && resolvedDomain !== this.defaultDomain){
87
+ domainPartials = this.domainPartialsCache.get(this.defaultDomain);
88
+ }
89
+ if(!domainPartials){
90
+ return this.partialsCache;
91
+ }
92
+ return Object.assign({}, this.partialsCache, domainPartials);
93
+ }
94
+
95
+ getDomainTemplatesMap()
96
+ {
97
+ return this.domainTemplatesMap;
98
+ }
99
+
100
+ }
101
+
102
+ module.exports.TemplateCache = TemplateCache;
@@ -0,0 +1,111 @@
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend - TemplateResolver
4
+ *
5
+ */
6
+
7
+ const { FileHandler } = require('@reldens/server-utils');
8
+ const { sc } = require('@reldens/utils');
9
+
10
+ class TemplateResolver
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.templatesPath = sc.get(props, 'templatesPath', '');
16
+ this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
17
+ this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
18
+ this.domainMapping = sc.get(props, 'domainMapping', {});
19
+ this.siteKeyMapping = sc.get(props, 'siteKeyMapping', {});
20
+ this.domainTemplatesMap = sc.get(props, 'domainTemplatesMap', new Map());
21
+ }
22
+
23
+ extractTemplateName(filename)
24
+ {
25
+ for(let extension of this.templateExtensions){
26
+ if(filename.endsWith(extension)){
27
+ return filename.replace(extension, '');
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+
33
+ resolveDomainToFolder(domain)
34
+ {
35
+ if(!domain){
36
+ domain = this.defaultDomain;
37
+ }
38
+ return sc.get(this.domainMapping, domain, domain);
39
+ }
40
+
41
+ resolveDomainToSiteKey(domain)
42
+ {
43
+ return sc.get(this.siteKeyMapping, this.resolveDomainToFolder(domain), 'default');
44
+ }
45
+
46
+ findTemplatePath(templateName, domain)
47
+ {
48
+ let resolvedDomain = this.resolveDomainToFolder(domain);
49
+ if(resolvedDomain){
50
+ let domainPath = this.domainTemplatesMap.get(resolvedDomain);
51
+ if(domainPath){
52
+ let domainTemplatePath = this.findTemplateInPath(templateName, domainPath);
53
+ if(domainTemplatePath){
54
+ return domainTemplatePath;
55
+ }
56
+ }
57
+ if(this.defaultDomain && resolvedDomain !== this.defaultDomain){
58
+ let defaultDomainPath = this.domainTemplatesMap.get(this.defaultDomain);
59
+ if(defaultDomainPath){
60
+ let defaultTemplatePath = this.findTemplateInPath(templateName, defaultDomainPath);
61
+ if(defaultTemplatePath){
62
+ return defaultTemplatePath;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return this.findTemplateInPath(templateName, this.templatesPath);
68
+ }
69
+
70
+ findTemplateInPath(templateName, basePath)
71
+ {
72
+ for(let extension of this.templateExtensions){
73
+ let templatePath = FileHandler.joinPaths(basePath, templateName + extension);
74
+ if(FileHandler.exists(templatePath)){
75
+ return templatePath;
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+
81
+ findLayoutPath(layoutName, domain)
82
+ {
83
+ let resolvedDomain = this.resolveDomainToFolder(domain);
84
+ if(resolvedDomain){
85
+ let domainPath = this.domainTemplatesMap.get(resolvedDomain);
86
+ if(domainPath){
87
+ let domainLayoutPath = this.findTemplateInPath('layouts/' + layoutName, domainPath);
88
+ if(domainLayoutPath){
89
+ return domainLayoutPath;
90
+ }
91
+ }
92
+ }
93
+ return this.findTemplateInPath('layouts/' + layoutName, this.templatesPath);
94
+ }
95
+
96
+ findTemplateByPath(path, domain)
97
+ {
98
+ if('/' === path){
99
+ path = '/index';
100
+ }
101
+ let templatePath = path.endsWith('/') ? path.slice(0, -1) : path;
102
+ templatePath = templatePath.startsWith('/') ? templatePath.substring(1) : templatePath;
103
+ if('page' === templatePath){
104
+ return false;
105
+ }
106
+ return this.findTemplatePath(templatePath, domain);
107
+ }
108
+
109
+ }
110
+
111
+ module.exports.TemplateResolver = TemplateResolver;