@reldens/cms 0.15.0 → 0.18.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.
Files changed (38) hide show
  1. package/README.md +150 -34
  2. package/admin/reldens-admin-client.css +39 -23
  3. package/admin/reldens-admin-client.js +7 -0
  4. package/admin/templates/cache-clean-button.html +4 -0
  5. package/admin/templates/edit.html +3 -1
  6. package/admin/templates/fields/view/textarea.html +1 -0
  7. package/admin/templates/sections/editForm/cms-pages.html +15 -0
  8. package/admin/templates/sections/viewForm/cms-pages.html +15 -0
  9. package/admin/templates/view.html +1 -0
  10. package/bin/reldens-cms-generate-entities.js +116 -5
  11. package/bin/reldens-cms.js +26 -8
  12. package/install/js/installer.js +5 -0
  13. package/install/success.html +1 -1
  14. package/lib/admin-manager/contents-builder.js +256 -0
  15. package/lib/admin-manager/router-contents.js +576 -0
  16. package/lib/admin-manager/router.js +208 -0
  17. package/lib/admin-manager.js +114 -944
  18. package/lib/cache/add-cache-button-subscriber.js +101 -0
  19. package/lib/cache/cache-manager.js +129 -0
  20. package/lib/cache/cache-routes-handler.js +76 -0
  21. package/lib/cms-pages-route-manager.js +117 -0
  22. package/lib/frontend.js +207 -64
  23. package/lib/installer.js +44 -20
  24. package/lib/json-fields-parser.js +74 -0
  25. package/lib/manager.js +55 -10
  26. package/lib/template-engine.js +361 -41
  27. package/lib/templates-list.js +10 -0
  28. package/migrations/default-blocks.sql +1 -1
  29. package/migrations/default-entity-access.sql +2 -2
  30. package/migrations/default-homepage.sql +27 -7
  31. package/migrations/install.sql +33 -36
  32. package/package.json +3 -3
  33. package/templates/index.js.dist +3 -3
  34. package/templates/js/scripts.js +5 -0
  35. package/templates/layouts/default.html +4 -4
  36. package/templates/page.html +14 -10
  37. package/templates/partials/footer.html +2 -23
  38. package/templates/partials/header.html +2 -35
@@ -2,20 +2,43 @@
2
2
 
3
3
  /**
4
4
  *
5
- * Reldens - CMS - CLI Installer
5
+ * Reldens - CMS - CLI
6
6
  *
7
7
  */
8
8
 
9
9
  const { Manager } = require('../index');
10
10
  const { Logger } = require('@reldens/utils');
11
+ const { FileHandler } = require('@reldens/server-utils');
11
12
 
12
13
  let args = process.argv.slice(2);
13
14
  let projectRoot = args[0] || process.cwd();
15
+ let indexPath = FileHandler.joinPaths(projectRoot, 'index.js');
14
16
 
15
- let manager = new Manager({projectRoot});
17
+ if(FileHandler.exists(indexPath)){
18
+ require(indexPath);
19
+ return;
20
+ }
21
+
22
+ let managerConfig = {projectRoot};
23
+ let entitiesPath = FileHandler.joinPaths(
24
+ projectRoot,
25
+ 'generated-entities',
26
+ 'models',
27
+ 'prisma',
28
+ 'registered-models-prisma.js'
29
+ );
30
+
31
+ if(FileHandler.exists(entitiesPath)){
32
+ let entitiesModule = require(entitiesPath);
33
+ managerConfig.rawRegisteredEntities = entitiesModule.rawRegisteredEntities;
34
+ managerConfig.entitiesConfig = entitiesModule.entitiesConfig;
35
+ managerConfig.entitiesTranslations = entitiesModule.entitiesTranslations;
36
+ }
37
+
38
+ let manager = new Manager(managerConfig);
16
39
  Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
17
40
 
