@reldens/cms 0.16.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 +55 -0
- package/admin/reldens-admin-client.js +24 -0
- package/admin/templates/cache-clean-button.html +4 -0
- package/admin/templates/clear-all-cache-button.html +18 -0
- package/admin/templates/fields/view/textarea.html +1 -1
- package/bin/reldens-cms-generate-entities.js +85 -18
- package/bin/reldens-cms.js +6 -6
- package/lib/admin-manager/contents-builder.js +257 -0
- package/lib/admin-manager/router-contents.js +618 -0
- package/lib/admin-manager/router.js +208 -0
- package/lib/admin-manager-validator.js +2 -1
- package/lib/admin-manager.js +116 -990
- package/lib/admin-translations.js +9 -1
- package/lib/cache/add-cache-button-subscriber.js +149 -0
- package/lib/cache/cache-manager.js +168 -0
- package/lib/cache/cache-routes-handler.js +99 -0
- package/lib/cms-pages-route-manager.js +45 -21
- package/lib/frontend.js +288 -71
- package/lib/installer.js +5 -2
- package/lib/json-fields-parser.js +74 -0
- package/lib/manager.js +49 -4
- 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 +53 -387
- package/lib/templates-list.js +2 -0
- package/migrations/default-homepage.sql +6 -6
- package/migrations/install.sql +21 -20
- 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/manager.js
CHANGED
|
@@ -17,6 +17,7 @@ const { AdminManager } = require('./admin-manager');
|
|
|
17
17
|
const { CmsPagesRouteManager } = require('./cms-pages-route-manager');
|
|
18
18
|
const { Installer } = require('./installer');
|
|
19
19
|
const { Frontend } = require('./frontend');
|
|
20
|
+
const { CacheManager } = require('./cache/cache-manager');
|
|
20
21
|
const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils');
|
|
21
22
|
const { DriversMap } = require('@reldens/storage');
|
|
22
23
|
const { AppServerFactory, FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
@@ -46,7 +47,7 @@ class Manager
|
|
|
46
47
|
this.projectAdminPath = FileHandler.joinPaths(this.projectRoot, 'admin');
|
|
47
48
|
this.projectAdminTemplatesPath = FileHandler.joinPaths(this.projectAdminPath, 'templates');
|
|
48
49
|
this.mimeTypes = sc.get(props, 'mimeTypes', MimeTypes);
|
|
49
|
-
this.allowedExtensions = sc.get(props, 'allowedExtensions', AllowedExtensions)
|
|
50
|
+
this.allowedExtensions = sc.get(props, 'allowedExtensions', AllowedExtensions);
|
|
50
51
|
this.adminRoleId = sc.get(props, 'adminRoleId', 99);
|
|
51
52
|
this.mappedAdminTemplates = TemplatesToPathMapper.map(this.adminTemplatesList, this.projectAdminTemplatesPath);
|
|
52
53
|
this.stylesFilePath = sc.get(props, 'stylesFilePath', '/css/reldens-admin-client.css');
|
|
@@ -58,6 +59,7 @@ class Manager
|
|
|
58
59
|
this.domainMapping = sc.get(props, 'domainMapping', sc.toJson(process.env.RELDENS_DOMAIN_MAPPING));
|
|
59
60
|
this.siteKeyMapping = sc.get(props, 'siteKeyMapping', sc.toJson(process.env.RELDENS_SITE_KEY_MAPPING));
|
|
60
61
|
this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.template']);
|
|
62
|
+
this.cache = sc.get(props, 'cache', false);
|
|
61
63
|
this.app = sc.get(props, 'app', false);
|
|
62
64
|
this.appServer = sc.get(props, 'appServer', false);
|
|
63
65
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
@@ -65,8 +67,22 @@ class Manager
|
|
|
65
67
|
this.frontend = sc.get(props, 'frontend', false);
|
|
66
68
|
this.renderEngine = sc.get(props, 'renderEngine', mustache);
|
|
67
69
|
this.prismaClient = sc.get(props, 'prismaClient', false);
|
|
70
|
+
this.developmentPatterns = sc.get(props, 'developmentPatterns', [
|
|
71
|
+
'localhost',
|
|
72
|
+
'127.0.0.1',
|
|
73
|
+
'.local',
|
|
74
|
+
'.test',
|
|
75
|
+
'.dev',
|
|
76
|
+
'.staging'
|
|
77
|
+
]);
|
|
78
|
+
this.developmentEnvironments = sc.get(props, 'developmentEnvironments', ['development', 'dev', 'test']);
|
|
79
|
+
this.developmentPorts = sc.get(props, 'developmentPorts', [3000, 8080, 8081]);
|
|
80
|
+
this.developmentMultiplier = sc.get(props, 'developmentMultiplier', 10);
|
|
81
|
+
this.appServerConfig = sc.get(props, 'appServerConfig', {});
|
|
82
|
+
this.developmentExternalDomains = sc.get(props, 'developmentExternalDomains', {});
|
|
68
83
|
this.appServerFactory = new AppServerFactory();
|
|
69
84
|
this.adminEntitiesGenerator = new AdminEntitiesGenerator();
|
|
85
|
+
this.cacheManager = new CacheManager({projectRoot: this.projectRoot, enabled: this.cache});
|
|
70
86
|
this.installer = new Installer({
|
|
71
87
|
projectRoot: this.projectRoot,
|
|
72
88
|
prismaClient: this.prismaClient,
|
|
@@ -169,7 +185,8 @@ class Manager
|
|
|
169
185
|
async start()
|
|
170
186
|
{
|
|
171
187
|
if(!this.useProvidedServer){
|
|
172
|
-
let
|
|
188
|
+
let appServerConfig = this.buildAppServerConfiguration();
|
|
189
|
+
let createdAppServer = this.appServerFactory.createAppServer(appServerConfig);
|
|
173
190
|
if(this.appServerFactory.error.message){
|
|
174
191
|
Logger.error('App server error: '+this.appServerFactory.error.message);
|
|
175
192
|
return false;
|
|
@@ -196,6 +213,30 @@ class Manager
|
|
|
196
213
|
}
|
|
197
214
|
}
|
|
198
215
|
|
|
216
|
+
buildAppServerConfiguration()
|
|
217
|
+
{
|
|
218
|
+
let baseConfig = {
|
|
219
|
+
port: this.config.port,
|
|
220
|
+
useHttps: this.config.host.startsWith('https://'),
|
|
221
|
+
domainMapping: this.domainMapping || {},
|
|
222
|
+
defaultDomain: this.defaultDomain,
|
|
223
|
+
developmentPatterns: this.developmentPatterns,
|
|
224
|
+
developmentEnvironments: this.developmentEnvironments,
|
|
225
|
+
developmentPorts: this.developmentPorts,
|
|
226
|
+
developmentMultiplier: this.developmentMultiplier,
|
|
227
|
+
developmentExternalDomains: this.developmentExternalDomains
|
|
228
|
+
};
|
|
229
|
+
let appServerConfig = Object.assign({}, baseConfig, this.appServerConfig);
|
|
230
|
+
if(this.domainMapping && 'object' === typeof this.domainMapping){
|
|
231
|
+
let mappingKeys = Object.keys(this.domainMapping);
|
|
232
|
+
for(let domain of mappingKeys){
|
|
233
|
+
this.appServerFactory.addDevelopmentDomain(domain);
|
|
234
|
+
}
|
|
235
|
+
this.appServerFactory.setDomainMapping(this.domainMapping);
|
|
236
|
+
}
|
|
237
|
+
return appServerConfig;
|
|
238
|
+
}
|
|
239
|
+
|
|
199
240
|
async initializeCmsAfterInstall(props)
|
|
200
241
|
{
|
|
201
242
|
try {
|
|
@@ -322,7 +363,8 @@ class Manager
|
|
|
322
363
|
user: this.config.database.user,
|
|
323
364
|
password: this.config.database.password
|
|
324
365
|
},
|
|
325
|
-
rawEntities: this.rawRegisteredEntities
|
|
366
|
+
rawEntities: this.rawRegisteredEntities,
|
|
367
|
+
entitiesConfig: this.entitiesConfig
|
|
326
368
|
};
|
|
327
369
|
let driverClass = DriversMap[this.config.database.driver];
|
|
328
370
|
if(!driverClass){
|
|
@@ -395,6 +437,7 @@ class Manager
|
|
|
395
437
|
adminRoleId: this.adminRoleId,
|
|
396
438
|
stylesFilePath: this.stylesFilePath,
|
|
397
439
|
scriptsFilePath: this.scriptsFilePath,
|
|
440
|
+
cacheManager: this.cacheManager,
|
|
398
441
|
branding: {
|
|
399
442
|
companyName: this.companyName,
|
|
400
443
|
logo: this.logo,
|
|
@@ -439,7 +482,9 @@ class Manager
|
|
|
439
482
|
defaultDomain: this.defaultDomain,
|
|
440
483
|
domainMapping: this.domainMapping,
|
|
441
484
|
siteKeyMapping: this.siteKeyMapping,
|
|
442
|
-
templateExtensions: this.templateExtensions
|
|
485
|
+
templateExtensions: this.templateExtensions,
|
|
486
|
+
entitiesConfig: this.entitiesConfig,
|
|
487
|
+
cacheManager: this.cacheManager
|
|
443
488
|
});
|
|
444
489
|
return await this.frontend.initialize();
|
|
445
490
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - CMS - PaginationHandler
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class PaginationHandler
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
constructor()
|
|
13
|
+
{
|
|
14
|
+
this.defaultLimit = 10;
|
|
15
|
+
this.prevPageLabel = 'Previous';
|
|
16
|
+
this.nextPageLabel = 'Next';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
sanitizeCollectionKey(collectionKey)
|
|
20
|
+
{
|
|
21
|
+
if(!collectionKey || 'object' !== typeof collectionKey){
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
let sanitizedKey = {};
|
|
25
|
+
let page = sc.parseNumber(sc.get(collectionKey, 'page', 0));
|
|
26
|
+
if(1 <= page){
|
|
27
|
+
sanitizedKey.page = page;
|
|
28
|
+
}
|
|
29
|
+
let limit = sc.parseNumber(sc.get(collectionKey, 'limit', 0));
|
|
30
|
+
if(0 < limit){
|
|
31
|
+
sanitizedKey.limit = limit;
|
|
32
|
+
}
|
|
33
|
+
let sortBy = sc.get(collectionKey, 'sortBy', '');
|
|
34
|
+
if(sc.isString(sortBy) && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(sortBy)){
|
|
35
|
+
sanitizedKey.sortBy = sortBy;
|
|
36
|
+
}
|
|
37
|
+
let sortDirection = sc.get(collectionKey, 'sortDirection');
|
|
38
|
+
if('asc' === sortDirection || 'desc' === sortDirection){
|
|
39
|
+
sanitizedKey.sortDirection = sortDirection;
|
|
40
|
+
}
|
|
41
|
+
let filters = sc.get(collectionKey, 'filters', false);
|
|
42
|
+
if('object' === typeof filters){
|
|
43
|
+
sanitizedKey.filters = this.sanitizeFilters(filters);
|
|
44
|
+
}
|
|
45
|
+
return sanitizedKey;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
sanitizeFilters(filters)
|
|
49
|
+
{
|
|
50
|
+
let sanitizedFilters = {};
|
|
51
|
+
for(let key of Object.keys(filters)){
|
|
52
|
+
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)){
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
let value = filters[key];
|
|
56
|
+
if(sc.isString(value) || sc.isNumber(value) || sc.isBoolean(value)){
|
|
57
|
+
sanitizedFilters[key] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return sanitizedFilters;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
extractCollectionKeyFromRequest(req, collectionId)
|
|
64
|
+
{
|
|
65
|
+
if(!req || !req.query){
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
let paramName = collectionId + '-key';
|
|
69
|
+
let paramValue = sc.get(req.query, paramName, '');
|
|
70
|
+
if(!paramValue){
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
let decodedValue = decodeURIComponent(paramValue);
|
|
74
|
+
let parsedKey = sc.parseJson(decodedValue, {});
|
|
75
|
+
if(!parsedKey){
|
|
76
|
+
Logger.warning('Invalid collection key JSON: ' + decodedValue);
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
return this.sanitizeCollectionKey(parsedKey);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
mergeCollectionParameters(templateParams, requestParams)
|
|
83
|
+
{
|
|
84
|
+
let templateFilters = sc.get(templateParams, 'filters', {});
|
|
85
|
+
let requestFilters = sc.get(requestParams, 'filters', {});
|
|
86
|
+
let mergedFilters = Object.assign({}, templateFilters, requestFilters);
|
|
87
|
+
return {
|
|
88
|
+
limit: sc.get(requestParams, 'limit', sc.get(templateParams, 'limit', this.defaultLimit)),
|
|
89
|
+
page: sc.get(requestParams, 'page', 1),
|
|
90
|
+
sortBy: sc.get(requestParams, 'sortBy', sc.get(templateParams, 'sortBy', 'id')),
|
|
91
|
+
sortDirection: sc.get(requestParams, 'sortDirection', sc.get(templateParams, 'sortDirection', 'asc')),
|
|
92
|
+
filters: mergedFilters
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
calculatePaginationData(totalRecords, currentPage, limit, baseUrl, collectionId, params, templateDefaults)
|
|
97
|
+
{
|
|
98
|
+
if(0 >= totalRecords){
|
|
99
|
+
return this.createEmptyPaginationData();
|
|
100
|
+
}
|
|
101
|
+
let totalPages = Math.ceil(totalRecords / limit);
|
|
102
|
+
if(currentPage > totalPages){
|
|
103
|
+
currentPage = totalPages;
|
|
104
|
+
}
|
|
105
|
+
let offset = (currentPage - 1) * limit;
|
|
106
|
+
let hasNextPage = currentPage < totalPages;
|
|
107
|
+
let hasPrevPage = 1 < currentPage;
|
|
108
|
+
let prevPageUrl = '';
|
|
109
|
+
let nextPageUrl = '';
|
|
110
|
+
if(hasPrevPage){
|
|
111
|
+
prevPageUrl = this.buildPageUrl(baseUrl, collectionId, params, currentPage - 1, templateDefaults);
|
|
112
|
+
}
|
|
113
|
+
if(hasNextPage){
|
|
114
|
+
nextPageUrl = this.buildPageUrl(baseUrl, collectionId, params, currentPage + 1, templateDefaults);
|
|
115
|
+
}
|
|
116
|
+
let prevPages = this.calculatePrevPages(currentPage, params, baseUrl, collectionId, templateDefaults);
|
|
117
|
+
let nextPages = this.calculateNextPages(
|
|
118
|
+
currentPage,
|
|
119
|
+
totalPages,
|
|
120
|
+
params,
|
|
121
|
+
baseUrl,
|
|
122
|
+
collectionId,
|
|
123
|
+
templateDefaults
|
|
124
|
+
);
|
|
125
|
+
return {
|
|
126
|
+
currentPage,
|
|
127
|
+
totalPages,
|
|
128
|
+
totalRecords,
|
|
129
|
+
limit,
|
|
130
|
+
offset,
|
|
131
|
+
hasNextPage,
|
|
132
|
+
hasPrevPage,
|
|
133
|
+
prevPageUrl,
|
|
134
|
+
nextPageUrl,
|
|
135
|
+
prevPageLabel: this.prevPageLabel,
|
|
136
|
+
nextPageLabel: this.nextPageLabel,
|
|
137
|
+
prevPages,
|
|
138
|
+
nextPages
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
createEmptyPaginationData()
|
|
143
|
+
{
|
|
144
|
+
return {
|
|
145
|
+
currentPage: 1,
|
|
146
|
+
totalPages: 0,
|
|
147
|
+
totalRecords: 0,
|
|
148
|
+
limit: this.defaultLimit,
|
|
149
|
+
offset: 0,
|
|
150
|
+
hasNextPage: false,
|
|
151
|
+
hasPrevPage: false,
|
|
152
|
+
prevPageUrl: '',
|
|
153
|
+
nextPageUrl: '',
|
|
154
|
+
prevPageLabel: this.prevPageLabel,
|
|
155
|
+
nextPageLabel: this.nextPageLabel,
|
|
156
|
+
prevPages: [],
|
|
157
|
+
nextPages: []
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
calculatePrevPages(currentPage, params, baseUrl, collectionId, templateDefaults)
|
|
162
|
+
{
|
|
163
|
+
let prevPagesCount = sc.parseNumber(sc.get(params, 'prevPages', 2));
|
|
164
|
+
if(!prevPagesCount || 0 >= prevPagesCount || 1 >= currentPage){
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
let prevPages = [];
|
|
168
|
+
let startPage = Math.max(1, currentPage - prevPagesCount);
|
|
169
|
+
for(let page = startPage; page < currentPage; page++){
|
|
170
|
+
prevPages.push({
|
|
171
|
+
pageLabel: page,
|
|
172
|
+
pageUrl: this.buildPageUrl(baseUrl, collectionId, params, page, templateDefaults)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return prevPages;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
calculateNextPages(currentPage, totalPages, params, baseUrl, collectionId, templateDefaults)
|
|
179
|
+
{
|
|
180
|
+
let nextPagesCount = sc.parseNumber(sc.get(params, 'nextPages', 2));
|
|
181
|
+
if(!nextPagesCount || 0 >= nextPagesCount || currentPage >= totalPages){
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
let nextPages = [];
|
|
185
|
+
let endPage = Math.min(totalPages, currentPage + nextPagesCount);
|
|
186
|
+
for(let page = currentPage + 1; page <= endPage; page++){
|
|
187
|
+
nextPages.push({
|
|
188
|
+
pageLabel: page,
|
|
189
|
+
pageUrl: this.buildPageUrl(baseUrl, collectionId, params, page, templateDefaults)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return nextPages;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
buildPageUrl(baseUrl, collectionId, params, page, templateDefaults)
|
|
196
|
+
{
|
|
197
|
+
let urlParams = {};
|
|
198
|
+
urlParams.page = page;
|
|
199
|
+
let templateFilters = sc.get(templateDefaults, 'filters', {});
|
|
200
|
+
let templateLimit = sc.get(templateDefaults, 'limit', this.defaultLimit);
|
|
201
|
+
let templateSortBy = sc.get(templateDefaults, 'sortBy', 'id');
|
|
202
|
+
let templateSortDirection = sc.get(templateDefaults, 'sortDirection', 'asc');
|
|
203
|
+
let currentFilters = sc.get(params, 'filters', {});
|
|
204
|
+
for(let filterKey of Object.keys(currentFilters)){
|
|
205
|
+
if(!sc.hasOwn(templateFilters, filterKey) || templateFilters[filterKey] !== currentFilters[filterKey]){
|
|
206
|
+
if(!sc.hasOwn(urlParams, 'filters')){
|
|
207
|
+
urlParams.filters = {};
|
|
208
|
+
}
|
|
209
|
+
urlParams.filters[filterKey] = currentFilters[filterKey];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if(params.limit !== templateLimit){
|
|
213
|
+
urlParams.limit = params.limit;
|
|
214
|
+
}
|
|
215
|
+
if(params.sortBy !== templateSortBy){
|
|
216
|
+
urlParams.sortBy = params.sortBy;
|
|
217
|
+
}
|
|
218
|
+
if(params.sortDirection !== templateSortDirection){
|
|
219
|
+
urlParams.sortDirection = params.sortDirection;
|
|
220
|
+
}
|
|
221
|
+
let collectionKey = encodeURIComponent(JSON.stringify(urlParams));
|
|
222
|
+
let paramName = collectionId + '-key';
|
|
223
|
+
let separator = -1 !== baseUrl.indexOf('?') ? '&' : '?';
|
|
224
|
+
return baseUrl + separator + paramName + '=' + collectionKey;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async getCollectionTotal(entity, filters)
|
|
228
|
+
{
|
|
229
|
+
if(!entity || !sc.isFunction(entity.count)){
|
|
230
|
+
Logger.critical('Entity does not support count method');
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
return await entity.count(filters || {});
|
|
235
|
+
} catch (error) {
|
|
236
|
+
Logger.critical('Failed to count collection records: ' + error.message);
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports.PaginationHandler = PaginationHandler;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - CMS - SearchRenderer
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class SearchRenderer
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
constructor(props)
|
|
13
|
+
{
|
|
14
|
+
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
15
|
+
this.getPartials = sc.get(props, 'getPartials', false);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async renderSearchResults(searchResults, config, domain, req)
|
|
19
|
+
{
|
|
20
|
+
if(!searchResults || !sc.isArray(searchResults) || 0 === searchResults.length){
|
|
21
|
+
return this.renderNoSearchResults(domain);
|
|
22
|
+
}
|
|
23
|
+
let renderedContent = '';
|
|
24
|
+
for(let entityResult of searchResults){
|
|
25
|
+
if(!sc.hasOwn(entityResult, 'results') || !sc.isArray(entityResult.results)){
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if(0 === entityResult.results.length){
|
|
29
|
+
renderedContent += this.renderNoSearchResults(domain);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
let partialName = sc.get(config, 'render.partial', 'entriesListView');
|
|
33
|
+
let partialTemplate = this.loadPartialTemplate(partialName, domain);
|
|
34
|
+
if(!partialTemplate){
|
|
35
|
+
Logger.error('Search result partial template not found: ' + partialName);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if(sc.hasOwn(entityResult, 'pagination')){
|
|
39
|
+
let entityContent = await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain);
|
|
40
|
+
let totalPages = sc.get(entityResult.pagination, 'totalPages', 1);
|
|
41
|
+
if(1 < totalPages){
|
|
42
|
+
let paginationContainer = sc.get(config, 'render.paginationContainer', 'pagedCollection');
|
|
43
|
+
let paginationTemplate = this.loadPaginationTemplate(paginationContainer, domain);
|
|
44
|
+
let paginationData = Object.assign({}, entityResult.pagination, {
|
|
45
|
+
collectionContentForCurrentPage: entityContent,
|
|
46
|
+
hasResults: 0 < entityResult.results.length,
|
|
47
|
+
noResultsMessage: entityResult.noResultsMessage || 'No results found.'
|
|
48
|
+
});
|
|
49
|
+
renderedContent += this.renderEngine.render(paginationTemplate, paginationData, this.getPartialsForDomain(domain));
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
renderedContent += entityContent;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
renderedContent += await this.renderSearchEntityResults(entityResult.results, partialTemplate, domain);
|
|
56
|
+
}
|
|
57
|
+
return renderedContent;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async renderSearchEntityResults(results, partialTemplate, domain)
|
|
61
|
+
{
|
|
62
|
+
let renderedContent = '';
|
|
63
|
+
for(let row of results){
|
|
64
|
+
renderedContent += this.renderEngine.render(partialTemplate, {row}, this.getPartialsForDomain(domain));
|
|
65
|
+
}
|
|
66
|
+
return renderedContent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
renderNoSearchResults(domain)
|
|
70
|
+
{
|
|
71
|
+
let noResultsTemplate = this.loadPartialTemplate('noResults', domain);
|
|
72
|
+
if(noResultsTemplate){
|
|
73
|
+
return this.renderEngine.render(
|
|
74
|
+
noResultsTemplate,
|
|
75
|
+
{
|
|
76
|
+
message: 'No search results found.',
|
|
77
|
+
cssClass: 'no-search-results',
|
|
78
|
+
alertClass: 'alert-info'
|
|
79
|
+
},
|
|
80
|
+
this.getPartialsForDomain(domain)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return '<div class="no-search-results alert alert-info">No search results found.</div>';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
loadPartialTemplate(partialName, domain)
|
|
87
|
+
{
|
|
88
|
+
let partials = this.getPartialsForDomain(domain);
|
|
89
|
+
return sc.get(partials, partialName, false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
loadPaginationTemplate(containerName, domain)
|
|
93
|
+
{
|
|
94
|
+
if(!containerName || '' === containerName){
|
|
95
|
+
containerName = 'pagedCollection';
|
|
96
|
+
}
|
|
97
|
+
let partialContent = this.loadPartialTemplate(containerName, domain);
|
|
98
|
+
if(partialContent){
|
|
99
|
+
return partialContent;
|
|
100
|
+
}
|
|
101
|
+
Logger.critical('Pagination template not found: ' + containerName + '. Please create the template file.');
|
|
102
|
+
return '{{&collectionContentForCurrentPage}}';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getPartialsForDomain(domain)
|
|
106
|
+
{
|
|
107
|
+
if(!this.getPartials){
|
|
108
|
+
Logger.error('getPartials function not provided to SearchRenderer');
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
return this.getPartials(domain);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports.SearchRenderer = SearchRenderer;
|