@reldens/cms 0.9.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 +263 -139
- 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,20 +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', {});
|
|
23
26
|
this.partialsCache = {};
|
|
24
27
|
this.domainPartialsCache = new Map();
|
|
25
28
|
this.domainTemplatesMap = new Map();
|
|
26
|
-
this.
|
|
29
|
+
this.entityAccessCache = new Map();
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
async initialize()
|
|
30
33
|
{
|
|
31
34
|
if(!this.app || !this.dataServer){
|
|
32
|
-
|
|
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.');
|
|
33
44
|
return false;
|
|
34
45
|
}
|
|
35
46
|
if(!FileHandler.exists(this.templatesPath)){
|
|
@@ -42,6 +53,7 @@ class Frontend
|
|
|
42
53
|
}
|
|
43
54
|
await this.loadPartials();
|
|
44
55
|
await this.setupDomainTemplates();
|
|
56
|
+
await this.loadEntityAccessRules();
|
|
45
57
|
this.setupStaticAssets();
|
|
46
58
|
this.app.get('*', async (req, res) => {
|
|
47
59
|
return await this.handleRequest(req, res);
|
|
@@ -49,22 +61,36 @@ class Frontend
|
|
|
49
61
|
return true;
|
|
50
62
|
}
|
|
51
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
|
+
|
|
52
77
|
async loadPartials()
|
|
53
78
|
{
|
|
54
79
|
let partialsPath = FileHandler.joinPaths(this.templatesPath, 'partials');
|
|
55
80
|
FileHandler.createFolder(partialsPath);
|
|
56
|
-
let partialFiles = FileHandler.getFilesInFolder(partialsPath, this.
|
|
81
|
+
let partialFiles = FileHandler.getFilesInFolder(partialsPath, this.templateExtensions);
|
|
57
82
|
for(let file of partialFiles){
|
|
58
|
-
let partialName =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
partialName = file.replace(extension, '');
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
83
|
+
let partialName = this.extractTemplateName(file);
|
|
84
|
+
if(!partialName){
|
|
85
|
+
continue;
|
|
64
86
|
}
|
|
65
87
|
let partialPath = FileHandler.joinPaths(partialsPath, file);
|
|
66
88
|
let partialContent = FileHandler.readFile(partialPath);
|
|
67
|
-
|
|
89
|
+
if(!partialContent){
|
|
90
|
+
Logger.error('Failed to read partial: '+partialPath);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
this.partialsCache[partialName] = partialContent;
|
|
68
94
|
}
|
|
69
95
|
}
|
|
70
96
|
|
|
@@ -75,18 +101,19 @@ class Frontend
|
|
|
75
101
|
return;
|
|
76
102
|
}
|
|
77
103
|
let domainPartials = {};
|
|
78
|
-
let partialFiles = FileHandler.getFilesInFolder(domainPartialsPath, this.
|
|
104
|
+
let partialFiles = FileHandler.getFilesInFolder(domainPartialsPath, this.templateExtensions);
|
|
79
105
|
for(let file of partialFiles){
|
|
80
|
-
let partialName =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
partialName = file.replace(extension, '');
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
106
|
+
let partialName = this.extractTemplateName(file);
|
|
107
|
+
if(!partialName){
|
|
108
|
+
continue;
|
|
86
109
|
}
|
|
87
110
|
let partialPath = FileHandler.joinPaths(domainPartialsPath, file);
|
|
88
111
|
let partialContent = FileHandler.readFile(partialPath);
|
|
89
|
-
|
|
112
|
+
if(!partialContent){
|
|
113
|
+
Logger.error('Failed to read domain partial: '+partialPath);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
domainPartials[partialName] = partialContent;
|
|
90
117
|
}
|
|
91
118
|
this.domainPartialsCache.set(domain, domainPartials);
|
|
92
119
|
}
|
|
@@ -105,6 +132,16 @@ class Frontend
|
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
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
|
+
|
|
108
145
|
getDomainFromRequest(req)
|
|
109
146
|
{
|
|
110
147
|
let host = req.get('host');
|
|
@@ -114,34 +151,80 @@ class Frontend
|
|
|
114
151
|
return host.split(':')[0];
|
|
115
152
|
}
|
|
116
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
|
+
|
|
117
167
|
getPartialsForDomain(domain)
|
|
118
168
|
{
|
|
119
|
-
let
|
|
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
|
+
}
|
|
120
174
|
if(!domainPartials){
|
|
121
175
|
return this.partialsCache;
|
|
122
176
|
}
|
|
123
177
|
return Object.assign({}, this.partialsCache, domainPartials);
|
|
124
178
|
}
|
|
125
179
|
|
|
126
|
-
|
|
180
|
+
findTemplatePath(templateName, domain)
|
|
127
181
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
134
200
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
}
|
|
138
211
|
}
|
|
139
|
-
return
|
|
212
|
+
return false;
|
|
140
213
|
}
|
|
141
214
|
|
|
142
|
-
|
|
215
|
+
findLayoutPath(layoutName, domain)
|
|
143
216
|
{
|
|
144
|
-
|
|
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);
|
|
145
228
|
}
|
|
146
229
|
|
|
147
230
|
setupStaticAssets()
|
|
@@ -158,37 +241,22 @@ class Frontend
|
|
|
158
241
|
|
|
159
242
|
async handleRequest(req, res)
|
|
160
243
|
{
|
|
161
|
-
let path = req.path;
|
|
162
|
-
if('/favicon.ico' === path){
|
|
163
|
-
return res.status(404).send('');
|
|
164
|
-
}
|
|
165
244
|
try {
|
|
245
|
+
let path = req.path;
|
|
166
246
|
let domain = this.getDomainFromRequest(req);
|
|
167
247
|
let route = await this.findRouteByPath(path);
|
|
168
248
|
if(route){
|
|
169
|
-
|
|
170
|
-
return await this.renderContentFromRoute(res, route, domain);
|
|
249
|
+
return await this.renderRoute(route, domain, res);
|
|
171
250
|
}
|
|
172
|
-
let
|
|
173
|
-
if(
|
|
174
|
-
|
|
175
|
-
if(entityResult){
|
|
176
|
-
Logger.debug('Found entity for path segments: '+pathSegments.join('/'));
|
|
177
|
-
return await this.renderContentFromEntity(
|
|
178
|
-
res,
|
|
179
|
-
entityResult.entity,
|
|
180
|
-
entityResult.entityName,
|
|
181
|
-
domain
|
|
182
|
-
);
|
|
183
|
-
}
|
|
251
|
+
let entityResult = await this.findEntityByPath(path);
|
|
252
|
+
if(entityResult){
|
|
253
|
+
return await this.renderEntity(entityResult, domain, res);
|
|
184
254
|
}
|
|
185
255
|
let templatePath = this.findTemplateByPath(path, domain);
|
|
186
256
|
if(templatePath){
|
|
187
|
-
|
|
188
|
-
return await this.renderTemplateOnly(res, templatePath, domain);
|
|
257
|
+
return await this.renderTemplate(templatePath, domain, res);
|
|
189
258
|
}
|
|
190
|
-
|
|
191
|
-
return await this.renderNotFoundPage(res, domain);
|
|
259
|
+
return await this.renderNotFound(domain, res);
|
|
192
260
|
} catch (error) {
|
|
193
261
|
Logger.error('Request handling error: '+error.message);
|
|
194
262
|
return res.status(500).send('Internal server error');
|
|
@@ -212,32 +280,34 @@ class Frontend
|
|
|
212
280
|
return false;
|
|
213
281
|
}
|
|
214
282
|
|
|
215
|
-
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)
|
|
216
292
|
{
|
|
217
|
-
|
|
218
|
-
|
|
293
|
+
let pathSegments = path.split('/').filter(segment => '' !== segment);
|
|
294
|
+
if(2 > pathSegments.length){
|
|
219
295
|
return false;
|
|
220
296
|
}
|
|
221
297
|
let entityName = pathSegments[0];
|
|
222
|
-
|
|
223
|
-
if(!entityId){
|
|
224
|
-
Logger.debug('No entity ID in path segments');
|
|
298
|
+
if(!await this.isEntityAccessible(entityName)){
|
|
225
299
|
return false;
|
|
226
300
|
}
|
|
301
|
+
let entityId = pathSegments[1];
|
|
227
302
|
let entity = this.dataServer.getEntity(entityName);
|
|
228
303
|
if(!entity){
|
|
229
|
-
Logger.debug('Entity not found: '+entityName);
|
|
230
304
|
return false;
|
|
231
305
|
}
|
|
232
306
|
let loadedEntity = await entity.loadById(entityId);
|
|
233
307
|
if(!loadedEntity){
|
|
234
|
-
Logger.debug('Entity not loaded by ID: '+entityId);
|
|
235
308
|
return false;
|
|
236
309
|
}
|
|
237
|
-
return {
|
|
238
|
-
entity: loadedEntity,
|
|
239
|
-
entityName
|
|
240
|
-
};
|
|
310
|
+
return {entity: loadedEntity, entityName};
|
|
241
311
|
}
|
|
242
312
|
|
|
243
313
|
findTemplateByPath(path, domain)
|
|
@@ -245,101 +315,155 @@ class Frontend
|
|
|
245
315
|
if('/' === path){
|
|
246
316
|
path = '/index';
|
|
247
317
|
}
|
|
248
|
-
let templatePath = path.endsWith('/')
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
? templatePath.substring(1)
|
|
253
|
-
: templatePath;
|
|
254
|
-
let fullPath = this.findTemplatePathForDomain(templatePath, domain);
|
|
255
|
-
if(FileHandler.exists(fullPath)){
|
|
256
|
-
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;
|
|
257
322
|
}
|
|
258
|
-
return
|
|
323
|
+
return this.findTemplatePath(templatePath, domain);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async renderEnhanced(template, data, partials)
|
|
327
|
+
{
|
|
328
|
+
return this.renderEngine.render(
|
|
329
|
+
await this.processCustomTemplateFunctions(template),
|
|
330
|
+
data,
|
|
331
|
+
partials
|
|
332
|
+
);
|
|
259
333
|
}
|
|
260
334
|
|
|
261
|
-
async
|
|
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)
|
|
262
363
|
{
|
|
263
364
|
if(!route.router || !route.content_id){
|
|
264
|
-
|
|
265
|
-
return await this.renderNotFoundPage(res, domain);
|
|
365
|
+
return await this.renderNotFound(domain, res);
|
|
266
366
|
}
|
|
267
367
|
let entity = this.dataServer.getEntity(route.router);
|
|
268
368
|
if(!entity){
|
|
269
|
-
|
|
270
|
-
return await this.renderNotFoundPage(res, domain);
|
|
369
|
+
return await this.renderNotFound(domain, res);
|
|
271
370
|
}
|
|
272
371
|
let content = await entity.loadById(route.content_id);
|
|
273
372
|
if(!content){
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
373
|
+
return await this.renderNotFound(domain, res);
|
|
374
|
+
}
|
|
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
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async renderEntity(entityResult, domain, res)
|
|
385
|
+
{
|
|
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);
|
|
285
401
|
}
|
|
286
|
-
|
|
287
|
-
let data = {
|
|
288
|
-
...route,
|
|
289
|
-
...content,
|
|
290
|
-
current_year: new Date().getFullYear()
|
|
291
|
-
};
|
|
292
|
-
let partials = this.getPartialsForDomain(domain);
|
|
293
|
-
let rendered = mustache.render(template, data, partials);
|
|
294
|
-
return res.send(rendered);
|
|
402
|
+
return await this.renderWithLayout({content}, data, 'default', domain, res);
|
|
295
403
|
}
|
|
296
404
|
|
|
297
|
-
async
|
|
405
|
+
async renderContentWithTemplate(templatePath, data, domain)
|
|
298
406
|
{
|
|
299
|
-
let
|
|
300
|
-
if(!
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return await this.renderNotFoundPage(res, domain);
|
|
304
|
-
}
|
|
407
|
+
let template = FileHandler.readFile(templatePath);
|
|
408
|
+
if(!template){
|
|
409
|
+
Logger.error('Failed to read template: ' + templatePath);
|
|
410
|
+
return false;
|
|
305
411
|
}
|
|
306
|
-
|
|
307
|
-
let data = {
|
|
308
|
-
...entity,
|
|
309
|
-
title: entity.title || entity.name || entityName,
|
|
310
|
-
current_year: new Date().getFullYear()
|
|
311
|
-
};
|
|
312
|
-
let partials = this.getPartialsForDomain(domain);
|
|
313
|
-
let rendered = mustache.render(template, data, partials);
|
|
314
|
-
return res.send(rendered);
|
|
412
|
+
return await this.renderEnhanced(template, data, this.getPartialsForDomain(domain));
|
|
315
413
|
}
|
|
316
414
|
|
|
317
|
-
async
|
|
415
|
+
async renderWithLayout(content, data, layoutName, domain, res)
|
|
318
416
|
{
|
|
319
|
-
let
|
|
320
|
-
let
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
+
);
|
|
327
452
|
}
|
|
328
453
|
|
|
329
|
-
async
|
|
454
|
+
async renderNotFound(domain, res)
|
|
330
455
|
{
|
|
331
|
-
let templatePath = this.
|
|
332
|
-
if(!
|
|
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){
|
|
333
463
|
return res.status(404).send('Page not found');
|
|
334
464
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
title: '404 - Page Not Found',
|
|
338
|
-
current_year: new Date().getFullYear()
|
|
339
|
-
};
|
|
340
|
-
let partials = this.getPartialsForDomain(domain);
|
|
341
|
-
let rendered = mustache.render(template, data, partials);
|
|
342
|
-
return res.status(404).send(rendered);
|
|
465
|
+
res.status(404);
|
|
466
|
+
return await this.renderWithLayout({content}, data, 'default', domain, res);
|
|
343
467
|
}
|
|
344
468
|
|
|
345
469
|
}
|