@reldens/cms 0.16.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.
@@ -817,6 +817,17 @@
817
817
  }
818
818
  }
819
819
 
820
+ .extra-actions {
821
+ display: block;
822
+ width: 100%;
823
+ margin-top: 2rem;
824
+ }
825
+
826
+ .cache-clean-form {
827
+ display: block;
828
+ text-align: center;
829
+ }
830
+
820
831
  & .extra-content-container, .default-room-container {
821
832
  display: flex;
822
833
  flex-direction: column;
@@ -0,0 +1,4 @@
1
+ <form class="cache-clean-form" method="POST" action="{{&cacheCleanRoute}}">
2
+ <input type="hidden" name="routeId" value="{{routeId}}"/>
3
+ <button class="button button-warning cache-clean-btn" type="submit">{{&buttonText}}</button>
4
+ </form>
@@ -1 +1 @@
1
- <textarea name="{{&fieldName}}" id="{{&fieldName}}" disabled="disabled">{{&fieldValue}}</textarea>
1
+ <textarea name="{{&fieldName}}" id="{{&fieldName}}" disabled="disabled">{{fieldValue}}</textarea>
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const { Manager } = require('../index');
10
- const { Logger } = require('@reldens/utils');
10
+ const { Logger, sc } = require('@reldens/utils');
11
11
  const { FileHandler } = require('@reldens/server-utils');
12
12
  const readline = require('readline');
13
13
 
@@ -18,22 +18,84 @@ class CmsEntitiesGenerator
18
18
  {
19
19
  this.args = process.argv.slice(2);
20
20
  this.projectRoot = process.cwd();
21
- this.isOverride = this.args.includes('--override');
22
- this.prismaClientPath = this.extractArgument('--prisma-client');
23
- this.driver = this.extractArgument('--driver') || process.env.RELDENS_STORAGE_DRIVER || 'prisma';
21
+ this.config = {};
22
+ this.parseArguments();
24
23
  }
25
24
 
26
- extractArgument(argumentName)
25
+ parseArguments()
27
26
  {
28
- let argIndex = this.args.indexOf(argumentName);
29
- if(-1 === argIndex || argIndex + 1 >= this.args.length){
30
- return null;
27
+ for(let i = 0; i < this.args.length; i++){
28
+ let arg = this.args[i];
29
+ if(!arg.startsWith('--')){
30
+ continue;
31
+ }
32
+ let equalIndex = arg.indexOf('=');
33
+ if(-1 === equalIndex){
34
+ let flag = arg.substring(2);
35
+ if('override' === flag || 'dry-prisma' === flag || 'help' === flag || 'h' === flag){
36
+ this.config[flag] = true;
37
+ }
38
+ continue;
39
+ }
40
+ let key = arg.substring(2, equalIndex);
41
+ let value = arg.substring(equalIndex + 1);
42
+ this.config[key] = value;
31
43
  }
32
- return this.args[argIndex + 1];
44
+ }
45
+
46
+ shouldShowHelp()
47
+ {
48
+ return 0 === this.args.length || sc.get(this.config, 'help', false) || sc.get(this.config, 'h', false);
49
+ }
50
+
51
+ showHelp()
52
+ {
53
+ Logger.info('');
54
+ Logger.info('Reldens CMS Entities Generator');
55
+ Logger.info('==============================');
56
+ Logger.info('');
57
+ Logger.info('Usage: npx reldens-cms-generate-entities [options]');
58
+ Logger.info('');
59
+ Logger.info('Options:');
60
+ Logger.info(' --prisma-client=[path] Path to Prisma client (e.g., ./prisma/client)');
61
+ Logger.info(' --driver=[driver] Storage driver (default: prisma)');
62
+ Logger.info(' --override Force regeneration and overwrite existing files');
63
+ Logger.info(' --dry-prisma Skip Prisma schema generation');
64
+ Logger.info(' --help, -h Show this help message');
65
+ Logger.info('');
66
+ Logger.info('Examples:');
67
+ Logger.info(' npx reldens-cms-generate-entities --prisma-client=./prisma/client --driver=prisma');
68
+ Logger.info(' npx reldens-cms-generate-entities --override --dry-prisma');
69
+ Logger.info(' npx reldens-cms-generate-entities --help');
70
+ Logger.info('');
71
+ }
72
+
73
+ get isOverride()
74
+ {
75
+ return sc.get(this.config, 'override', false);
76
+ }
77
+
78
+ get isDryPrisma()
79
+ {
80
+ return sc.get(this.config, 'dry-prisma', false);
81
+ }
82
+
83
+ get prismaClientPath()
84
+ {
85
+ return sc.get(this.config, 'prisma-client', null);
86
+ }
87
+
88
+ get driver()
89
+ {
90
+ return sc.get(this.config, 'driver', process.env.RELDENS_STORAGE_DRIVER || 'prisma');
33
91
  }
34
92
 
35
93
  async run()
36
94
  {
95
+ if(this.shouldShowHelp()){
96
+ this.showHelp();
97
+ return true;
98
+ }
37
99
  if(this.isOverride){
38
100
  let confirmed = await this.confirmOverride();
39
101
  if(!confirmed){
@@ -41,6 +103,9 @@ class CmsEntitiesGenerator
41
103
  return false;
42
104
  }
43
105
  }
106
+ if(this.isDryPrisma){
107
+ Logger.info('Running in dry-prisma mode - skipping Prisma schema generation.');
108
+ }
44
109
  let managerConfig = {projectRoot: this.projectRoot};
45
110
  if('prisma' === this.driver){
46
111
  let prismaClient = await this.loadPrismaClient();
@@ -55,7 +120,7 @@ class CmsEntitiesGenerator
55
120
  }
56
121
  Logger.debug('Reldens CMS Manager instance created for entities generation.');
57
122
  await manager.initializeDataServer();
58
- let success = await manager.installer.generateEntities(manager.dataServer, this.isOverride, false);
123
+ let success = await manager.installer.generateEntities(manager.dataServer, this.isOverride, false, this.isDryPrisma);
59
124
  if(!success){
60
125
  Logger.error('Entities generation failed.');
61
126
  return false;
@@ -68,24 +133,26 @@ class CmsEntitiesGenerator
68
133
  {
69
134
  let clientPath = this.prismaClientPath;
70
135
  if(!clientPath){
71
- clientPath = FileHandler.joinPaths(process.cwd(), 'generated-entities', 'prisma');
136
+ return false;
72
137
  }
73
- if(!FileHandler.exists(clientPath)){
74
- Logger.error('Prisma client not found at: '+clientPath);
75
- Logger.error('Please ensure the client exists or specify a custom path with --prisma-client');
138
+ let resolvedPath = clientPath.startsWith('./')
139
+ ? FileHandler.joinPaths(process.cwd(), clientPath.substring(2))
140
+ : clientPath;
141
+ if(!FileHandler.exists(resolvedPath)){
142
+ Logger.error('Prisma client not found at: '+resolvedPath);
76
143
  return false;
77
144
  }
78
145
  try {
79
- let PrismaClientModule = require(clientPath);
146
+ let PrismaClientModule = require(resolvedPath);
80
147
  let PrismaClient = PrismaClientModule.PrismaClient || PrismaClientModule.default?.PrismaClient;
81
148
  if(!PrismaClient){
82
- Logger.error('PrismaClient not found in module: '+clientPath);
149
+ Logger.error('PrismaClient not found in module: '+resolvedPath);
83
150
  return false;
84
151
  }
85
- Logger.debug('Prisma client loaded from: '+clientPath);
152
+ Logger.debug('Prisma client loaded from: '+resolvedPath);
86
153
  return new PrismaClient();
87
154
  } catch (error) {
88
- Logger.error('Failed to load Prisma client from '+clientPath+': '+error.message);
155
+ Logger.error('Failed to load Prisma client from '+resolvedPath+': '+error.message);
89
156
  return false;
90
157
  }
91
158
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  *
5
- * Reldens - CMS - CLI Installer
5
+ * Reldens - CMS - CLI
6
6
  *
7
7
  */
8
8
 
@@ -21,10 +21,10 @@ if(FileHandler.exists(indexPath)){
21
21
 
22
22
  let managerConfig = {projectRoot};
23
23
  let entitiesPath = FileHandler.joinPaths(
24
- projectRoot,
25
- 'generated-entities',
26
- 'models',
27
- 'prisma',
24
+ projectRoot,
25
+ 'generated-entities',
26
+ 'models',
27
+ 'prisma',
28
28
  'registered-models-prisma.js'
29
29
  );
30
30
 
@@ -48,4 +48,4 @@ manager.start().then((result) => {
48
48
  }).catch((error) => {
49
49
  Logger.critical('Failed to start CMS:', error);
50
50
  process.exit();
51
- });
51
+ });
@@ -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;