@reldens/cms 0.18.0 → 0.20.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 +260 -20
- package/admin/reldens-admin-client.css +127 -80
- package/admin/reldens-admin-client.js +24 -0
- package/admin/templates/clear-all-cache-button.html +18 -0
- package/lib/admin-manager/contents-builder.js +7 -6
- package/lib/admin-manager/router-contents.js +58 -16
- package/lib/admin-manager-validator.js +2 -1
- package/lib/admin-manager.js +4 -2
- package/lib/admin-translations.js +9 -1
- package/lib/cache/add-cache-button-subscriber.js +53 -5
- package/lib/cache/cache-manager.js +47 -8
- package/lib/cache/cache-routes-handler.js +23 -0
- package/lib/cms-pages-route-manager.js +16 -4
- package/lib/frontend.js +310 -119
- package/lib/manager.js +43 -3
- package/lib/pagination-handler.js +243 -0
- package/lib/search-renderer.js +116 -0
- package/lib/search.js +344 -0
- package/lib/template-engine/asset-transformer.js +41 -0
- package/lib/template-engine/collections-single-transformer.js +70 -0
- package/lib/template-engine/collections-transformer-base.js +84 -0
- package/lib/template-engine/collections-transformer.js +374 -0
- package/lib/template-engine/date-transformer.js +53 -0
- package/lib/template-engine/entities-transformer.js +67 -0
- package/lib/template-engine/partials-transformer.js +175 -0
- package/lib/template-engine/system-variables-provider.js +105 -0
- package/lib/template-engine/translate-transformer.js +98 -0
- package/lib/template-engine/translation-service.js +104 -0
- package/lib/template-engine/url-transformer.js +41 -0
- package/lib/template-engine.js +133 -438
- package/lib/templates-list.js +1 -0
- package/migrations/install.sql +18 -18
- package/package.json +4 -4
- package/templates/page.html +19 -2
- package/templates/partials/entriesListView.html +14 -0
- package/templates/partials/pagedCollection.html +33 -0
package/lib/frontend.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const { TemplateEngine } = require('./template-engine');
|
|
8
|
+
const { Search } = require('./search');
|
|
9
|
+
const { SearchRenderer } = require('./search-renderer');
|
|
7
10
|
const { FileHandler } = require('@reldens/server-utils');
|
|
8
11
|
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
-
const { TemplateEngine } = require('./template-engine');
|
|
10
12
|
|
|
11
13
|
class Frontend
|
|
12
14
|
{
|
|
@@ -17,6 +19,7 @@ class Frontend
|
|
|
17
19
|
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
18
20
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
19
21
|
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
22
|
+
this.events = sc.get(props, 'events', false);
|
|
20
23
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
21
24
|
this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
22
25
|
this.publicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
@@ -31,6 +34,24 @@ class Frontend
|
|
|
31
34
|
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
32
35
|
this.templateEngine = false;
|
|
33
36
|
this.cacheManager = sc.get(props, 'cacheManager', false);
|
|
37
|
+
this.searchPath = sc.get(props, 'searchPath', '/search');
|
|
38
|
+
this.searchSets = sc.get(props, 'searchSets', false);
|
|
39
|
+
this.searchConfig = {dataServer: this.dataServer};
|
|
40
|
+
if(this.searchSets){
|
|
41
|
+
this.searchConfig.searchSets = this.searchSets;
|
|
42
|
+
}
|
|
43
|
+
this.search = new Search(this.searchConfig);
|
|
44
|
+
this.searchRenderer = new SearchRenderer({
|
|
45
|
+
renderEngine: this.renderEngine,
|
|
46
|
+
getPartials: this.getPartialsForDomain.bind(this)
|
|
47
|
+
});
|
|
48
|
+
this.metaDefaults = sc.get(props, 'metaDefaults', {
|
|
49
|
+
locale: 'en',
|
|
50
|
+
viewport: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover',
|
|
51
|
+
meta_robots: 'index,follow',
|
|
52
|
+
meta_theme_color: '#000000',
|
|
53
|
+
meta_twitter_card_type: 'summary'
|
|
54
|
+
});
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
async initialize()
|
|
@@ -59,12 +80,20 @@ class Frontend
|
|
|
59
80
|
renderEngine: this.renderEngine,
|
|
60
81
|
dataServer: this.dataServer,
|
|
61
82
|
getPartials: this.getPartialsForDomain.bind(this),
|
|
62
|
-
entitiesConfig: this.entitiesConfig
|
|
83
|
+
entitiesConfig: this.entitiesConfig,
|
|
84
|
+
events: this.events,
|
|
85
|
+
defaultDomain: this.defaultDomain,
|
|
86
|
+
projectRoot: this.projectRoot,
|
|
87
|
+
publicPath: this.publicPath
|
|
63
88
|
});
|
|
89
|
+
this.searchConfig.jsonFieldsParser = this.templateEngine.jsonFieldsParser;
|
|
64
90
|
await this.loadPartials();
|
|
65
91
|
await this.setupDomainTemplates();
|
|
66
92
|
await this.loadEntityAccessRules();
|
|
67
93
|
this.setupStaticAssets();
|
|
94
|
+
this.app.get(this.searchPath, async (req, res) => {
|
|
95
|
+
return await this.handleSearchRequest(req, res);
|
|
96
|
+
});
|
|
68
97
|
this.app.get('*', async (req, res) => {
|
|
69
98
|
return await this.handleRequest(req, res);
|
|
70
99
|
});
|
|
@@ -249,31 +278,160 @@ class Frontend
|
|
|
249
278
|
return false;
|
|
250
279
|
}
|
|
251
280
|
|
|
281
|
+
fetchMetaFields(data)
|
|
282
|
+
{
|
|
283
|
+
if(!sc.isObject(data) || 0 === Object.keys(data).length){
|
|
284
|
+
return this.metaDefaults;
|
|
285
|
+
}
|
|
286
|
+
let result = Object.assign({}, this.metaDefaults);
|
|
287
|
+
for(let key of Object.keys(data)){
|
|
288
|
+
let value = data[key];
|
|
289
|
+
if(null !== value && '' !== value && 'undefined' !== typeof value){
|
|
290
|
+
result[key] = value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
let titleValue = sc.get(result, 'title', '');
|
|
294
|
+
let metaTitleValue = sc.get(result, 'meta_title', titleValue);
|
|
295
|
+
if(metaTitleValue && '' !== metaTitleValue){
|
|
296
|
+
result.meta_title = metaTitleValue;
|
|
297
|
+
}
|
|
298
|
+
let metaOgTitleValue = sc.get(result, 'meta_og_title', metaTitleValue);
|
|
299
|
+
if(metaOgTitleValue && '' !== metaOgTitleValue){
|
|
300
|
+
result.meta_og_title = metaOgTitleValue;
|
|
301
|
+
}
|
|
302
|
+
let metaDescValue = sc.get(result, 'meta_description', '');
|
|
303
|
+
let metaOgDescValue = sc.get(result, 'meta_og_description', metaDescValue);
|
|
304
|
+
if(metaOgDescValue && '' !== metaOgDescValue){
|
|
305
|
+
result.meta_og_description = metaOgDescValue;
|
|
306
|
+
}
|
|
307
|
+
let jsonData = sc.get(result, 'json_data', null);
|
|
308
|
+
if(sc.isString(jsonData)){
|
|
309
|
+
jsonData = sc.toJson(jsonData, {});
|
|
310
|
+
}
|
|
311
|
+
if(!sc.isObject(jsonData)){
|
|
312
|
+
jsonData = {};
|
|
313
|
+
}
|
|
314
|
+
let viewportValue = sc.get(jsonData, 'viewport', this.metaDefaults.viewport);
|
|
315
|
+
if(viewportValue && '' !== viewportValue){
|
|
316
|
+
jsonData.viewport = viewportValue;
|
|
317
|
+
}
|
|
318
|
+
result.json_data = jsonData;
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async handleSearchRequest(req, res)
|
|
323
|
+
{
|
|
324
|
+
try {
|
|
325
|
+
let domain = this.getDomainFromRequest(req);
|
|
326
|
+
let config = this.search.parseSearchParameters(req.query);
|
|
327
|
+
if(!config){
|
|
328
|
+
return res.redirect('/?error-message=searchInvalidParameters');
|
|
329
|
+
}
|
|
330
|
+
let cacheKey = this.buildCacheKey(req.path, req);
|
|
331
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
332
|
+
let cachedContent = await this.cacheManager.get(domain, cacheKey);
|
|
333
|
+
if(cachedContent){
|
|
334
|
+
return res.send(cachedContent);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
let searchResults = await this.search.executeSearch(config);
|
|
338
|
+
if(false === searchResults){
|
|
339
|
+
return res.redirect('/?error-message=searchExecutionFailed');
|
|
340
|
+
}
|
|
341
|
+
let content = await this.renderWithTemplateContent(
|
|
342
|
+
{
|
|
343
|
+
template: config.render.page,
|
|
344
|
+
layout: config.render.layout,
|
|
345
|
+
content: await this.searchRenderer.renderSearchResults(searchResults, config, domain, req)
|
|
346
|
+
},
|
|
347
|
+
Object.assign({}, {
|
|
348
|
+
search_query: sc.get(req.query, 'search', ''),
|
|
349
|
+
searchConfig: config,
|
|
350
|
+
query: req.query
|
|
351
|
+
}),
|
|
352
|
+
domain,
|
|
353
|
+
req,
|
|
354
|
+
null
|
|
355
|
+
);
|
|
356
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
357
|
+
await this.cacheManager.set(domain, cacheKey, content);
|
|
358
|
+
}
|
|
359
|
+
return res.send(content);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
Logger.error('Search request handling error: ' + error.message);
|
|
362
|
+
return res.redirect('/?error-message=searchError');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
normalizePathForRouteSearch(path)
|
|
367
|
+
{
|
|
368
|
+
if(!path || '/' === path){
|
|
369
|
+
return '/';
|
|
370
|
+
}
|
|
371
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async handleRouteRedirect(route, res)
|
|
375
|
+
{
|
|
376
|
+
let redirectUrl = sc.get(route, 'redirect_url', null);
|
|
377
|
+
if(!redirectUrl){
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
let redirectType = sc.get(route, 'redirect_type', '301');
|
|
381
|
+
let statusCode = '301' === redirectType ? 301 : 302;
|
|
382
|
+
return res.redirect(statusCode, redirectUrl);
|
|
383
|
+
}
|
|
384
|
+
|
|
252
385
|
async handleRequest(req, res)
|
|
253
386
|
{
|
|
254
387
|
try {
|
|
255
|
-
let
|
|
388
|
+
let originalPath = req.path;
|
|
256
389
|
let domain = this.getDomainFromRequest(req);
|
|
257
|
-
this.templateEngine.setCurrentDomain(domain);
|
|
258
390
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
259
|
-
let cachedContent = await this.cacheManager.get(domain,
|
|
391
|
+
let cachedContent = await this.cacheManager.get(domain, originalPath);
|
|
260
392
|
if(cachedContent){
|
|
261
393
|
return res.send(cachedContent);
|
|
262
394
|
}
|
|
263
395
|
}
|
|
264
|
-
let route = await this.findRouteByPath(
|
|
396
|
+
let route = await this.findRouteByPath(originalPath, domain);
|
|
265
397
|
if(route){
|
|
266
|
-
|
|
398
|
+
let redirectResult = await this.handleRouteRedirect(route, res);
|
|
399
|
+
if(redirectResult){
|
|
400
|
+
return redirectResult;
|
|
401
|
+
}
|
|
402
|
+
let normalizedPath = this.normalizePathForRouteSearch(originalPath);
|
|
403
|
+
let routeFoundWithSlash = originalPath !== normalizedPath && route.path === normalizedPath + '/';
|
|
404
|
+
if(!originalPath.endsWith('/') && routeFoundWithSlash){
|
|
405
|
+
return res.redirect(301, originalPath + '/');
|
|
406
|
+
}
|
|
407
|
+
return await this.renderRouteWithCache(route, domain, res, originalPath, req);
|
|
408
|
+
}
|
|
409
|
+
let normalizedPath = this.normalizePathForRouteSearch(originalPath);
|
|
410
|
+
if(originalPath !== normalizedPath){
|
|
411
|
+
let alternativeRoute = await this.findRouteByPath(normalizedPath + '/', domain);
|
|
412
|
+
if(alternativeRoute){
|
|
413
|
+
let redirectResult = await this.handleRouteRedirect(alternativeRoute, res);
|
|
414
|
+
if(redirectResult){
|
|
415
|
+
return redirectResult;
|
|
416
|
+
}
|
|
417
|
+
if(!originalPath.endsWith('/')){
|
|
418
|
+
return res.redirect(301, originalPath + '/');
|
|
419
|
+
}
|
|
420
|
+
return await this.renderRouteWithCache(alternativeRoute, domain, res, originalPath, req);
|
|
421
|
+
}
|
|
267
422
|
}
|
|
268
|
-
|
|
423
|
+
if(!originalPath.endsWith('/') && '/' !== originalPath){
|
|
424
|
+
return res.redirect(301, originalPath + '/');
|
|
425
|
+
}
|
|
426
|
+
let entityResult = await this.findEntityByPath(originalPath);
|
|
269
427
|
if(entityResult){
|
|
270
|
-
return await this.renderEntityWithCache(entityResult, domain, res,
|
|
428
|
+
return await this.renderEntityWithCache(entityResult, domain, res, originalPath, req);
|
|
271
429
|
}
|
|
272
|
-
let templatePath = this.findTemplateByPath(
|
|
430
|
+
let templatePath = this.findTemplateByPath(originalPath, domain);
|
|
273
431
|
if(templatePath){
|
|
274
|
-
return await this.renderTemplateWithCache(templatePath, domain, res,
|
|
432
|
+
return await this.renderTemplateWithCache(templatePath, domain, res, originalPath, req);
|
|
275
433
|
}
|
|
276
|
-
return await this.renderNotFound(domain, res);
|
|
434
|
+
return await this.renderNotFound(domain, res, req);
|
|
277
435
|
} catch (error) {
|
|
278
436
|
Logger.error('Request handling error: '+error.message);
|
|
279
437
|
return res.status(500).send('Internal server error');
|
|
@@ -287,8 +445,9 @@ class Frontend
|
|
|
287
445
|
Logger.error('Routes entity not found in dataServer.');
|
|
288
446
|
return false;
|
|
289
447
|
}
|
|
448
|
+
let normalizedPath = this.normalizePathForRouteSearch(path);
|
|
290
449
|
let domainFilter = domain || null;
|
|
291
|
-
let routeFilters = {path, enabled: 1};
|
|
450
|
+
let routeFilters = {path: normalizedPath, enabled: 1};
|
|
292
451
|
let routes = await routesEntity.load(routeFilters);
|
|
293
452
|
let matchingRoute = false;
|
|
294
453
|
let nullDomain = false;
|
|
@@ -304,7 +463,25 @@ class Frontend
|
|
|
304
463
|
if(matchingRoute){
|
|
305
464
|
return matchingRoute;
|
|
306
465
|
}
|
|
307
|
-
|
|
466
|
+
if(nullDomain){
|
|
467
|
+
return nullDomain;
|
|
468
|
+
}
|
|
469
|
+
if(normalizedPath !== path){
|
|
470
|
+
let routeFiltersWithSlash = {path: path, enabled: 1};
|
|
471
|
+
let routesWithSlash = await routesEntity.load(routeFiltersWithSlash);
|
|
472
|
+
for(let route of routesWithSlash){
|
|
473
|
+
if(route.domain === domainFilter){
|
|
474
|
+
return route;
|
|
475
|
+
}
|
|
476
|
+
if(!route.domain){
|
|
477
|
+
nullDomain = route;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if(nullDomain){
|
|
481
|
+
return nullDomain;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return false;
|
|
308
485
|
}
|
|
309
486
|
|
|
310
487
|
async isEntityAccessible(entityName)
|
|
@@ -350,57 +527,46 @@ class Frontend
|
|
|
350
527
|
return this.findTemplatePath(templatePath, domain);
|
|
351
528
|
}
|
|
352
529
|
|
|
353
|
-
async renderRouteWithCache(route, domain, res, path)
|
|
530
|
+
async renderRouteWithCache(route, domain, res, path, req)
|
|
354
531
|
{
|
|
355
|
-
let renderedContent = await this.generateRouteContent(route, domain);
|
|
532
|
+
let renderedContent = await this.generateRouteContent(route, domain, req);
|
|
356
533
|
if(!renderedContent){
|
|
357
|
-
return await this.renderNotFound(domain, res);
|
|
534
|
+
return await this.renderNotFound(domain, res, req);
|
|
358
535
|
}
|
|
359
536
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
360
|
-
|
|
537
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
538
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
361
539
|
}
|
|
362
540
|
return res.send(renderedContent);
|
|
363
541
|
}
|
|
364
542
|
|
|
365
|
-
async renderEntityWithCache(entityResult, domain, res, path)
|
|
543
|
+
async renderEntityWithCache(entityResult, domain, res, path, req)
|
|
366
544
|
{
|
|
367
|
-
let renderedContent = await this.generateEntityContent(entityResult, domain);
|
|
545
|
+
let renderedContent = await this.generateEntityContent(entityResult, domain, req);
|
|
368
546
|
if(!renderedContent){
|
|
369
|
-
return await this.renderNotFound(domain, res);
|
|
547
|
+
return await this.renderNotFound(domain, res, req);
|
|
370
548
|
}
|
|
371
549
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
372
|
-
|
|
550
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
551
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
373
552
|
}
|
|
374
553
|
return res.send(renderedContent);
|
|
375
554
|
}
|
|
376
555
|
|
|
377
|
-
async renderTemplateWithCache(templatePath, domain, res, path)
|
|
556
|
+
async renderTemplateWithCache(templatePath, domain, res, path, req)
|
|
378
557
|
{
|
|
379
|
-
let renderedContent = await this.generateTemplateContent(templatePath, domain);
|
|
558
|
+
let renderedContent = await this.generateTemplateContent(templatePath, domain, req);
|
|
380
559
|
if(!renderedContent){
|
|
381
560
|
return res.status(500).send('Template error: '+templatePath);
|
|
382
561
|
}
|
|
383
562
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
let layoutContent = await this.processContentWithLayout({content: renderedContent}, {}, 'default', domain);
|
|
387
|
-
let pagePath = this.findTemplatePath('page', domain);
|
|
388
|
-
if(!pagePath){
|
|
389
|
-
return res.send(layoutContent);
|
|
563
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
564
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
390
565
|
}
|
|
391
|
-
|
|
392
|
-
if(!pageTemplate){
|
|
393
|
-
return res.send(layoutContent);
|
|
394
|
-
}
|
|
395
|
-
let finalContent = await this.templateEngine.render(
|
|
396
|
-
pageTemplate,
|
|
397
|
-
Object.assign({}, {}, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
398
|
-
this.getPartialsForDomain(domain)
|
|
399
|
-
);
|
|
400
|
-
return res.send(finalContent);
|
|
566
|
+
return await this.renderWithLayout({content: renderedContent}, {}, 'default', domain, res, req);
|
|
401
567
|
}
|
|
402
568
|
|
|
403
|
-
async generateRouteContent(route, domain)
|
|
569
|
+
async generateRouteContent(route, domain, req)
|
|
404
570
|
{
|
|
405
571
|
if(!route.router){
|
|
406
572
|
return false;
|
|
@@ -413,78 +579,73 @@ class Frontend
|
|
|
413
579
|
if(!content){
|
|
414
580
|
return false;
|
|
415
581
|
}
|
|
416
|
-
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain);
|
|
582
|
+
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain, req, route);
|
|
417
583
|
}
|
|
418
584
|
|
|
419
|
-
async generateEntityContent(entityResult, domain)
|
|
585
|
+
async generateEntityContent(entityResult, domain, req)
|
|
420
586
|
{
|
|
421
|
-
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain);
|
|
587
|
+
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain, req, null);
|
|
422
588
|
}
|
|
423
589
|
|
|
424
|
-
async generateTemplateContent(templatePath, domain)
|
|
590
|
+
async generateTemplateContent(templatePath, domain, req)
|
|
425
591
|
{
|
|
426
592
|
let template = FileHandler.readFile(templatePath);
|
|
427
593
|
if(!template){
|
|
428
594
|
Logger.error('Failed to read template: ' + templatePath);
|
|
429
595
|
return false;
|
|
430
596
|
}
|
|
431
|
-
return await this.templateEngine.render(
|
|
597
|
+
return await this.templateEngine.render(
|
|
598
|
+
template,
|
|
599
|
+
{},
|
|
600
|
+
this.getPartialsForDomain(domain),
|
|
601
|
+
domain,
|
|
602
|
+
req,
|
|
603
|
+
null,
|
|
604
|
+
null
|
|
605
|
+
);
|
|
432
606
|
}
|
|
433
607
|
|
|
434
|
-
async renderRoute(route, domain, res)
|
|
608
|
+
async renderRoute(route, domain, res, req)
|
|
435
609
|
{
|
|
436
610
|
if(!route.router){
|
|
437
|
-
return await this.renderNotFound(domain, res);
|
|
611
|
+
return await this.renderNotFound(domain, res, req);
|
|
438
612
|
}
|
|
439
613
|
let entity = this.dataServer.getEntity(route.router);
|
|
440
614
|
if(!entity){
|
|
441
|
-
return await this.renderNotFound(domain, res);
|
|
615
|
+
return await this.renderNotFound(domain, res, req);
|
|
442
616
|
}
|
|
443
617
|
let content = await entity.loadOne({route_id: route.id});
|
|
444
618
|
if(!content){
|
|
445
|
-
return await this.renderNotFound(domain, res);
|
|
619
|
+
return await this.renderNotFound(domain, res, req);
|
|
446
620
|
}
|
|
447
|
-
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res);
|
|
621
|
+
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res, req);
|
|
448
622
|
}
|
|
449
623
|
|
|
450
|
-
async
|
|
624
|
+
async renderWithTemplate(content, data, domain, res, req)
|
|
451
625
|
{
|
|
452
|
-
return await this.
|
|
626
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req, null));
|
|
453
627
|
}
|
|
454
628
|
|
|
455
|
-
async
|
|
629
|
+
async renderWithTemplateContent(content, data, domain, req, route)
|
|
456
630
|
{
|
|
457
|
-
let templateName = sc.get(content, 'template', '');
|
|
458
|
-
let layoutName = sc.get(content, 'layout', 'default');
|
|
459
|
-
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
|
|
631
|
+
let templateName = sc.get(content, 'template', 'page');
|
|
460
632
|
if(!templateName){
|
|
461
633
|
templateName = 'page';
|
|
462
634
|
}
|
|
463
|
-
let
|
|
464
|
-
if(!
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
let
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
)
|
|
635
|
+
let layoutName = sc.get(content, 'layout', '');
|
|
636
|
+
if(!layoutName){
|
|
637
|
+
layoutName = 'default';
|
|
638
|
+
}
|
|
639
|
+
let currentEntityData = Object.assign({}, content, data);
|
|
640
|
+
let layoutContent = await this.processContentWithLayout(
|
|
641
|
+
content,
|
|
642
|
+
data,
|
|
643
|
+
layoutName,
|
|
644
|
+
domain,
|
|
645
|
+
req,
|
|
646
|
+
route,
|
|
647
|
+
currentEntityData
|
|
477
648
|
);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async renderWithTemplateContent(content, data, domain)
|
|
481
|
-
{
|
|
482
|
-
let templateName = sc.get(content, 'template', '');
|
|
483
|
-
let layoutName = sc.get(content, 'layout', 'default');
|
|
484
|
-
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
|
|
485
|
-
if(!templateName){
|
|
486
|
-
templateName = 'page';
|
|
487
|
-
}
|
|
488
649
|
let templatePath = this.findTemplatePath(templateName, domain);
|
|
489
650
|
if(!templatePath){
|
|
490
651
|
return layoutContent;
|
|
@@ -495,53 +656,48 @@ class Frontend
|
|
|
495
656
|
}
|
|
496
657
|
return await this.templateEngine.render(
|
|
497
658
|
pageTemplate,
|
|
498
|
-
Object.assign(
|
|
499
|
-
|
|
659
|
+
Object.assign(
|
|
660
|
+
{},
|
|
661
|
+
this.fetchMetaFields(data),
|
|
662
|
+
{
|
|
663
|
+
content: layoutContent,
|
|
664
|
+
siteHandle: this.resolveDomainToSiteKey(domain)
|
|
665
|
+
}
|
|
666
|
+
),
|
|
667
|
+
this.getPartialsForDomain(domain),
|
|
668
|
+
domain,
|
|
669
|
+
req,
|
|
670
|
+
route,
|
|
671
|
+
currentEntityData
|
|
500
672
|
);
|
|
501
673
|
}
|
|
502
674
|
|
|
503
|
-
async
|
|
504
|
-
{
|
|
505
|
-
let content = await this.renderContentWithTemplate(templatePath, {}, domain);
|
|
506
|
-
if(!content){
|
|
507
|
-
return res.status(500).send('Template error: '+templatePath);
|
|
508
|
-
}
|
|
509
|
-
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
async renderContentWithTemplate(templatePath, data, domain)
|
|
675
|
+
async renderContentWithTemplate(templatePath, data, domain, req)
|
|
513
676
|
{
|
|
514
677
|
let template = FileHandler.readFile(templatePath);
|
|
515
678
|
if(!template){
|
|
516
679
|
Logger.error('Failed to read template: ' + templatePath);
|
|
517
680
|
return false;
|
|
518
681
|
}
|
|
519
|
-
return await this.templateEngine.render(
|
|
682
|
+
return await this.templateEngine.render(
|
|
683
|
+
template,
|
|
684
|
+
data,
|
|
685
|
+
this.getPartialsForDomain(domain),
|
|
686
|
+
domain,
|
|
687
|
+
req,
|
|
688
|
+
null,
|
|
689
|
+
null
|
|
690
|
+
);
|
|
520
691
|
}
|
|
521
692
|
|
|
522
|
-
async renderWithLayout(content, data, layoutName, domain, res)
|
|
693
|
+
async renderWithLayout(content, data, layoutName, domain, res, req)
|
|
523
694
|
{
|
|
524
|
-
|
|
525
|
-
let pagePath = this.findTemplatePath('page', domain);
|
|
526
|
-
if(!pagePath){
|
|
527
|
-
return res.send(layoutContent);
|
|
528
|
-
}
|
|
529
|
-
let pageTemplate = FileHandler.readFile(pagePath);
|
|
530
|
-
if(!pageTemplate){
|
|
531
|
-
return res.send(layoutContent);
|
|
532
|
-
}
|
|
533
|
-
return res.send(
|
|
534
|
-
await this.templateEngine.render(
|
|
535
|
-
pageTemplate,
|
|
536
|
-
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
537
|
-
this.getPartialsForDomain(domain)
|
|
538
|
-
)
|
|
539
|
-
);
|
|
695
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req, null));
|
|
540
696
|
}
|
|
541
697
|
|
|
542
|
-
async processContentWithLayout(content, data, layoutName, domain)
|
|
698
|
+
async processContentWithLayout(content, data, layoutName, domain, req, route, currentEntityData)
|
|
543
699
|
{
|
|
544
|
-
let processedContent = await this.processContent(content, data, domain);
|
|
700
|
+
let processedContent = await this.processContent(content, data, domain, req, route, currentEntityData);
|
|
545
701
|
let layoutPath = this.findLayoutPath(layoutName, domain);
|
|
546
702
|
if(!layoutPath){
|
|
547
703
|
return processedContent;
|
|
@@ -553,32 +709,67 @@ class Frontend
|
|
|
553
709
|
return await this.templateEngine.render(
|
|
554
710
|
layoutTemplate,
|
|
555
711
|
Object.assign({}, data, {content: processedContent}),
|
|
556
|
-
this.getPartialsForDomain(domain)
|
|
712
|
+
this.getPartialsForDomain(domain),
|
|
713
|
+
domain,
|
|
714
|
+
req,
|
|
715
|
+
route,
|
|
716
|
+
currentEntityData
|
|
557
717
|
);
|
|
558
718
|
}
|
|
559
719
|
|
|
560
|
-
async processContent(content, data, domain)
|
|
720
|
+
async processContent(content, data, domain, req, route, currentEntityData)
|
|
561
721
|
{
|
|
562
722
|
let contentText = sc.get(content, 'content', '');
|
|
563
723
|
if(!contentText){
|
|
564
724
|
return '';
|
|
565
725
|
}
|
|
566
|
-
return await this.templateEngine.render(
|
|
726
|
+
return await this.templateEngine.render(
|
|
727
|
+
contentText,
|
|
728
|
+
data,
|
|
729
|
+
this.getPartialsForDomain(domain),
|
|
730
|
+
domain,
|
|
731
|
+
req,
|
|
732
|
+
route,
|
|
733
|
+
currentEntityData
|
|
734
|
+
);
|
|
567
735
|
}
|
|
568
736
|
|
|
569
|
-
async renderNotFound(domain, res)
|
|
737
|
+
async renderNotFound(domain, res, req)
|
|
570
738
|
{
|
|
571
739
|
let notFoundPath = this.findTemplatePath('404', domain);
|
|
572
740
|
if(notFoundPath){
|
|
573
|
-
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain);
|
|
741
|
+
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain, req);
|
|
574
742
|
if(content){
|
|
575
743
|
res.status(404);
|
|
576
|
-
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
744
|
+
return await this.renderWithLayout({content}, {}, 'default', domain, res, req);
|
|
577
745
|
}
|
|
578
746
|
}
|
|
579
747
|
return res.status(404).send('Page not found');
|
|
580
748
|
}
|
|
581
749
|
|
|
750
|
+
buildCacheKey(path, req)
|
|
751
|
+
{
|
|
752
|
+
if(!req || !req.query){
|
|
753
|
+
return path;
|
|
754
|
+
}
|
|
755
|
+
for(let key of Object.keys(req.query)){
|
|
756
|
+
if(key.endsWith('-key')){
|
|
757
|
+
let queryString = '';
|
|
758
|
+
for(let qKey of Object.keys(req.query)){
|
|
759
|
+
queryString += (queryString ? '&' : '') + qKey + '=' + req.query[qKey];
|
|
760
|
+
}
|
|
761
|
+
let hash = 0;
|
|
762
|
+
for(let i = 0; i < queryString.length; i++){
|
|
763
|
+
let char = queryString.charCodeAt(i);
|
|
764
|
+
hash = ((hash << 5) - hash) + char;
|
|
765
|
+
hash = hash & hash;
|
|
766
|
+
}
|
|
767
|
+
return path + '_' + Math.abs(hash);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return path;
|
|
771
|
+
}
|
|
772
|
+
|
|
582
773
|
}
|
|
583
774
|
|
|
584
775
|
module.exports.Frontend = Frontend;
|