@reldens/cms 0.9.0 → 0.10.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 +316 -68
- package/admin/templates/fields/edit/text.html +1 -1
- package/install/css/installer.css +8 -0
- package/install/index.html +14 -6
- package/lib/admin-manager.js +39 -31
- package/lib/frontend.js +263 -139
- package/lib/installer.js +36 -9
- package/lib/manager.js +37 -5
- package/migrations/default-blocks.sql +8 -0
- package/migrations/default-entity-access.sql +9 -0
- package/migrations/default-homepage.sql +1 -1
- package/migrations/install.sql +34 -9
- package/migrations/users-authentication.sql +2 -2
- package/package.json +4 -4
- package/templates/404.html +7 -29
- package/templates/css/styles.css +4 -1
- package/templates/domains/.gitkeep +0 -0
- package/templates/js/scripts.js +9 -7
- package/templates/layouts/default.html +21 -0
- package/templates/page.html +14 -32
- package/templates/partials/footer.html +35 -0
- package/templates/partials/header.html +36 -0
package/lib/installer.js
CHANGED
|
@@ -8,7 +8,6 @@ const { FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
|
8
8
|
const { DriversMap, EntitiesGenerator, PrismaSchemaGenerator } = require('@reldens/storage');
|
|
9
9
|
const { EntitiesLoader } = require('./entities-loader');
|
|
10
10
|
const { Logger, sc } = require('@reldens/utils');
|
|
11
|
-
const mustache = require('mustache');
|
|
12
11
|
|
|
13
12
|
class Installer
|
|
14
13
|
{
|
|
@@ -18,6 +17,7 @@ class Installer
|
|
|
18
17
|
this.app = sc.get(props, 'app', false);
|
|
19
18
|
this.appServer = sc.get(props, 'appServer', false);
|
|
20
19
|
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
20
|
+
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
21
21
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
22
22
|
this.projectTemplatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
23
23
|
this.projectPublicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
@@ -43,7 +43,7 @@ class Installer
|
|
|
43
43
|
return FileHandler.exists(this.installLockPath);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async prepareSetup(app, appServer, appServerFactory)
|
|
46
|
+
async prepareSetup(app, appServer, appServerFactory, renderEngine)
|
|
47
47
|
{
|
|
48
48
|
if(!app){
|
|
49
49
|
Logger.error('Missing app on prepareSetup for Installer.');
|
|
@@ -53,9 +53,14 @@ class Installer
|
|
|
53
53
|
Logger.error('Missing appServerFactory on prepareSetup for Installer.');
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
|
+
if(!renderEngine){
|
|
57
|
+
Logger.error('Missing renderEngine for Installer.');
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
56
60
|
this.app = app;
|
|
57
61
|
this.appServerFactory = appServerFactory;
|
|
58
62
|
this.appServer = appServer;
|
|
63
|
+
this.renderEngine = renderEngine;
|
|
59
64
|
app.use('/install-assets', appServerFactory.applicationFramework.static(this.installerPath, {index: false}));
|
|
60
65
|
app.use(appServerFactory.session({
|
|
61
66
|
secret: Encryptor.generateSecretKey(),
|
|
@@ -88,7 +93,7 @@ class Installer
|
|
|
88
93
|
if(errorParam){
|
|
89
94
|
contentParams.errorMessage = this.getErrorMessage(errorParam);
|
|
90
95
|
}
|
|
91
|
-
return res.send(
|
|
96
|
+
return res.send(this.renderEngine.render(content, contentParams));
|
|
92
97
|
}
|
|
93
98
|
if('/install' !== urlPath){
|
|
94
99
|
return res.redirect('/');
|
|
@@ -161,7 +166,9 @@ class Installer
|
|
|
161
166
|
'install-cms-tables': 'install.sql',
|
|
162
167
|
'install-user-auth': 'users-authentication.sql',
|
|
163
168
|
'install-default-user': 'default-user.sql',
|
|
164
|
-
'install-default-homepage': 'default-homepage.sql'
|
|
169
|
+
'install-default-homepage': 'default-homepage.sql',
|
|
170
|
+
'install-default-blocks': 'default-blocks.sql',
|
|
171
|
+
'install-entity-access': 'default-entity-access.sql'
|
|
165
172
|
};
|
|
166
173
|
for(let checkboxName of Object.keys(executeFiles)){
|
|
167
174
|
let fileName = executeFiles[checkboxName];
|
|
@@ -191,17 +198,24 @@ class Installer
|
|
|
191
198
|
await this.appServer.close();
|
|
192
199
|
}
|
|
193
200
|
Logger.debug('Running postInstallCallback.');
|
|
194
|
-
await this.postInstallCallback({
|
|
201
|
+
let callbackResult = await this.postInstallCallback({
|
|
195
202
|
loadedEntities: this.entitiesLoader.loadEntities(selectedDriver),
|
|
196
203
|
mappedVariablesForConfig
|
|
197
204
|
});
|
|
205
|
+
if(false === callbackResult){
|
|
206
|
+
Logger.error('Post-install callback failed.');
|
|
207
|
+
return res.redirect('/?error=installation-entities-callback-failed');
|
|
208
|
+
}
|
|
198
209
|
}
|
|
199
210
|
await this.createLockFile();
|
|
200
211
|
Logger.info('Installation successful!');
|
|
201
212
|
let successContent = 'Installation successful! Run "node ." to start your CMS.';
|
|
202
213
|
let successFileContent = FileHandler.readFile(FileHandler.joinPaths(this.installerPath, 'success.html'));
|
|
203
214
|
if(successFileContent){
|
|
204
|
-
successContent =
|
|
215
|
+
successContent = this.renderEngine.render(
|
|
216
|
+
successFileContent,
|
|
217
|
+
{adminPath: templateVariables['app-admin-path']}
|
|
218
|
+
);
|
|
205
219
|
}
|
|
206
220
|
return res.send(successContent);
|
|
207
221
|
} catch (error) {
|
|
@@ -245,6 +259,7 @@ class Installer
|
|
|
245
259
|
{
|
|
246
260
|
let generator = new PrismaSchemaGenerator({
|
|
247
261
|
...connectionData,
|
|
262
|
+
dataProxy: true,
|
|
248
263
|
prismaSchemaPath: this.projectRoot+'/prisma'
|
|
249
264
|
});
|
|
250
265
|
let success = await generator.generate();
|
|
@@ -263,7 +278,7 @@ class Installer
|
|
|
263
278
|
Logger.error('Template ".env.dist" not found: '+envTemplatePath);
|
|
264
279
|
return false;
|
|
265
280
|
}
|
|
266
|
-
return FileHandler.writeFile(this.envFilePath,
|
|
281
|
+
return FileHandler.writeFile(this.envFilePath, this.renderEngine.render(envTemplateContent, templateVariables));
|
|
267
282
|
}
|
|
268
283
|
|
|
269
284
|
mapVariablesForTemplate(configVariables)
|
|
@@ -310,7 +325,7 @@ class Installer
|
|
|
310
325
|
}
|
|
311
326
|
let indexTemplate = FileHandler.readFile(this.indexTemplatePath);
|
|
312
327
|
let driverKey = templateVariables['db-storage-driver'];
|
|
313
|
-
let indexContent =
|
|
328
|
+
let indexContent = this.renderEngine.render(indexTemplate, {driverKey});
|
|
314
329
|
let indexFilePath = FileHandler.joinPaths(this.projectRoot, 'index.js');
|
|
315
330
|
if(FileHandler.exists(indexFilePath)){
|
|
316
331
|
Logger.info('Index.js file already exists, the CMS installer will not override the existent one.');
|
|
@@ -353,6 +368,7 @@ class Installer
|
|
|
353
368
|
async prepareProjectDirectories()
|
|
354
369
|
{
|
|
355
370
|
FileHandler.createFolder(this.projectTemplatesPath);
|
|
371
|
+
FileHandler.createFolder(FileHandler.joinPaths(this.projectTemplatesPath, 'layouts'));
|
|
356
372
|
FileHandler.createFolder(this.projectPublicPath);
|
|
357
373
|
FileHandler.createFolder(this.projectPublicAssetsPath);
|
|
358
374
|
FileHandler.createFolder(this.projectCssPath);
|
|
@@ -360,7 +376,6 @@ class Installer
|
|
|
360
376
|
let baseFiles = [
|
|
361
377
|
'page.html',
|
|
362
378
|
'404.html',
|
|
363
|
-
'layout.html',
|
|
364
379
|
'browserconfig.xml',
|
|
365
380
|
'favicon.ico',
|
|
366
381
|
'site.webmanifest'
|
|
@@ -371,6 +386,10 @@ class Installer
|
|
|
371
386
|
FileHandler.joinPaths(this.projectTemplatesPath, fileName)
|
|
372
387
|
);
|
|
373
388
|
}
|
|
389
|
+
FileHandler.copyFile(
|
|
390
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'layouts', 'default.html'),
|
|
391
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'layouts', 'default.html')
|
|
392
|
+
);
|
|
374
393
|
FileHandler.copyFile(
|
|
375
394
|
FileHandler.joinPaths(this.defaultTemplatesPath, 'css', 'styles.css'),
|
|
376
395
|
FileHandler.joinPaths(this.projectCssPath, 'styles.css')
|
|
@@ -379,6 +398,14 @@ class Installer
|
|
|
379
398
|
FileHandler.joinPaths(this.defaultTemplatesPath, 'js', 'scripts.js'),
|
|
380
399
|
FileHandler.joinPaths(this.projectJsPath, 'scripts.js')
|
|
381
400
|
);
|
|
401
|
+
FileHandler.copyFolderSync(
|
|
402
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'partials'),
|
|
403
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'partials')
|
|
404
|
+
);
|
|
405
|
+
FileHandler.copyFolderSync(
|
|
406
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'domains'),
|
|
407
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'domains')
|
|
408
|
+
);
|
|
382
409
|
return true;
|
|
383
410
|
}
|
|
384
411
|
|
package/lib/manager.js
CHANGED
|
@@ -37,6 +37,7 @@ class Manager
|
|
|
37
37
|
this.entitiesTranslations = sc.get(props, 'entitiesTranslations', {});
|
|
38
38
|
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
39
39
|
this.processedEntities = sc.get(props, 'processedEntities', {});
|
|
40
|
+
this.entityAccess = sc.get(props, 'entityAccess', {});
|
|
40
41
|
this.authenticationMethod = sc.get(props, 'authenticationMethod', 'db-users');
|
|
41
42
|
this.authenticationCallback = sc.get(props, 'authenticationCallback', false);
|
|
42
43
|
this.events = sc.get(props, 'events', EventsManagerSingleton);
|
|
@@ -51,11 +52,16 @@ class Manager
|
|
|
51
52
|
this.companyName = sc.get(props, 'companyName', 'Reldens - CMS');
|
|
52
53
|
this.logo = sc.get(props, 'logo', '/assets/web/reldens-your-logo-mage.png');
|
|
53
54
|
this.favicon = sc.get(props, 'favicon', '/assets/web/favicon.ico');
|
|
55
|
+
this.defaultDomain = sc.get(props, 'defaultDomain', (process.env.RELDENS_DEFAULT_DOMAIN || ''));
|
|
56
|
+
this.domainMapping = sc.get(props, 'domainMapping', sc.toJson(process.env.RELDENS_DOMAIN_MAPPING));
|
|
57
|
+
this.siteKeyMapping = sc.get(props, 'siteKeyMapping', sc.toJson(process.env.RELDENS_SITE_KEY_MAPPING));
|
|
58
|
+
this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
|
|
54
59
|
this.app = sc.get(props, 'app', false);
|
|
55
60
|
this.appServer = sc.get(props, 'appServer', false);
|
|
56
61
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
57
62
|
this.adminManager = sc.get(props, 'adminManager', false);
|
|
58
63
|
this.frontend = sc.get(props, 'frontend', false);
|
|
64
|
+
this.renderEngine = sc.get(props, 'renderEngine', mustache);
|
|
59
65
|
this.appServerFactory = new AppServerFactory();
|
|
60
66
|
this.adminEntitiesGenerator = new AdminEntitiesGenerator();
|
|
61
67
|
this.installer = new Installer({
|
|
@@ -165,7 +171,7 @@ class Manager
|
|
|
165
171
|
}
|
|
166
172
|
if(!this.isInstalled()){
|
|
167
173
|
Logger.info('CMS not installed, preparing setup');
|
|
168
|
-
await this.installer.prepareSetup(this.app, this.appServer, this.appServerFactory);
|
|
174
|
+
await this.installer.prepareSetup(this.app, this.appServer, this.appServerFactory, this.renderEngine);
|
|
169
175
|
if(!this.useProvidedServer){
|
|
170
176
|
await this.appServer.listen(this.config.port);
|
|
171
177
|
}
|
|
@@ -185,7 +191,6 @@ class Manager
|
|
|
185
191
|
async initializeCmsAfterInstall(props)
|
|
186
192
|
{
|
|
187
193
|
try {
|
|
188
|
-
//Logger.debug('Loaded entities post install.', props.loadedEntities);
|
|
189
194
|
this.rawRegisteredEntities = props.loadedEntities.rawRegisteredEntities;
|
|
190
195
|
this.entitiesTranslations = props.loadedEntities.entitiesTranslations;
|
|
191
196
|
this.entitiesConfig = props.loadedEntities.entitiesConfig;
|
|
@@ -206,6 +211,9 @@ class Manager
|
|
|
206
211
|
return false;
|
|
207
212
|
}
|
|
208
213
|
}
|
|
214
|
+
if(0 < Object.keys(this.entityAccess).length){
|
|
215
|
+
await this.setupEntityAccess();
|
|
216
|
+
}
|
|
209
217
|
if(!this.loadProcessedEntities()){
|
|
210
218
|
return false;
|
|
211
219
|
}
|
|
@@ -228,6 +236,25 @@ class Manager
|
|
|
228
236
|
return true;
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
async setupEntityAccess()
|
|
240
|
+
{
|
|
241
|
+
let accessEntity = this.dataServer.getEntity('cmsEntityAccess');
|
|
242
|
+
if(!accessEntity){
|
|
243
|
+
Logger.warning('Entity access control table not found.');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
for(let entityName of Object.keys(this.entityAccess)){
|
|
247
|
+
let accessConfig = this.entityAccess[entityName];
|
|
248
|
+
if(!await accessEntity.loadOneBy('entity_name', entityName)){
|
|
249
|
+
await accessEntity.create({
|
|
250
|
+
entity_name: entityName,
|
|
251
|
+
is_public: sc.get(accessConfig, 'public', false),
|
|
252
|
+
allowed_operations: JSON.stringify(sc.get(accessConfig, 'operations', ['read']))
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
231
258
|
loadProcessedEntities()
|
|
232
259
|
{
|
|
233
260
|
if(0 === Object.keys(this.processedEntities).length){
|
|
@@ -354,12 +381,12 @@ class Manager
|
|
|
354
381
|
return true;
|
|
355
382
|
}
|
|
356
383
|
|
|
357
|
-
async renderCallback(template, params)
|
|
384
|
+
async renderCallback(template, params = {})
|
|
358
385
|
{
|
|
359
386
|
if(!template){
|
|
360
387
|
return '';
|
|
361
388
|
}
|
|
362
|
-
return
|
|
389
|
+
return this.renderEngine.render(template, params);
|
|
363
390
|
}
|
|
364
391
|
|
|
365
392
|
async initializeFrontend()
|
|
@@ -367,8 +394,13 @@ class Manager
|
|
|
367
394
|
this.frontend = new Frontend({
|
|
368
395
|
app: this.app,
|
|
369
396
|
dataServer: this.dataServer,
|
|
397
|
+
renderEngine: this.renderEngine,
|
|
370
398
|
projectRoot: this.projectRoot,
|
|
371
|
-
appServerFactory: this.appServerFactory
|
|
399
|
+
appServerFactory: this.appServerFactory,
|
|
400
|
+
defaultDomain: this.defaultDomain,
|
|
401
|
+
domainMapping: this.domainMapping,
|
|
402
|
+
siteKeyMapping: this.siteKeyMapping,
|
|
403
|
+
templateExtensions: this.templateExtensions
|
|
372
404
|
});
|
|
373
405
|
return await this.frontend.initialize();
|
|
374
406
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
-- Default CMS blocks:
|
|
3
|
+
|
|
4
|
+
REPLACE INTO `cms_blocks` (`name`, `title`, `content`, `is_active`) VALUES
|
|
5
|
+
('header-main', 'Main Header', '{{>header}}', 1),
|
|
6
|
+
('sidebar-left', 'Left Sidebar', '<aside class="sidebar-left">{{>sidebar}}</aside>', 1),
|
|
7
|
+
('sidebar-right', 'Right Sidebar', '<aside class="sidebar-right">{{>sidebar}}</aside>', 1),
|
|
8
|
+
('footer-main', 'Main Footer', '{{>footer}}', 1);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
-- Default entity access rules:
|
|
3
|
+
|
|
4
|
+
REPLACE INTO `cms_entity_access` (`entity_name`, `is_public`, `allowed_operations`) VALUES
|
|
5
|
+
('cms_pages', TRUE, '["read"]'),
|
|
6
|
+
('routes', FALSE, '[]'),
|
|
7
|
+
('users', FALSE, '[]'),
|
|
8
|
+
('cms_blocks', FALSE, '[]'),
|
|
9
|
+
('cms_entity_access', FALSE, '[]');
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
-- Create a default homepage route if not exists
|
|
5
5
|
REPLACE INTO `cms_pages` (`id`, `title`, `content`, `template`, `created_at`) VALUES
|
|
6
|
-
(1, 'Home', '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>',
|
|
6
|
+
(1, 'Home', '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>', NULL, NOW());
|
|
7
7
|
|
|
8
8
|
-- Create a default route to the homepage
|
|
9
9
|
REPLACE INTO `routes` (`id`, `path`, `router`, `content_id`, `title`, `meta_description`, `status`, `created_at`) VALUES
|
package/migrations/install.sql
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
-- Install Reldens CMS
|
|
3
3
|
|
|
4
4
|
CREATE TABLE IF NOT EXISTS `routes` (
|
|
5
|
-
`id` INT
|
|
5
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
6
6
|
`path` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
7
7
|
`router` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
8
|
-
`content_id` INT
|
|
8
|
+
`content_id` INT UNSIGNED NOT NULL,
|
|
9
9
|
`title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
10
10
|
`meta_description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
11
11
|
`canonical_url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
@@ -25,12 +25,10 @@ CREATE TABLE IF NOT EXISTS `routes` (
|
|
|
25
25
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
26
26
|
|
|
27
27
|
CREATE TABLE IF NOT EXISTS `cms_pages_meta` (
|
|
28
|
-
`id` INT
|
|
29
|
-
`page_id` INT
|
|
30
|
-
`layout` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
28
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
29
|
+
`page_id` INT UNSIGNED NOT NULL,
|
|
31
30
|
`publish_date` TIMESTAMP NULL,
|
|
32
31
|
`expire_date` TIMESTAMP NULL,
|
|
33
|
-
`author_id` INT(10) UNSIGNED NULL,
|
|
34
32
|
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
35
33
|
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
36
34
|
PRIMARY KEY (`id`) USING BTREE,
|
|
@@ -38,21 +36,22 @@ CREATE TABLE IF NOT EXISTS `cms_pages_meta` (
|
|
|
38
36
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
39
37
|
|
|
40
38
|
CREATE TABLE IF NOT EXISTS `cms_pages` (
|
|
41
|
-
`id` INT
|
|
39
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
42
40
|
`title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
43
41
|
`content` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
44
42
|
`markdown` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
45
43
|
`json_data` JSON NULL,
|
|
46
44
|
`template` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
45
|
+
`layout` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'default',
|
|
47
46
|
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
48
47
|
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
49
48
|
PRIMARY KEY (`id`) USING BTREE
|
|
50
49
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
51
50
|
|
|
52
51
|
CREATE TABLE IF NOT EXISTS `entities_meta` (
|
|
53
|
-
`id` INT
|
|
52
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
54
53
|
`entity_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
55
|
-
`entity_id` INT
|
|
54
|
+
`entity_id` INT UNSIGNED NOT NULL,
|
|
56
55
|
`meta_key` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
57
56
|
`meta_value` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
58
57
|
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
@@ -60,3 +59,29 @@ CREATE TABLE IF NOT EXISTS `entities_meta` (
|
|
|
60
59
|
PRIMARY KEY (`id`) USING BTREE,
|
|
61
60
|
UNIQUE KEY `entity_meta` (`entity_name`, `entity_id`, `meta_key`) USING BTREE
|
|
62
61
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
62
|
+
|
|
63
|
+
CREATE TABLE IF NOT EXISTS `cms_blocks` (
|
|
64
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
65
|
+
`name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
66
|
+
`title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
67
|
+
`content` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
68
|
+
`template_file` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
|
|
69
|
+
`variables` JSON NULL,
|
|
70
|
+
`is_active` BOOLEAN NOT NULL DEFAULT TRUE,
|
|
71
|
+
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
72
|
+
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
73
|
+
PRIMARY KEY (`id`) USING BTREE,
|
|
74
|
+
UNIQUE KEY `name` (`name`) USING BTREE
|
|
75
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS `cms_entity_access` (
|
|
78
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
79
|
+
`entity_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
80
|
+
`is_public` BOOLEAN NOT NULL DEFAULT FALSE,
|
|
81
|
+
`allowed_operations` JSON NULL DEFAULT ('["read"]'),
|
|
82
|
+
`access_rules` JSON NULL,
|
|
83
|
+
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
84
|
+
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
85
|
+
PRIMARY KEY (`id`) USING BTREE,
|
|
86
|
+
UNIQUE KEY `entity_name` (`entity_name`) USING BTREE
|
|
87
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
-- Default db-users authentication:
|
|
3
3
|
|
|
4
4
|
CREATE TABLE IF NOT EXISTS `users` (
|
|
5
|
-
`id` INT
|
|
5
|
+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
6
6
|
`email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
7
7
|
`username` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
8
8
|
`password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
9
|
-
`role_id` INT
|
|
9
|
+
`role_id` INT UNSIGNED NOT NULL,
|
|
10
10
|
`status` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
11
11
|
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
12
12
|
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/cms",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"description": "Reldens - CMS",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"url": "https://github.com/damian-pastorini/reldens-cms/issues"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@reldens/server-utils": "^0.
|
|
36
|
-
"@reldens/storage": "^0.
|
|
37
|
-
"@reldens/utils": "^0.
|
|
35
|
+
"@reldens/server-utils": "^0.18.0",
|
|
36
|
+
"@reldens/storage": "^0.47.0",
|
|
37
|
+
"@reldens/utils": "^0.49.0",
|
|
38
38
|
"dotenv": "^16.5.0",
|
|
39
39
|
"mustache": "^4.2.0"
|
|
40
40
|
}
|
package/templates/404.html
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<script src="/js/scripts.js"></script>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<header>
|
|
11
|
-
<div class="container">
|
|
12
|
-
<h1>Reldens CMS</h1>
|
|
13
|
-
<nav>
|
|
14
|
-
<a href="/">Home</a>
|
|
15
|
-
</nav>
|
|
1
|
+
<section class="hero d-flex align-items-center">
|
|
2
|
+
<div class="container text-center">
|
|
3
|
+
<h1>404</h1>
|
|
4
|
+
<h2>Page Not Found</h2>
|
|
5
|
+
<p>The page you are looking for doesn't exist.</p>
|
|
6
|
+
<a href="/" class="btn-get-started">Go Home</a>
|
|
16
7
|
</div>
|
|
17
|
-
</
|
|
18
|
-
<div class="container">
|
|
19
|
-
<main class="error-container">
|
|
20
|
-
<h1>404 - Page Not Found</h1>
|
|
21
|
-
<p>The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.</p>
|
|
22
|
-
<a href="/" class="button">Return to Homepage</a>
|
|
23
|
-
</main>
|
|
24
|
-
</div>
|
|
25
|
-
<footer>
|
|
26
|
-
<p>© {{current_year}} - Built with Reldens CMS</p>
|
|
27
|
-
</footer>
|
|
28
|
-
</body>
|
|
29
|
-
</html>
|
|
30
|
-
|
|
8
|
+
</section>
|
package/templates/css/styles.css
CHANGED
|
File without changes
|
package/templates/js/scripts.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Reldens - CMS - Scripts
|
|
3
|
+
*/
|
|
4
|
+
|
|
2
5
|
document.addEventListener('DOMContentLoaded', function() {
|
|
3
|
-
|
|
4
|
-
const currentPath = window.location.pathname;
|
|
5
|
-
const navLinks = document.querySelectorAll('nav a');
|
|
6
|
+
let currentPath = window.location.pathname;
|
|
6
7
|
|
|
7
|
-
navLinks.
|
|
8
|
-
|
|
8
|
+
let navLinks = document.querySelectorAll('nav a');
|
|
9
|
+
for(let link of navLinks){
|
|
10
|
+
if(link.getAttribute('href') === currentPath){
|
|
9
11
|
link.classList.add('active');
|
|
10
12
|
}
|
|
11
|
-
}
|
|
13
|
+
}
|
|
12
14
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
{{>header}}
|
|
3
|
+
|
|
4
|
+
{{ entity('cmsBlocks', 'header-main') }}
|
|
5
|
+
|
|
6
|
+
<main id="main" class="main-container">
|
|
7
|
+
<div class="container">
|
|
8
|
+
<div class="row">
|
|
9
|
+
<div class="col-md-3">
|
|
10
|
+
{{ entity('cmsBlocks', 'sidebar-left') }}
|
|
11
|
+
</div>
|
|
12
|
+
<div class="col-md-9">
|
|
13
|
+
{{{content}}}
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</main>
|
|
18
|
+
|
|
19
|
+
{{ entity('cmsBlocks', 'footer-main') }}
|
|
20
|
+
|
|
21
|
+
{{>footer}}
|
package/templates/page.html
CHANGED
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html lang="{{locale}}">
|
|
3
3
|
<head>
|
|
4
4
|
<title>{{title}}</title>
|
|
5
|
-
<meta
|
|
6
|
-
<meta
|
|
7
|
-
<meta
|
|
8
|
-
<meta
|
|
9
|
-
<meta
|
|
10
|
-
<meta
|
|
11
|
-
|
|
12
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
|
13
|
-
<link rel="stylesheet" href="/css/styles.css">
|
|
14
|
-
<script src="/js/scripts.js"></script>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover"/>
|
|
7
|
+
<meta name="theme-color" content="#000000"/>
|
|
8
|
+
<meta name="description" content="{{description}}"/>
|
|
9
|
+
<meta property="og:title" content="{{title}}"/>
|
|
10
|
+
<meta property="og:description" content="{{description}}"/>
|
|
11
|
+
<link href="/css/styles.css" rel="stylesheet"/>
|
|
15
12
|
</head>
|
|
16
|
-
<body>
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{{#navigation}}
|
|
23
|
-
<a href="{{url}}">{{label}}</a>
|
|
24
|
-
{{/navigation}}
|
|
25
|
-
</nav>
|
|
26
|
-
</div>
|
|
27
|
-
</header>
|
|
28
|
-
<div class="container">
|
|
29
|
-
<main>
|
|
30
|
-
<h1>{{title}}</h1>
|
|
31
|
-
{{{content}}}
|
|
32
|
-
</main>
|
|
33
|
-
</div>
|
|
34
|
-
<footer>
|
|
35
|
-
<p>© {{current_year}} - Built with Reldens CMS</p>
|
|
36
|
-
</footer>
|
|
13
|
+
<body class="{{siteHandle}}{{#elementClassName}} {{elementClassName}}{{/elementClassName}}"{{#backgroundImage}} style="background-image: url('{{backgroundImage}}');"{{/backgroundImage}}>
|
|
14
|
+
{{{content}}}
|
|
15
|
+
<script type="text/javascript" defer src="/js/scripts.js"></script>
|
|
16
|
+
<script type="text/javascript">
|
|
17
|
+
const currentYear = {{currentYear}};
|
|
18
|
+
</script>
|
|
37
19
|
</body>
|
|
38
20
|
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<div class="footer-top">
|
|
2
|
+
<div class="container top-border">
|
|
3
|
+
<div class="row">
|
|
4
|
+
<div class="col-lg-4 col-md-6 footer-contact">
|
|
5
|
+
<h3>{{site_name}}</h3>
|
|
6
|
+
<p><strong>E-mail:</strong> <a href="mailto:{{contact_email}}">{{contact_email}}</a></p>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="col-lg-4 col-md-6 footer-links">
|
|
9
|
+
{{#footer_menu}}
|
|
10
|
+
<h4>Useful Links</h4>
|
|
11
|
+
<ul>
|
|
12
|
+
{{#items}}
|
|
13
|
+
<li><a href="{{url}}">{{title}}</a></li>
|
|
14
|
+
{{/items}}
|
|
15
|
+
</ul>
|
|
16
|
+
{{/footer_menu}}
|
|
17
|
+
</div>
|
|
18
|
+
{{#social_links}}
|
|
19
|
+
<div class="col-lg-4 col-md-12 footer-links text-right">
|
|
20
|
+
<h4>Networks</h4>
|
|
21
|
+
<div class="social-links mt-3">
|
|
22
|
+
{{#links}}
|
|
23
|
+
<a href="{{url}}" target="_blank"><i class="bx {{icon}}"></i></a>
|
|
24
|
+
{{/links}}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
{{/social_links}}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="container footer-bottom clearfix">
|
|
32
|
+
<div class="copyright">
|
|
33
|
+
© Copyright {{currentYear}} <strong><a href="https://www.dwdeveloper.com" target="_blank">DwDeveloper</a></strong>. All Rights Reserved
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<div class="container d-flex align-items-center">
|
|
2
|
+
<div class="logo{{#is_reldens}} reldens-logo{{/is_reldens}}">
|
|
3
|
+
{{#logo_image}}
|
|
4
|
+
<a href="{{home_url}}">
|
|
5
|
+
<img src="{{logo_image}}" alt="{{site_name}}">
|
|
6
|
+
</a>
|
|
7
|
+
{{/logo_image}}
|
|
8
|
+
{{^logo_image}}
|
|
9
|
+
<a href="{{home_url}}">{{site_name}}</a>
|
|
10
|
+
{{/logo_image}}
|
|
11
|
+
</div>
|
|
12
|
+
{{#menu_items}}
|
|
13
|
+
<nav class="nav-menu d-none d-lg-block">
|
|
14
|
+
<ul>
|
|
15
|
+
{{#items}}
|
|
16
|
+
<li{{#has_children}} class="drop-down"{{/has_children}}>
|
|
17
|
+
<a href="{{url}}"{{#is_external}} target="_blank"{{/is_external}}>{{title}}</a>
|
|
18
|
+
{{#has_children}}
|
|
19
|
+
<ul>
|
|
20
|
+
{{#children}}
|
|
21
|
+
<li>
|
|
22
|
+
<a href="{{url}}"{{#is_external}} target="_blank"{{/is_external}}>{{title}}</a>
|
|
23
|
+
</li>
|
|
24
|
+
{{/children}}
|
|
25
|
+
</ul>
|
|
26
|
+
{{/has_children}}
|
|
27
|
+
</li>
|
|
28
|
+
{{/items}}
|
|
29
|
+
</ul>
|
|
30
|
+
</nav>
|
|
31
|
+
{{/menu_items}}
|
|
32
|
+
<form class="search" action="/search" method="get">
|
|
33
|
+
<input type="search" class="quicksearch" name="q" value="{{search_query}}" placeholder="Search..."/>
|
|
34
|
+
<button type="submit"><i class="bx bx-search"></i></button>
|
|
35
|
+
</form>
|
|
36
|
+
</div>
|