@reldens/cms 0.8.0 → 0.10.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 +316 -68
- package/admin/templates/fields/edit/text.html +1 -1
- package/install/css/installer.css +8 -0
- package/install/index.html +14 -6
- package/lib/admin-manager.js +39 -31
- package/lib/frontend.js +336 -105
- package/lib/installer.js +36 -9
- package/lib/manager.js +37 -5
- package/migrations/default-blocks.sql +8 -0
- package/migrations/default-entity-access.sql +9 -0
- package/migrations/default-homepage.sql +1 -1
- package/migrations/install.sql +34 -9
- package/migrations/users-authentication.sql +2 -2
- package/package.json +4 -4
- package/templates/404.html +7 -29
- package/templates/css/styles.css +4 -1
- package/templates/domains/.gitkeep +0 -0
- package/templates/js/scripts.js +9 -7
- package/templates/layouts/default.html +21 -0
- package/templates/page.html +14 -32
- package/templates/partials/footer.html +35 -0
- package/templates/partials/header.html +36 -0
package/lib/frontend.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
const { FileHandler } = require('@reldens/server-utils');
|
|
8
8
|
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
-
const mustache = require('mustache');
|
|
10
9
|
|
|
11
10
|
class Frontend
|
|
12
11
|
{
|
|
@@ -16,16 +15,32 @@ class Frontend
|
|
|
16
15
|
this.app = sc.get(props, 'app', false);
|
|
17
16
|
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
18
17
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
18
|
+
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
19
19
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
20
20
|
this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
21
21
|
this.publicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
22
|
-
this.
|
|
22
|
+
this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
|
|
23
|
+
this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
|
|
24
|
+
this.domainMapping = sc.get(props, 'domainMapping', {});
|
|
25
|
+
this.siteKeyMapping = sc.get(props, 'siteKeyMapping', {});
|
|
26
|
+
this.partialsCache = {};
|
|
27
|
+
this.domainPartialsCache = new Map();
|
|
28
|
+
this.domainTemplatesMap = new Map();
|
|
29
|
+
this.entityAccessCache = new Map();
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
async initialize()
|
|
26
33
|
{
|
|
27
34
|
if(!this.app || !this.dataServer){
|
|
28
|
-
|
|
35
|
+
Logger.error('Missing app or dataServer');
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if(!this.renderEngine){
|
|
39
|
+
Logger.error('Please, provide a renderEngine, it must contain a "render" method.');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if(!sc.isFunction(this.renderEngine.render)){
|
|
43
|
+
Logger.error('The provided renderEngine does not contain a "render" method.');
|
|
29
44
|
return false;
|
|
30
45
|
}
|
|
31
46
|
if(!FileHandler.exists(this.templatesPath)){
|
|
@@ -36,6 +51,9 @@ class Frontend
|
|
|
36
51
|
Logger.error('Public folder not found: '+this.publicPath);
|
|
37
52
|
return false;
|
|
38
53
|
}
|
|
54
|
+
await this.loadPartials();
|
|
55
|
+
await this.setupDomainTemplates();
|
|
56
|
+
await this.loadEntityAccessRules();
|
|
39
57
|
this.setupStaticAssets();
|
|
40
58
|
this.app.get('*', async (req, res) => {
|
|
41
59
|
return await this.handleRequest(req, res);
|
|
@@ -43,6 +61,172 @@ class Frontend
|
|
|
43
61
|
return true;
|
|
44
62
|
}
|
|
45
63
|
|
|
64
|
+
async loadEntityAccessRules()
|
|
65
|
+
{
|
|
66
|
+
let accessEntity = this.dataServer.getEntity('cmsEntityAccess');
|
|
67
|
+
if(!accessEntity){
|
|
68
|
+
Logger.warning('CMS Entity Access not found.');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let accessRules = await accessEntity.loadAll();
|
|
72
|
+
for(let rule of accessRules){
|
|
73
|
+
this.entityAccessCache.set(rule.entity_name, rule.is_public);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async loadPartials()
|
|
78
|
+
{
|
|
79
|
+
let partialsPath = FileHandler.joinPaths(this.templatesPath, 'partials');
|
|
80
|
+
FileHandler.createFolder(partialsPath);
|
|
81
|
+
let partialFiles = FileHandler.getFilesInFolder(partialsPath, this.templateExtensions);
|
|
82
|
+
for(let file of partialFiles){
|
|
83
|
+
let partialName = this.extractTemplateName(file);
|
|
84
|
+
if(!partialName){
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
let partialPath = FileHandler.joinPaths(partialsPath, file);
|
|
88
|
+
let partialContent = FileHandler.readFile(partialPath);
|
|
89
|
+
if(!partialContent){
|
|
90
|
+
Logger.error('Failed to read partial: '+partialPath);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
this.partialsCache[partialName] = partialContent;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async loadDomainPartials(domain, domainPath)
|
|
98
|
+
{
|
|
99
|
+
let domainPartialsPath = FileHandler.joinPaths(domainPath, 'partials');
|
|
100
|
+
if(!FileHandler.exists(domainPartialsPath)){
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
let domainPartials = {};
|
|
104
|
+
let partialFiles = FileHandler.getFilesInFolder(domainPartialsPath, this.templateExtensions);
|
|
105
|
+
for(let file of partialFiles){
|
|
106
|
+
let partialName = this.extractTemplateName(file);
|
|
107
|
+
if(!partialName){
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
let partialPath = FileHandler.joinPaths(domainPartialsPath, file);
|
|
111
|
+
let partialContent = FileHandler.readFile(partialPath);
|
|
112
|
+
if(!partialContent){
|
|
113
|
+
Logger.error('Failed to read domain partial: '+partialPath);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
domainPartials[partialName] = partialContent;
|
|
117
|
+
}
|
|
118
|
+
this.domainPartialsCache.set(domain, domainPartials);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async setupDomainTemplates()
|
|
122
|
+
{
|
|
123
|
+
let domainsPath = FileHandler.joinPaths(this.templatesPath, 'domains');
|
|
124
|
+
if(!FileHandler.exists(domainsPath)){
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
let domainFolders = FileHandler.fetchSubFoldersList(domainsPath);
|
|
128
|
+
for(let domain of domainFolders){
|
|
129
|
+
let domainPath = FileHandler.joinPaths(domainsPath, domain);
|
|
130
|
+
this.domainTemplatesMap.set(domain, domainPath);
|
|
131
|
+
await this.loadDomainPartials(domain, domainPath);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
extractTemplateName(filename)
|
|
136
|
+
{
|
|
137
|
+
for(let extension of this.templateExtensions){
|
|
138
|
+
if(filename.endsWith(extension)){
|
|
139
|
+
return filename.replace(extension, '');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getDomainFromRequest(req)
|
|
146
|
+
{
|
|
147
|
+
let host = req.get('host');
|
|
148
|
+
if(!host){
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return host.split(':')[0];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
resolveDomainToFolder(domain)
|
|
155
|
+
{
|
|
156
|
+
if(!domain){
|
|
157
|
+
domain = this.defaultDomain;
|
|
158
|
+
}
|
|
159
|
+
return sc.get(this.domainMapping, domain, domain);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
resolveDomainToSiteKey(domain)
|
|
163
|
+
{
|
|
164
|
+
return sc.get(this.siteKeyMapping, this.resolveDomainToFolder(domain), 'default');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getPartialsForDomain(domain)
|
|
168
|
+
{
|
|
169
|
+
let resolvedDomain = this.resolveDomainToFolder(domain);
|
|
170
|
+
let domainPartials = this.domainPartialsCache.get(resolvedDomain);
|
|
171
|
+
if(!domainPartials && this.defaultDomain && resolvedDomain !== this.defaultDomain){
|
|
172
|
+
domainPartials = this.domainPartialsCache.get(this.defaultDomain);
|
|
173
|
+
}
|
|
174
|
+
if(!domainPartials){
|
|
175
|
+
return this.partialsCache;
|
|
176
|
+
}
|
|
177
|
+
return Object.assign({}, this.partialsCache, domainPartials);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
findTemplatePath(templateName, domain)
|
|
181
|
+
{
|
|
182
|
+
let resolvedDomain = this.resolveDomainToFolder(domain);
|
|
183
|
+
if(resolvedDomain){
|
|
184
|
+
let domainPath = this.domainTemplatesMap.get(resolvedDomain);
|
|
185
|
+
if(domainPath){
|
|
186
|
+
let domainTemplatePath = this.findTemplateInPath(templateName, domainPath);
|
|
187
|
+
if(domainTemplatePath){
|
|
188
|
+
return domainTemplatePath;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if(this.defaultDomain && resolvedDomain !== this.defaultDomain){
|
|
192
|
+
let defaultDomainPath = this.domainTemplatesMap.get(this.defaultDomain);
|
|
193
|
+
if(defaultDomainPath){
|
|
194
|
+
let defaultTemplatePath = this.findTemplateInPath(templateName, defaultDomainPath);
|
|
195
|
+
if(defaultTemplatePath){
|
|
196
|
+
return defaultTemplatePath;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return this.findTemplateInPath(templateName, this.templatesPath);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
findTemplateInPath(templateName, basePath)
|
|
205
|
+
{
|
|
206
|
+
for(let extension of this.templateExtensions){
|
|
207
|
+
let templatePath = FileHandler.joinPaths(basePath, templateName + extension);
|
|
208
|
+
if(FileHandler.exists(templatePath)){
|
|
209
|
+
return templatePath;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
findLayoutPath(layoutName, domain)
|
|
216
|
+
{
|
|
217
|
+
let resolvedDomain = this.resolveDomainToFolder(domain);
|
|
218
|
+
if(resolvedDomain){
|
|
219
|
+
let domainPath = this.domainTemplatesMap.get(resolvedDomain);
|
|
220
|
+
if(domainPath){
|
|
221
|
+
let domainLayoutPath = this.findTemplateInPath('layouts/' + layoutName, domainPath);
|
|
222
|
+
if(domainLayoutPath){
|
|
223
|
+
return domainLayoutPath;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return this.findTemplateInPath('layouts/' + layoutName, this.templatesPath);
|
|
228
|
+
}
|
|
229
|
+
|
|
46
230
|
setupStaticAssets()
|
|
47
231
|
{
|
|
48
232
|
if(!this.app || !this.appServerFactory || !this.publicPath){
|
|
@@ -57,35 +241,22 @@ class Frontend
|
|
|
57
241
|
|
|
58
242
|
async handleRequest(req, res)
|
|
59
243
|
{
|
|
60
|
-
let path = req.path;
|
|
61
|
-
if('/favicon.ico' === path){
|
|
62
|
-
return res.status(404).send('');
|
|
63
|
-
}
|
|
64
244
|
try {
|
|
245
|
+
let path = req.path;
|
|
246
|
+
let domain = this.getDomainFromRequest(req);
|
|
65
247
|
let route = await this.findRouteByPath(path);
|
|
66
248
|
if(route){
|
|
67
|
-
|
|
68
|
-
return await this.renderContentFromRoute(res, route);
|
|
249
|
+
return await this.renderRoute(route, domain, res);
|
|
69
250
|
}
|
|
70
|
-
let
|
|
71
|
-
if(
|
|
72
|
-
|
|
73
|
-
if(entityResult){
|
|
74
|
-
Logger.debug('Found entity for path segments: '+pathSegments.join('/'));
|
|
75
|
-
return await this.renderContentFromEntity(
|
|
76
|
-
res,
|
|
77
|
-
entityResult.entity,
|
|
78
|
-
entityResult.entityName
|
|
79
|
-
);
|
|
80
|
-
}
|
|
251
|
+
let entityResult = await this.findEntityByPath(path);
|
|
252
|
+
if(entityResult){
|
|
253
|
+
return await this.renderEntity(entityResult, domain, res);
|
|
81
254
|
}
|
|
82
|
-
let templatePath = this.findTemplateByPath(path);
|
|
255
|
+
let templatePath = this.findTemplateByPath(path, domain);
|
|
83
256
|
if(templatePath){
|
|
84
|
-
|
|
85
|
-
return await this.renderTemplateOnly(res, templatePath);
|
|
257
|
+
return await this.renderTemplate(templatePath, domain, res);
|
|
86
258
|
}
|
|
87
|
-
|
|
88
|
-
return await this.renderNotFoundPage(res);
|
|
259
|
+
return await this.renderNotFound(domain, res);
|
|
89
260
|
} catch (error) {
|
|
90
261
|
Logger.error('Request handling error: '+error.message);
|
|
91
262
|
return res.status(500).send('Internal server error');
|
|
@@ -109,130 +280,190 @@ class Frontend
|
|
|
109
280
|
return false;
|
|
110
281
|
}
|
|
111
282
|
|
|
112
|
-
async
|
|
283
|
+
async isEntityAccessible(entityName)
|
|
284
|
+
{
|
|
285
|
+
if(this.entityAccessCache.has(entityName)){
|
|
286
|
+
return this.entityAccessCache.get(entityName);
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async findEntityByPath(path)
|
|
113
292
|
{
|
|
114
|
-
|
|
115
|
-
|
|
293
|
+
let pathSegments = path.split('/').filter(segment => '' !== segment);
|
|
294
|
+
if(2 > pathSegments.length){
|
|
116
295
|
return false;
|
|
117
296
|
}
|
|
118
297
|
let entityName = pathSegments[0];
|
|
119
|
-
|
|
120
|
-
if(!entityId){
|
|
121
|
-
Logger.debug('No entity ID in path segments');
|
|
298
|
+
if(!await this.isEntityAccessible(entityName)){
|
|
122
299
|
return false;
|
|
123
300
|
}
|
|
301
|
+
let entityId = pathSegments[1];
|
|
124
302
|
let entity = this.dataServer.getEntity(entityName);
|
|
125
303
|
if(!entity){
|
|
126
|
-
Logger.debug('Entity not found: '+entityName);
|
|
127
304
|
return false;
|
|
128
305
|
}
|
|
129
306
|
let loadedEntity = await entity.loadById(entityId);
|
|
130
307
|
if(!loadedEntity){
|
|
131
|
-
Logger.debug('Entity not loaded by ID: '+entityId);
|
|
132
308
|
return false;
|
|
133
309
|
}
|
|
134
|
-
return {
|
|
135
|
-
entity: loadedEntity,
|
|
136
|
-
entityName
|
|
137
|
-
};
|
|
310
|
+
return {entity: loadedEntity, entityName};
|
|
138
311
|
}
|
|
139
312
|
|
|
140
|
-
findTemplateByPath(path)
|
|
313
|
+
findTemplateByPath(path, domain)
|
|
141
314
|
{
|
|
142
315
|
if('/' === path){
|
|
143
316
|
path = '/index';
|
|
144
317
|
}
|
|
145
|
-
let templatePath = path.endsWith('/')
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
? templatePath.substring(1)
|
|
150
|
-
: templatePath;
|
|
151
|
-
let fullPath = FileHandler.joinPaths(this.templatesPath, templatePath + '.html');
|
|
152
|
-
if(FileHandler.exists(fullPath)){
|
|
153
|
-
return fullPath;
|
|
318
|
+
let templatePath = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
319
|
+
templatePath = templatePath.startsWith('/') ? templatePath.substring(1) : templatePath;
|
|
320
|
+
if('page' === templatePath){
|
|
321
|
+
return false;
|
|
154
322
|
}
|
|
155
|
-
return
|
|
323
|
+
return this.findTemplatePath(templatePath, domain);
|
|
156
324
|
}
|
|
157
325
|
|
|
158
|
-
async
|
|
326
|
+
async renderEnhanced(template, data, partials)
|
|
327
|
+
{
|
|
328
|
+
return this.renderEngine.render(
|
|
329
|
+
await this.processCustomTemplateFunctions(template),
|
|
330
|
+
data,
|
|
331
|
+
partials
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async processCustomTemplateFunctions(template)
|
|
336
|
+
{
|
|
337
|
+
let entityRegex = /\{\{\s*entity\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*\)\s*\}\}/g;
|
|
338
|
+
let processedTemplate = template;
|
|
339
|
+
for(let match of template.matchAll(entityRegex)){
|
|
340
|
+
let tableName = match[1];
|
|
341
|
+
let identifier = match[2];
|
|
342
|
+
let entityData = await this.fetchEntityForTemplate(tableName, identifier);
|
|
343
|
+
processedTemplate = processedTemplate.replace(match[0], sc.get(entityData, 'content', ''));
|
|
344
|
+
}
|
|
345
|
+
return processedTemplate;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async fetchEntityForTemplate(tableName, identifier)
|
|
349
|
+
{
|
|
350
|
+
let entity = this.dataServer.getEntity(tableName);
|
|
351
|
+
if(!entity){
|
|
352
|
+
Logger.warning('Entity not found in dataServer: '+tableName);
|
|
353
|
+
return {};
|
|
354
|
+
}
|
|
355
|
+
let result = await entity.loadOneBy('name', identifier);
|
|
356
|
+
if(!result){
|
|
357
|
+
result = await entity.loadById(identifier);
|
|
358
|
+
}
|
|
359
|
+
return result || {};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async renderRoute(route, domain, res)
|
|
159
363
|
{
|
|
160
364
|
if(!route.router || !route.content_id){
|
|
161
|
-
|
|
162
|
-
return await this.renderNotFoundPage(res);
|
|
365
|
+
return await this.renderNotFound(domain, res);
|
|
163
366
|
}
|
|
164
367
|
let entity = this.dataServer.getEntity(route.router);
|
|
165
368
|
if(!entity){
|
|
166
|
-
|
|
167
|
-
return await this.renderNotFoundPage(res);
|
|
369
|
+
return await this.renderNotFound(domain, res);
|
|
168
370
|
}
|
|
169
371
|
let content = await entity.loadById(route.content_id);
|
|
170
372
|
if(!content){
|
|
171
|
-
|
|
172
|
-
return await this.renderNotFoundPage(res);
|
|
173
|
-
}
|
|
174
|
-
let templateName = content.template || route.router;
|
|
175
|
-
let templatePath = FileHandler.joinPaths(this.templatesPath, templateName + '.html');
|
|
176
|
-
if(!FileHandler.exists(templatePath)){
|
|
177
|
-
templatePath = FileHandler.joinPaths(this.templatesPath, 'page.html');
|
|
178
|
-
if(!FileHandler.exists(templatePath)){
|
|
179
|
-
Logger.debug('Neither template found: '+templateName+'.html nor page.html');
|
|
180
|
-
return await this.renderNotFoundPage(res);
|
|
181
|
-
}
|
|
373
|
+
return await this.renderNotFound(domain, res);
|
|
182
374
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return res.send(rendered);
|
|
375
|
+
return await this.renderWithLayout(
|
|
376
|
+
content,
|
|
377
|
+
Object.assign({}, route, content, {currentYear: new Date().getFullYear()}),
|
|
378
|
+
sc.get(content, 'layout', 'default'),
|
|
379
|
+
domain,
|
|
380
|
+
res
|
|
381
|
+
);
|
|
191
382
|
}
|
|
192
383
|
|
|
193
|
-
async
|
|
384
|
+
async renderEntity(entityResult, domain, res)
|
|
194
385
|
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
386
|
+
return await this.renderWithLayout(
|
|
387
|
+
entityResult.entity,
|
|
388
|
+
Object.assign({}, entityResult.entity, {currentYear: new Date().getFullYear()}),
|
|
389
|
+
sc.get(entityResult.entity, 'layout', 'default'),
|
|
390
|
+
domain,
|
|
391
|
+
res
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async renderTemplate(templatePath, domain, res)
|
|
396
|
+
{
|
|
397
|
+
let data = { currentYear: new Date().getFullYear() };
|
|
398
|
+
let content = await this.renderContentWithTemplate(templatePath, data, domain);
|
|
399
|
+
if(!content){
|
|
400
|
+
return res.status(500).send('Template error: '+templatePath);
|
|
201
401
|
}
|
|
202
|
-
|
|
203
|
-
let data = {
|
|
204
|
-
...entity,
|
|
205
|
-
title: entity.title || entity.name || entityName,
|
|
206
|
-
current_year: new Date().getFullYear()
|
|
207
|
-
};
|
|
208
|
-
let rendered = mustache.render(template, data);
|
|
209
|
-
return res.send(rendered);
|
|
402
|
+
return await this.renderWithLayout({content}, data, 'default', domain, res);
|
|
210
403
|
}
|
|
211
404
|
|
|
212
|
-
async
|
|
405
|
+
async renderContentWithTemplate(templatePath, data, domain)
|
|
213
406
|
{
|
|
214
|
-
let template = FileHandler.readFile(templatePath)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return res.send(rendered);
|
|
407
|
+
let template = FileHandler.readFile(templatePath);
|
|
408
|
+
if(!template){
|
|
409
|
+
Logger.error('Failed to read template: ' + templatePath);
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return await this.renderEnhanced(template, data, this.getPartialsForDomain(domain));
|
|
221
413
|
}
|
|
222
414
|
|
|
223
|
-
async
|
|
415
|
+
async renderWithLayout(content, data, layoutName, domain, res)
|
|
224
416
|
{
|
|
225
|
-
let
|
|
226
|
-
|
|
417
|
+
let layoutPath = this.findLayoutPath(layoutName, domain);
|
|
418
|
+
let layoutContent = '';
|
|
419
|
+
if(layoutPath){
|
|
420
|
+
let layoutTemplate = FileHandler.readFile(layoutPath);
|
|
421
|
+
if(layoutTemplate){
|
|
422
|
+
layoutContent = await this.renderEnhanced(
|
|
423
|
+
layoutTemplate,
|
|
424
|
+
Object.assign({}, data, {
|
|
425
|
+
content: sc.get(content, 'content', '')
|
|
426
|
+
}),
|
|
427
|
+
this.getPartialsForDomain(domain)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if('' === layoutContent){
|
|
432
|
+
layoutContent = sc.get(content, 'content', '');
|
|
433
|
+
}
|
|
434
|
+
let pagePath = this.findTemplatePath('page', domain);
|
|
435
|
+
if(!pagePath){
|
|
436
|
+
return res.send(layoutContent);
|
|
437
|
+
}
|
|
438
|
+
let pageTemplate = FileHandler.readFile(pagePath);
|
|
439
|
+
if(!pageTemplate){
|
|
440
|
+
return res.send(layoutContent);
|
|
441
|
+
}
|
|
442
|
+
return res.send(
|
|
443
|
+
await this.renderEnhanced(
|
|
444
|
+
pageTemplate,
|
|
445
|
+
Object.assign({}, data, {
|
|
446
|
+
content: layoutContent,
|
|
447
|
+
siteHandle: this.resolveDomainToSiteKey(domain)
|
|
448
|
+
}),
|
|
449
|
+
this.getPartialsForDomain(domain)
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async renderNotFound(domain, res)
|
|
455
|
+
{
|
|
456
|
+
let templatePath = this.findTemplatePath('404', domain);
|
|
457
|
+
if(!templatePath){
|
|
458
|
+
return res.status(404).send('Page not found');
|
|
459
|
+
}
|
|
460
|
+
let data = {title: '404 - Page Not Found', currentYear: new Date().getFullYear()};
|
|
461
|
+
let content = await this.renderContentWithTemplate(templatePath, data, domain);
|
|
462
|
+
if(!content){
|
|
227
463
|
return res.status(404).send('Page not found');
|
|
228
464
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
title: '404 - Page Not Found',
|
|
232
|
-
current_year: new Date().getFullYear()
|
|
233
|
-
};
|
|
234
|
-
let rendered = mustache.render(template, data);
|
|
235
|
-
return res.status(404).send(rendered);
|
|
465
|
+
res.status(404);
|
|
466
|
+
return await this.renderWithLayout({content}, data, 'default', domain, res);
|
|
236
467
|
}
|
|
237
468
|
|
|
238
469
|
}
|