@reldens/cms 0.18.0 → 0.19.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 +92 -9
- package/admin/reldens-admin-client.css +46 -2
- 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 +188 -111
- package/lib/manager.js +42 -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/collections-single-transformer.js +53 -0
- package/lib/template-engine/collections-transformer-base.js +84 -0
- package/lib/template-engine/collections-transformer.js +353 -0
- package/lib/template-engine/entities-transformer.js +65 -0
- package/lib/template-engine/partials-transformer.js +171 -0
- package/lib/template-engine.js +49 -435
- 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
|
@@ -67,17 +67,53 @@ class CacheManager
|
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
findAllCacheFilesForPath(domain, path)
|
|
71
|
+
{
|
|
72
|
+
let cacheInfo = this.generateCacheKey(domain, path);
|
|
73
|
+
if(!FileHandler.exists(cacheInfo.folderPath)){
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
let allFiles = FileHandler.readFolder(cacheInfo.folderPath);
|
|
77
|
+
if(0 === allFiles.length){
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
let baseFileName = cacheInfo.fileName.replace('.html', '');
|
|
81
|
+
let matchingFiles = [];
|
|
82
|
+
for(let file of allFiles){
|
|
83
|
+
if(file === cacheInfo.fileName){
|
|
84
|
+
matchingFiles.push(FileHandler.joinPaths(cacheInfo.folderPath, file));
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if(file.startsWith(baseFileName + '_') && file.endsWith('.html')){
|
|
88
|
+
matchingFiles.push(FileHandler.joinPaths(cacheInfo.folderPath, file));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return matchingFiles;
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
async delete(domain, path)
|
|
71
95
|
{
|
|
72
96
|
let cacheByDomains = this.fetchCachePathsByDomain(domain, path);
|
|
73
97
|
for(let cacheInfo of cacheByDomains){
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
98
|
+
let allCacheFiles = this.findAllCacheFilesForPath(cacheInfo.domain || domain, path);
|
|
99
|
+
if(0 === allCacheFiles.length){
|
|
100
|
+
let singleCacheInfo = this.generateCacheKey(cacheInfo.domain || domain, path);
|
|
101
|
+
if(!FileHandler.exists(singleCacheInfo.fullPath)){
|
|
102
|
+
Logger.debug('No cache files found for: '+path);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
allCacheFiles = [singleCacheInfo.fullPath];
|
|
77
106
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
for(let cacheFilePath of allCacheFiles){
|
|
108
|
+
if(!FileHandler.exists(cacheFilePath)){
|
|
109
|
+
Logger.debug('File does not exist: '+cacheFilePath);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if(!FileHandler.remove(cacheFilePath)){
|
|
113
|
+
Logger.error('Failed to delete cache file: '+cacheFilePath);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
Logger.debug('Deleted cache file: '+cacheFilePath);
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
return true;
|
|
@@ -87,12 +123,15 @@ class CacheManager
|
|
|
87
123
|
{
|
|
88
124
|
let cacheByDomains = [];
|
|
89
125
|
if(domain && 'default' !== domain){
|
|
90
|
-
cacheByDomains.push(
|
|
126
|
+
cacheByDomains.push({domain: domain});
|
|
127
|
+
return cacheByDomains;
|
|
128
|
+
}
|
|
129
|
+
if(!FileHandler.exists(this.cacheBasePath)){
|
|
91
130
|
return cacheByDomains;
|
|
92
131
|
}
|
|
93
132
|
let cachedDomainFolders = FileHandler.readFolder(this.cacheBasePath);
|
|
94
133
|
for(let cachedDomainFolder of cachedDomainFolders) {
|
|
95
|
-
cacheByDomains.push(
|
|
134
|
+
cacheByDomains.push({domain: cachedDomainFolder});
|
|
96
135
|
}
|
|
97
136
|
return cacheByDomains;
|
|
98
137
|
}
|
|
@@ -18,6 +18,8 @@ class CacheRoutesHandler
|
|
|
18
18
|
this.rootPath = sc.get(props, 'rootPath', '');
|
|
19
19
|
this.cacheCleanPath = '/cache-clean';
|
|
20
20
|
this.cacheCleanRoute = this.rootPath+this.cacheCleanPath;
|
|
21
|
+
this.clearAllCachePath = '/cache-clear-all';
|
|
22
|
+
this.clearAllCacheRoute = this.rootPath+this.clearAllCachePath;
|
|
21
23
|
this.setupRoutes();
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -42,6 +44,13 @@ class CacheRoutesHandler
|
|
|
42
44
|
return await this.processCacheClean(req, res);
|
|
43
45
|
}
|
|
44
46
|
);
|
|
47
|
+
this.router.adminRouter.post(
|
|
48
|
+
this.clearAllCachePath,
|
|
49
|
+
this.router.isAuthenticated.bind(this.router),
|
|
50
|
+
async (req, res) => {
|
|
51
|
+
return await this.processClearAllCache(req, res);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
45
54
|
return true;
|
|
46
55
|
}
|
|
47
56
|
|
|
@@ -71,6 +80,20 @@ class CacheRoutesHandler
|
|
|
71
80
|
return res.redirect(this.rootPath+'/routes/view'+'?id='+routeId+'&result=success');
|
|
72
81
|
}
|
|
73
82
|
|
|
83
|
+
async processClearAllCache(req, res)
|
|
84
|
+
{
|
|
85
|
+
if(!this.cacheManager){
|
|
86
|
+
return res.json({error: 'Cache manager not available'});
|
|
87
|
+
}
|
|
88
|
+
let clearResult = await this.cacheManager.clear();
|
|
89
|
+
if(!clearResult){
|
|
90
|
+
Logger.error('Failed to clear all cache');
|
|
91
|
+
return res.redirect(this.rootPath+'/routes?result=errorClearAllCache');
|
|
92
|
+
}
|
|
93
|
+
Logger.info('All cache cleared successfully');
|
|
94
|
+
return res.redirect(this.rootPath+'/routes?result=success');
|
|
95
|
+
}
|
|
96
|
+
|
|
74
97
|
}
|
|
75
98
|
|
|
76
99
|
module.exports.CacheRoutesHandler = CacheRoutesHandler;
|
|
@@ -76,9 +76,14 @@ class CmsPagesRouteManager
|
|
|
76
76
|
Logger.error('Routes repository not found.');
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
+
let path = sc.get(req.body, 'routePath', this.generateDefaultRoutePath(entityData));
|
|
80
|
+
if(!path || '' === path.trim()){
|
|
81
|
+
Logger.debug('No valid path available for CMS page route creation. Skipping route management.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
79
84
|
let cmsPageId = Number(entityData.id);
|
|
80
85
|
let routePatchData = {
|
|
81
|
-
path
|
|
86
|
+
path,
|
|
82
87
|
router: sc.get(req.body, 'routeRouter', 'cmsPages'),
|
|
83
88
|
cache_ttl_seconds: Number(sc.get(req.body, 'routeCacheTtl', 3600)),
|
|
84
89
|
enabled: Number(sc.get(req.body, 'routeEnabled', 1)),
|
|
@@ -91,9 +96,12 @@ class CmsPagesRouteManager
|
|
|
91
96
|
if(!routeResult){
|
|
92
97
|
routeResult = await routesRepository.create(routePatchData);
|
|
93
98
|
if(routeResult){
|
|
94
|
-
let pagesRepository = this.dataServer.getEntity('
|
|
99
|
+
let pagesRepository = this.dataServer.getEntity('cmsPages');
|
|
95
100
|
if(pagesRepository){
|
|
96
|
-
await pagesRepository.updateById(cmsPageId, {route_id: routeResult.id});
|
|
101
|
+
let pageResult = await pagesRepository.updateById(cmsPageId, {route_id: routeResult.id});
|
|
102
|
+
if(!pageResult){
|
|
103
|
+
Logger.error('Page could not be updated with route ID.', routeResult);
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
}
|
|
@@ -105,7 +113,11 @@ class CmsPagesRouteManager
|
|
|
105
113
|
|
|
106
114
|
generateDefaultRoutePath(pageData)
|
|
107
115
|
{
|
|
108
|
-
|
|
116
|
+
let title = sc.get(pageData, 'title', '');
|
|
117
|
+
if(!title || '' === title.trim()){
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
return '/' + title.toLowerCase()
|
|
109
121
|
.replace(/[^a-z0-9\s-]/g, '')
|
|
110
122
|
.replace(/\s+/g, '-')
|
|
111
123
|
.replace(/-+/g, '-')
|
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
|
{
|
|
@@ -31,6 +33,24 @@ class Frontend
|
|
|
31
33
|
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
32
34
|
this.templateEngine = false;
|
|
33
35
|
this.cacheManager = sc.get(props, 'cacheManager', false);
|
|
36
|
+
this.searchPath = sc.get(props, 'searchPath', '/search');
|
|
37
|
+
this.searchSets = sc.get(props, 'searchSets', false);
|
|
38
|
+
this.searchConfig = {dataServer: this.dataServer};
|
|
39
|
+
if(this.searchSets){
|
|
40
|
+
this.searchConfig.searchSets = this.searchSets;
|
|
41
|
+
}
|
|
42
|
+
this.search = new Search(this.searchConfig);
|
|
43
|
+
this.searchRenderer = new SearchRenderer({
|
|
44
|
+
renderEngine: this.renderEngine,
|
|
45
|
+
getPartials: this.getPartialsForDomain.bind(this)
|
|
46
|
+
});
|
|
47
|
+
this.metaDefaults = sc.get(props, 'metaDefaults', {
|
|
48
|
+
locale: 'en',
|
|
49
|
+
viewport: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover',
|
|
50
|
+
meta_robots: 'index,follow',
|
|
51
|
+
meta_theme_color: '#000000',
|
|
52
|
+
meta_twitter_card_type: 'summary'
|
|
53
|
+
});
|
|
34
54
|
}
|
|
35
55
|
|
|
36
56
|
async initialize()
|
|
@@ -61,10 +81,14 @@ class Frontend
|
|
|
61
81
|
getPartials: this.getPartialsForDomain.bind(this),
|
|
62
82
|
entitiesConfig: this.entitiesConfig
|
|
63
83
|
});
|
|
84
|
+
this.searchConfig.jsonFieldsParser = this.templateEngine.jsonFieldsParser;
|
|
64
85
|
await this.loadPartials();
|
|
65
86
|
await this.setupDomainTemplates();
|
|
66
87
|
await this.loadEntityAccessRules();
|
|
67
88
|
this.setupStaticAssets();
|
|
89
|
+
this.app.get(this.searchPath, async (req, res) => {
|
|
90
|
+
return await this.handleSearchRequest(req, res);
|
|
91
|
+
});
|
|
68
92
|
this.app.get('*', async (req, res) => {
|
|
69
93
|
return await this.handleRequest(req, res);
|
|
70
94
|
});
|
|
@@ -249,12 +273,95 @@ class Frontend
|
|
|
249
273
|
return false;
|
|
250
274
|
}
|
|
251
275
|
|
|
276
|
+
fetchMetaFields(data)
|
|
277
|
+
{
|
|
278
|
+
if(!sc.isObject(data) || 0 === Object.keys(data).length){
|
|
279
|
+
return this.metaDefaults;
|
|
280
|
+
}
|
|
281
|
+
let result = Object.assign({}, this.metaDefaults);
|
|
282
|
+
for(let key of Object.keys(data)){
|
|
283
|
+
let value = data[key];
|
|
284
|
+
if(null !== value && '' !== value && 'undefined' !== typeof value){
|
|
285
|
+
result[key] = value;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let titleValue = sc.get(result, 'title', '');
|
|
289
|
+
let metaTitleValue = sc.get(result, 'meta_title', titleValue);
|
|
290
|
+
if(metaTitleValue && '' !== metaTitleValue){
|
|
291
|
+
result.meta_title = metaTitleValue;
|
|
292
|
+
}
|
|
293
|
+
let metaOgTitleValue = sc.get(result, 'meta_og_title', metaTitleValue);
|
|
294
|
+
if(metaOgTitleValue && '' !== metaOgTitleValue){
|
|
295
|
+
result.meta_og_title = metaOgTitleValue;
|
|
296
|
+
}
|
|
297
|
+
let metaDescValue = sc.get(result, 'meta_description', '');
|
|
298
|
+
let metaOgDescValue = sc.get(result, 'meta_og_description', metaDescValue);
|
|
299
|
+
if(metaOgDescValue && '' !== metaOgDescValue){
|
|
300
|
+
result.meta_og_description = metaOgDescValue;
|
|
301
|
+
}
|
|
302
|
+
let jsonData = sc.get(result, 'json_data', null);
|
|
303
|
+
if(sc.isString(jsonData)){
|
|
304
|
+
jsonData = sc.toJson(jsonData, {});
|
|
305
|
+
}
|
|
306
|
+
if(!sc.isObject(jsonData)){
|
|
307
|
+
jsonData = {};
|
|
308
|
+
}
|
|
309
|
+
let viewportValue = sc.get(jsonData, 'viewport', this.metaDefaults.viewport);
|
|
310
|
+
if(viewportValue && '' !== viewportValue){
|
|
311
|
+
jsonData.viewport = viewportValue;
|
|
312
|
+
}
|
|
313
|
+
result.json_data = jsonData;
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async handleSearchRequest(req, res)
|
|
318
|
+
{
|
|
319
|
+
try {
|
|
320
|
+
let domain = this.getDomainFromRequest(req);
|
|
321
|
+
let config = this.search.parseSearchParameters(req.query);
|
|
322
|
+
if(!config){
|
|
323
|
+
return res.redirect('/?error-message=searchInvalidParameters');
|
|
324
|
+
}
|
|
325
|
+
let cacheKey = this.buildCacheKey(req.path, req);
|
|
326
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
327
|
+
let cachedContent = await this.cacheManager.get(domain, cacheKey);
|
|
328
|
+
if(cachedContent){
|
|
329
|
+
return res.send(cachedContent);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let searchResults = await this.search.executeSearch(config);
|
|
333
|
+
if(false === searchResults){
|
|
334
|
+
return res.redirect('/?error-message=searchExecutionFailed');
|
|
335
|
+
}
|
|
336
|
+
let content = await this.renderWithTemplateContent(
|
|
337
|
+
{
|
|
338
|
+
template: config.render.page,
|
|
339
|
+
layout: config.render.layout,
|
|
340
|
+
content: await this.searchRenderer.renderSearchResults(searchResults, config, domain, req)
|
|
341
|
+
},
|
|
342
|
+
Object.assign({}, {
|
|
343
|
+
search_query: sc.get(req.query, 'search', ''),
|
|
344
|
+
searchConfig: config,
|
|
345
|
+
query: req.query
|
|
346
|
+
}),
|
|
347
|
+
domain,
|
|
348
|
+
req
|
|
349
|
+
);
|
|
350
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
351
|
+
await this.cacheManager.set(domain, cacheKey, content);
|
|
352
|
+
}
|
|
353
|
+
return res.send(content);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
Logger.error('Search request handling error: ' + error.message);
|
|
356
|
+
return res.redirect('/?error-message=searchError');
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
252
360
|
async handleRequest(req, res)
|
|
253
361
|
{
|
|
254
362
|
try {
|
|
255
363
|
let path = req.path;
|
|
256
364
|
let domain = this.getDomainFromRequest(req);
|
|
257
|
-
this.templateEngine.setCurrentDomain(domain);
|
|
258
365
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
259
366
|
let cachedContent = await this.cacheManager.get(domain, path);
|
|
260
367
|
if(cachedContent){
|
|
@@ -263,17 +370,17 @@ class Frontend
|
|
|
263
370
|
}
|
|
264
371
|
let route = await this.findRouteByPath(path, domain);
|
|
265
372
|
if(route){
|
|
266
|
-
return await this.renderRouteWithCache(route, domain, res, path);
|
|
373
|
+
return await this.renderRouteWithCache(route, domain, res, path, req);
|
|
267
374
|
}
|
|
268
375
|
let entityResult = await this.findEntityByPath(path);
|
|
269
376
|
if(entityResult){
|
|
270
|
-
return await this.renderEntityWithCache(entityResult, domain, res, path);
|
|
377
|
+
return await this.renderEntityWithCache(entityResult, domain, res, path, req);
|
|
271
378
|
}
|
|
272
379
|
let templatePath = this.findTemplateByPath(path, domain);
|
|
273
380
|
if(templatePath){
|
|
274
|
-
return await this.renderTemplateWithCache(templatePath, domain, res, path);
|
|
381
|
+
return await this.renderTemplateWithCache(templatePath, domain, res, path, req);
|
|
275
382
|
}
|
|
276
|
-
return await this.renderNotFound(domain, res);
|
|
383
|
+
return await this.renderNotFound(domain, res, req);
|
|
277
384
|
} catch (error) {
|
|
278
385
|
Logger.error('Request handling error: '+error.message);
|
|
279
386
|
return res.status(500).send('Internal server error');
|
|
@@ -350,57 +457,46 @@ class Frontend
|
|
|
350
457
|
return this.findTemplatePath(templatePath, domain);
|
|
351
458
|
}
|
|
352
459
|
|
|
353
|
-
async renderRouteWithCache(route, domain, res, path)
|
|
460
|
+
async renderRouteWithCache(route, domain, res, path, req)
|
|
354
461
|
{
|
|
355
|
-
let renderedContent = await this.generateRouteContent(route, domain);
|
|
462
|
+
let renderedContent = await this.generateRouteContent(route, domain, req);
|
|
356
463
|
if(!renderedContent){
|
|
357
|
-
return await this.renderNotFound(domain, res);
|
|
464
|
+
return await this.renderNotFound(domain, res, req);
|
|
358
465
|
}
|
|
359
466
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
360
|
-
|
|
467
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
468
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
361
469
|
}
|
|
362
470
|
return res.send(renderedContent);
|
|
363
471
|
}
|
|
364
472
|
|
|
365
|
-
async renderEntityWithCache(entityResult, domain, res, path)
|
|
473
|
+
async renderEntityWithCache(entityResult, domain, res, path, req)
|
|
366
474
|
{
|
|
367
|
-
let renderedContent = await this.generateEntityContent(entityResult, domain);
|
|
475
|
+
let renderedContent = await this.generateEntityContent(entityResult, domain, req);
|
|
368
476
|
if(!renderedContent){
|
|
369
|
-
return await this.renderNotFound(domain, res);
|
|
477
|
+
return await this.renderNotFound(domain, res, req);
|
|
370
478
|
}
|
|
371
479
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
372
|
-
|
|
480
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
481
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
373
482
|
}
|
|
374
483
|
return res.send(renderedContent);
|
|
375
484
|
}
|
|
376
485
|
|
|
377
|
-
async renderTemplateWithCache(templatePath, domain, res, path)
|
|
486
|
+
async renderTemplateWithCache(templatePath, domain, res, path, req)
|
|
378
487
|
{
|
|
379
|
-
let renderedContent = await this.generateTemplateContent(templatePath, domain);
|
|
488
|
+
let renderedContent = await this.generateTemplateContent(templatePath, domain, req);
|
|
380
489
|
if(!renderedContent){
|
|
381
490
|
return res.status(500).send('Template error: '+templatePath);
|
|
382
491
|
}
|
|
383
492
|
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
384
|
-
|
|
493
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
494
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
385
495
|
}
|
|
386
|
-
|
|
387
|
-
let pagePath = this.findTemplatePath('page', domain);
|
|
388
|
-
if(!pagePath){
|
|
389
|
-
return res.send(layoutContent);
|
|
390
|
-
}
|
|
391
|
-
let pageTemplate = FileHandler.readFile(pagePath);
|
|
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);
|
|
496
|
+
return await this.renderWithLayout({content: renderedContent}, {}, 'default', domain, res, req);
|
|
401
497
|
}
|
|
402
498
|
|
|
403
|
-
async generateRouteContent(route, domain)
|
|
499
|
+
async generateRouteContent(route, domain, req)
|
|
404
500
|
{
|
|
405
501
|
if(!route.router){
|
|
406
502
|
return false;
|
|
@@ -413,78 +509,56 @@ class Frontend
|
|
|
413
509
|
if(!content){
|
|
414
510
|
return false;
|
|
415
511
|
}
|
|
416
|
-
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain);
|
|
512
|
+
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain, req);
|
|
417
513
|
}
|
|
418
514
|
|
|
419
|
-
async generateEntityContent(entityResult, domain)
|
|
515
|
+
async generateEntityContent(entityResult, domain, req)
|
|
420
516
|
{
|
|
421
|
-
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain);
|
|
517
|
+
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain, req);
|
|
422
518
|
}
|
|
423
519
|
|
|
424
|
-
async generateTemplateContent(templatePath, domain)
|
|
520
|
+
async generateTemplateContent(templatePath, domain, req)
|
|
425
521
|
{
|
|
426
522
|
let template = FileHandler.readFile(templatePath);
|
|
427
523
|
if(!template){
|
|
428
524
|
Logger.error('Failed to read template: ' + templatePath);
|
|
429
525
|
return false;
|
|
430
526
|
}
|
|
431
|
-
return await this.templateEngine.render(template, {}, this.getPartialsForDomain(domain));
|
|
527
|
+
return await this.templateEngine.render(template, {}, this.getPartialsForDomain(domain), domain, req);
|
|
432
528
|
}
|
|
433
529
|
|
|
434
|
-
async renderRoute(route, domain, res)
|
|
530
|
+
async renderRoute(route, domain, res, req)
|
|
435
531
|
{
|
|
436
532
|
if(!route.router){
|
|
437
|
-
return await this.renderNotFound(domain, res);
|
|
533
|
+
return await this.renderNotFound(domain, res, req);
|
|
438
534
|
}
|
|
439
535
|
let entity = this.dataServer.getEntity(route.router);
|
|
440
536
|
if(!entity){
|
|
441
|
-
return await this.renderNotFound(domain, res);
|
|
537
|
+
return await this.renderNotFound(domain, res, req);
|
|
442
538
|
}
|
|
443
539
|
let content = await entity.loadOne({route_id: route.id});
|
|
444
540
|
if(!content){
|
|
445
|
-
return await this.renderNotFound(domain, res);
|
|
541
|
+
return await this.renderNotFound(domain, res, req);
|
|
446
542
|
}
|
|
447
|
-
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res);
|
|
543
|
+
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res, req);
|
|
448
544
|
}
|
|
449
545
|
|
|
450
|
-
async
|
|
546
|
+
async renderWithTemplate(content, data, domain, res, req)
|
|
451
547
|
{
|
|
452
|
-
return await this.
|
|
548
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req));
|
|
453
549
|
}
|
|
454
550
|
|
|
455
|
-
async
|
|
551
|
+
async renderWithTemplateContent(content, data, domain, req)
|
|
456
552
|
{
|
|
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);
|
|
553
|
+
let templateName = sc.get(content, 'template', 'page');
|
|
460
554
|
if(!templateName){
|
|
461
555
|
templateName = 'page';
|
|
462
556
|
}
|
|
463
|
-
let
|
|
464
|
-
if(!
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
let pageTemplate = FileHandler.readFile(templatePath);
|
|
468
|
-
if(!pageTemplate){
|
|
469
|
-
return res.send(layoutContent);
|
|
470
|
-
}
|
|
471
|
-
return res.send(
|
|
472
|
-
await this.templateEngine.render(
|
|
473
|
-
pageTemplate,
|
|
474
|
-
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
475
|
-
this.getPartialsForDomain(domain)
|
|
476
|
-
)
|
|
477
|
-
);
|
|
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';
|
|
557
|
+
let layoutName = sc.get(content, 'layout', '');
|
|
558
|
+
if(!layoutName){
|
|
559
|
+
layoutName = 'default';
|
|
487
560
|
}
|
|
561
|
+
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain, req);
|
|
488
562
|
let templatePath = this.findTemplatePath(templateName, domain);
|
|
489
563
|
if(!templatePath){
|
|
490
564
|
return layoutContent;
|
|
@@ -495,53 +569,31 @@ class Frontend
|
|
|
495
569
|
}
|
|
496
570
|
return await this.templateEngine.render(
|
|
497
571
|
pageTemplate,
|
|
498
|
-
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
499
|
-
this.getPartialsForDomain(domain)
|
|
572
|
+
Object.assign({}, this.fetchMetaFields(data), {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
573
|
+
this.getPartialsForDomain(domain),
|
|
574
|
+
domain,
|
|
575
|
+
req
|
|
500
576
|
);
|
|
501
577
|
}
|
|
502
578
|
|
|
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)
|
|
579
|
+
async renderContentWithTemplate(templatePath, data, domain, req)
|
|
513
580
|
{
|
|
514
581
|
let template = FileHandler.readFile(templatePath);
|
|
515
582
|
if(!template){
|
|
516
583
|
Logger.error('Failed to read template: ' + templatePath);
|
|
517
584
|
return false;
|
|
518
585
|
}
|
|
519
|
-
return await this.templateEngine.render(template, data, this.getPartialsForDomain(domain));
|
|
586
|
+
return await this.templateEngine.render(template, data, this.getPartialsForDomain(domain), domain, req);
|
|
520
587
|
}
|
|
521
588
|
|
|
522
|
-
async renderWithLayout(content, data, layoutName, domain, res)
|
|
589
|
+
async renderWithLayout(content, data, layoutName, domain, res, req)
|
|
523
590
|
{
|
|
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
|
-
);
|
|
591
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req));
|
|
540
592
|
}
|
|
541
593
|
|
|
542
|
-
async processContentWithLayout(content, data, layoutName, domain)
|
|
594
|
+
async processContentWithLayout(content, data, layoutName, domain, req)
|
|
543
595
|
{
|
|
544
|
-
let processedContent = await this.processContent(content, data, domain);
|
|
596
|
+
let processedContent = await this.processContent(content, data, domain, req);
|
|
545
597
|
let layoutPath = this.findLayoutPath(layoutName, domain);
|
|
546
598
|
if(!layoutPath){
|
|
547
599
|
return processedContent;
|
|
@@ -553,32 +605,57 @@ class Frontend
|
|
|
553
605
|
return await this.templateEngine.render(
|
|
554
606
|
layoutTemplate,
|
|
555
607
|
Object.assign({}, data, {content: processedContent}),
|
|
556
|
-
this.getPartialsForDomain(domain)
|
|
608
|
+
this.getPartialsForDomain(domain),
|
|
609
|
+
domain,
|
|
610
|
+
req
|
|
557
611
|
);
|
|
558
612
|
}
|
|
559
613
|
|
|
560
|
-
async processContent(content, data, domain)
|
|
614
|
+
async processContent(content, data, domain, req)
|
|
561
615
|
{
|
|
562
616
|
let contentText = sc.get(content, 'content', '');
|
|
563
617
|
if(!contentText){
|
|
564
618
|
return '';
|
|
565
619
|
}
|
|
566
|
-
return await this.templateEngine.render(contentText, data, this.getPartialsForDomain(domain));
|
|
620
|
+
return await this.templateEngine.render(contentText, data, this.getPartialsForDomain(domain), domain, req);
|
|
567
621
|
}
|
|
568
622
|
|
|
569
|
-
async renderNotFound(domain, res)
|
|
623
|
+
async renderNotFound(domain, res, req)
|
|
570
624
|
{
|
|
571
625
|
let notFoundPath = this.findTemplatePath('404', domain);
|
|
572
626
|
if(notFoundPath){
|
|
573
|
-
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain);
|
|
627
|
+
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain, req);
|
|
574
628
|
if(content){
|
|
575
629
|
res.status(404);
|
|
576
|
-
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
630
|
+
return await this.renderWithLayout({content}, {}, 'default', domain, res, req);
|
|
577
631
|
}
|
|
578
632
|
}
|
|
579
633
|
return res.status(404).send('Page not found');
|
|
580
634
|
}
|
|
581
635
|
|
|
636
|
+
buildCacheKey(path, req)
|
|
637
|
+
{
|
|
638
|
+
if(!req || !req.query){
|
|
639
|
+
return path;
|
|
640
|
+
}
|
|
641
|
+
for(let key of Object.keys(req.query)){
|
|
642
|
+
if(key.endsWith('-key')){
|
|
643
|
+
let queryString = '';
|
|
644
|
+
for(let qKey of Object.keys(req.query)){
|
|
645
|
+
queryString += (queryString ? '&' : '') + qKey + '=' + req.query[qKey];
|
|
646
|
+
}
|
|
647
|
+
let hash = 0;
|
|
648
|
+
for(let i = 0; i < queryString.length; i++){
|
|
649
|
+
let char = queryString.charCodeAt(i);
|
|
650
|
+
hash = ((hash << 5) - hash) + char;
|
|
651
|
+
hash = hash & hash;
|
|
652
|
+
}
|
|
653
|
+
return path + '_' + Math.abs(hash);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return path;
|
|
657
|
+
}
|
|
658
|
+
|
|
582
659
|
}
|
|
583
660
|
|
|
584
661
|
module.exports.Frontend = Frontend;
|