18
- let started = manager.start().then((result) => {
41
+ manager.start().then((result) => {
19
42
  if(!result){
20
43
  Logger.info('Reldens CMS started by command failed.');
21
44
  return false;
@@ -26,8 +49,3 @@ let started = manager.start().then((result) => {
26
49
  Logger.critical('Failed to start CMS:', error);
27
50
  process.exit();
28
51
  });
29
-
30
- if(!started){
31
- Logger.error('Reldens CMS start process failed.');
32
- process.exit();
33
- }
@@ -29,4 +29,9 @@ document.addEventListener('DOMContentLoaded', function() {
29
29
  installButton.disabled = true;
30
30
  }
31
31
  });
32
+
33
+ let copyRightYear = document.querySelector('.copyright-year');
34
+ if(copyRightYear){
35
+ copyRightYear.innerHTML = String((new Date()).getFullYear());
36
+ }
32
37
  });
@@ -29,7 +29,7 @@
29
29
  </div>
30
30
  <div class="footer">
31
31
  <div class="copyright">
32
- &copy;{{currentYear}} Reldens CMS
32
+ &copy;<span class="copyright-year">2025</span> Reldens CMS
33
33
  </div>
34
34
  </div>
35
35
  </div>
@@ -0,0 +1,256 @@
1
+ /**
2
+ *
3
+ * Reldens - ContentsBuilder
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+
9
+ class ContentsBuilder
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.renderCallback = props.renderCallback;
15
+ this.adminFilesContents = props.adminFilesContents;
16
+ this.stylesFilePath = props.stylesFilePath;
17
+ this.scriptsFilePath = props.scriptsFilePath;
18
+ this.rootPath = props.rootPath;
19
+ this.branding = props.branding;
20
+ this.translations = props.translations;
21
+ this.resources = props.resources;
22
+ this.buildAdminCssOnActivation = props.buildAdminCssOnActivation;
23
+ this.buildAdminScriptsOnActivation = props.buildAdminScriptsOnActivation;
24
+ this.updateAdminAssetsDistOnActivation = props.updateAdminAssetsDistOnActivation;
25
+ this.emitEvent = props.emitEvent;
26
+ this.editPath = props.editPath;
27
+ this.savePath = props.savePath;
28
+ this.deletePath = props.deletePath;
29
+ this.fetchUploadProperties = props.fetchUploadProperties;
30
+ this.fetchTranslation = props.fetchTranslation;
31
+ this.fetchEntityIdPropertyKey = props.fetchEntityIdPropertyKey;
32
+ this.adminContents = {};
33
+ }
34
+
35
+ async buildAdminContents()
36
+ {
37
+ this.adminContents.layout = await this.buildLayout();
38
+ this.adminContents.sideBar = await this.buildSideBar();
39
+ this.adminContents.login = await this.renderRoute(this.adminFilesContents.login, '');
40
+ this.adminContents.dashboard = await this.renderRoute(this.adminFilesContents.dashboard, this.adminContents.sideBar);
41
+ this.adminContents.entities = await this.buildEntitiesContents();
42
+ await this.emitEvent('reldens.buildAdminContentsAfter');
43
+ return this.adminContents;
44
+ }
45
+
46
+ async buildLayout()
47
+ {
48
+ return await this.render(
49
+ this.adminFilesContents.layout,
50
+ {
51
+ sideBar: '{{&sideBar}}',
52
+ pageContent: '{{&pageContent}}',
53
+ stylesFilePath: this.stylesFilePath,
54
+ scriptsFilePath: this.scriptsFilePath,
55
+ rootPath: this.rootPath,
56
+ brandingCompanyName: this.branding.companyName,
57
+ copyRight: this.branding.copyRight
58
+ }
59
+ );
60
+ }
61
+
62
+ async buildSideBar()
63
+ {
64
+ let navigationContents = {};
65
+ let eventBuildSideBarBefore = {navigationContents};
66
+ await this.emitEvent('reldens.eventBuildSideBarBefore', eventBuildSideBarBefore);
67
+ navigationContents = eventBuildSideBarBefore.navigationContents;
68
+ for(let driverResource of this.resources()){
69
+ let navigation = driverResource.options?.navigation;
70
+ let name = this.translations.labels[driverResource.id()] || this.translations.labels[driverResource.entityKey];
71
+ let path = this.rootPath+'/'+(driverResource.id().replace(/_/g, '-'));
72
+ if(navigation?.name){
73
+ if(!navigationContents[navigation.name]){
74
+ navigationContents[navigation.name] = {};
75
+ }
76
+ navigationContents[navigation.name][driverResource.id()] = await this.render(
77
+ this.adminFilesContents.sideBarItem,
78
+ {name, path}
79
+ );
80
+ continue;
81
+ }
82
+ navigationContents[driverResource.id()] = await this.render(
83
+ this.adminFilesContents.sideBarItem,
84
+ {name, path}
85
+ );
86
+ }
87
+ let eventAdminSideBarBeforeSubItems = {navigationContents};
88
+ await this.emitEvent('reldens.adminSideBarBeforeSubItems', eventAdminSideBarBeforeSubItems);
89
+ let navigationView = '';
90
+ for(let id of Object.keys(navigationContents)){
91
+ if(sc.isObject(navigationContents[id])){
92
+ let subItems = '';
93
+ for(let subId of Object.keys(navigationContents[id])){
94
+ subItems += navigationContents[id][subId];
95
+ }
96
+ navigationView += await this.render(
97
+ this.adminFilesContents.sideBarHeader,
98
+ {name: id, subItems}
99
+ );
100
+ continue;
101
+ }
102
+ navigationView += navigationContents[id];
103
+ }
104
+ let eventAdminSideBarBeforeRender = {navigationContents, navigationView};
105
+ await this.emitEvent('reldens.adminSideBarBeforeRender', eventAdminSideBarBeforeRender);
106
+ return await this.render(
107
+ this.adminFilesContents.sideBar,
108
+ {
109
+ rootPath: this.rootPath,
110
+ navigationView: eventAdminSideBarBeforeRender.navigationView
111
+ }
112
+ );
113
+ }
114
+
115
+ async buildEntitiesContents()
116
+ {
117
+ let entitiesContents = {};
118
+ for(let driverResource of this.resources()){
119
+ let templateTitle = this.translations.labels[driverResource.id()];
120
+ let entityName = (driverResource.id().replace(/_/g, '-'));
121
+ let entityListRoute = this.rootPath+'/'+entityName;
122
+ let entityEditRoute = entityListRoute+this.editPath;
123
+ let entitySaveRoute = entityListRoute+this.savePath;
124
+ let entityDeleteRoute = entityListRoute+this.deletePath;
125
+ let uploadProperties = this.fetchUploadProperties(driverResource);
126
+ let multipartFormData = 0 < Object.keys(uploadProperties).length ? ' enctype="multipart/form-data"' : '';
127
+ let idProperty = this.fetchEntityIdPropertyKey(driverResource);
128
+ let editProperties = Object.keys(driverResource.options.properties);
129
+ editProperties.splice(editProperties.indexOf(idProperty), 1);
130
+ let filters = driverResource.options.filterProperties.map((property) => {
131
+ return {
132
+ propertyKey: property,
133
+ name: this.fetchTranslation(property),
134
+ value: '{{&'+property+'}}'
135
+ };
136
+ });
137
+ let fields = driverResource.options.showProperties.map((property) => {
138
+ return {
139
+ name: this.fetchTranslation(property),
140
+ value: '{{&'+property+'}}'
141
+ };
142
+ });
143
+ let editFields = editProperties.map((property) => {
144
+ return {
145
+ name: this.fetchTranslation(property),
146
+ value: '{{&'+property+'}}'
147
+ };
148
+ });
149
+ let sectionsContents = this.adminFilesContents?.sections;
150
+ let extraContentForList = sc.get(sectionsContents?.list, driverResource.entityPath, '');
151
+ let extraContentForView = await this.render(
152
+ sc.get(sectionsContents?.view, driverResource.entityPath, ''),
153
+ {
154
+ id: '{{&id}}',
155
+ entitySerializedData: '{{&entitySerializedData}}'
156
+ }
157
+ );
158
+ let extraFormContentForView = sc.get(sectionsContents?.viewForm, driverResource.entityPath, '');
159
+ let extraContentForEdit = sc.get(sectionsContents?.edit, driverResource.entityPath, '');
160
+ let extraFormContentForEdit = sc.get(sectionsContents?.editForm, driverResource.entityPath, '');
161
+ entitiesContents[entityName] = {
162
+ list: await this.render(
163
+ this.adminFilesContents.list,
164
+ {
165
+ entityName,
166
+ templateTitle,
167
+ entityListRoute,
168
+ entityEditRoute,
169
+ filters,
170
+ list: '{{&list}}',
171
+ pagination: '{{&pagination}}',
172
+ extraContent: '{{&extraContentForList}}'+extraContentForList,
173
+ }
174
+ ),
175
+ view: await this.render(
176
+ this.adminFilesContents.view,
177
+ {
178
+ entityName,
179
+ templateTitle,
180
+ entityDeleteRoute,
181
+ entityListRoute,
182
+ fields,
183
+ id: '{{&id}}',
184
+ entityEditRoute: '{{&entityEditRoute}}',
185
+ entityNewRoute: '{{&entityNewRoute}}',
186
+ extraContent: '{{&extraContentForView}}'+extraContentForView,
187
+ extraFormContent: '{{&extraFormContentForView}}'+extraFormContentForView
188
+ }
189
+ ),
190
+ edit: await this.render(
191
+ this.adminFilesContents.edit,
192
+ {
193
+ entityName,
194
+ entitySaveRoute,
195
+ multipartFormData,
196
+ editFields,
197
+ idValue: '{{&idValue}}',
198
+ idProperty: '{{&idProperty}}',
199
+ templateTitle: '{{&templateTitle}}',
200
+ entityViewRoute: '{{&entityViewRoute}}',
201
+ extraContent: '{{&extraContentForEdit}}'+extraContentForEdit,
202
+ extraFormContent: '{{&extraFormContentForEdit}}'+extraFormContentForEdit
203
+ }
204
+ )
205
+ };
206
+ }
207
+ return entitiesContents;
208
+ }
209
+
210
+ async render(content, params)
211
+ {
212
+ return await this.renderCallback(content, params);
213
+ }
214
+
215
+ async renderRoute(pageContent, sideBar)
216
+ {
217
+ return await this.render(
218
+ this.adminContents.layout,
219
+ {
220
+ stylesFilePath: this.stylesFilePath,
221
+ scriptsFilePath: this.scriptsFilePath,
222
+ brandingCompanyName: this.branding.companyName,
223
+ copyRight: this.branding.copyRight,
224
+ pageContent,
225
+ sideBar
226
+ }
227
+ );
228
+ }
229
+
230
+ async buildAdminScripts()
231
+ {
232
+ if(!sc.isFunction(this.buildAdminScriptsOnActivation)){
233
+ return false;
234
+ }
235
+ return this.buildAdminScriptsOnActivation();
236
+ }
237
+
238
+ async updateAdminAssets()
239
+ {
240
+ if(!sc.isFunction(this.updateAdminAssetsDistOnActivation)){
241
+ return false;
242
+ }
243
+ return this.updateAdminAssetsDistOnActivation();
244
+ }
245
+
246
+ async buildAdminCss()
247
+ {
248
+ if(!sc.isFunction(this.buildAdminCssOnActivation)){
249
+ return false;
250
+ }
251
+ return this.buildAdminCssOnActivation();
252
+ }
253
+
254
+ }
255
+
256
+ module.exports.ContentsBuilder = ContentsBuilder;