@reldens/cms 0.5.0 → 0.7.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 +1 -1
- package/admin/assets/admin/filters.png +0 -0
- package/admin/assets/admin/list.png +0 -0
- package/admin/reldens-admin-client.css +830 -0
- package/admin/reldens-admin-client.js +272 -0
- package/admin/templates/dashboard.html +1 -0
- package/admin/templates/default-copyright.html +5 -0
- package/admin/templates/edit.html +25 -0
- package/admin/templates/fields/edit/button.html +3 -0
- package/admin/templates/fields/edit/checkbox.html +1 -0
- package/admin/templates/fields/edit/file.html +2 -0
- package/admin/templates/fields/edit/radio.html +1 -0
- package/admin/templates/fields/edit/select.html +5 -0
- package/admin/templates/fields/edit/text.html +1 -0
- package/admin/templates/fields/edit/textarea.html +1 -0
- package/admin/templates/fields/view/boolean.html +1 -0
- package/admin/templates/fields/view/image.html +4 -0
- package/admin/templates/fields/view/images.html +7 -0
- package/admin/templates/fields/view/link.html +1 -0
- package/admin/templates/fields/view/links.html +6 -0
- package/admin/templates/fields/view/text.html +1 -0
- package/admin/templates/layout.html +37 -0
- package/admin/templates/list-content.html +70 -0
- package/admin/templates/list.html +35 -0
- package/admin/templates/login.html +19 -0
- package/admin/templates/management.html +22 -0
- package/admin/templates/pagination-link.html +1 -0
- package/admin/templates/sidebar-header.html +4 -0
- package/admin/templates/sidebar-item.html +3 -0
- package/admin/templates/sidebar.html +11 -0
- package/admin/templates/view.html +23 -0
- package/bin/reldens-cms.js +20 -8
- package/index.js +2 -2
- package/install/index.html +0 -7
- package/install/success.html +30 -0
- package/lib/admin-manager.js +1 -1
- package/lib/admin-templates-loader.js +37 -0
- package/lib/admin-translations.js +4 -218
- package/lib/allowed-extensions.js +11 -0
- package/lib/entities-loader.js +42 -0
- package/lib/{storefront.js → frontend.js} +14 -19
- package/lib/installer.js +137 -52
- package/lib/loaded-entities-processor.js +30 -0
- package/lib/manager.js +135 -37
- package/lib/mime-types.js +35 -0
- package/lib/templates-list.js +0 -9
- package/lib/templates-to-path-mapper.js +28 -0
- package/migrations/default-user.sql +2 -1
- package/package.json +2 -2
- package/templates/.env.dist +11 -11
- package/templates/assets/web/loading.gif +0 -0
- package/templates/assets/web/reldens-your-logo-mage.png +0 -0
- package/templates/css/styles.css +1 -1
- package/templates/index.js.dist +32 -0
- package/templates/js/scripts.js +1 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Admin Client JS
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
8
|
+
|
|
9
|
+
// helpers:
|
|
10
|
+
let location = window.location;
|
|
11
|
+
let currentPath = location.pathname;
|
|
12
|
+
let queryString = location.search;
|
|
13
|
+
let urlParams = new URLSearchParams(queryString);
|
|
14
|
+
|
|
15
|
+
function getCookie(name)
|
|
16
|
+
{
|
|
17
|
+
let value = `; ${document.cookie}`;
|
|
18
|
+
let parts = value.split(`; ${name}=`);
|
|
19
|
+
if(2 === parts.length){
|
|
20
|
+
return parts.pop().split(';').shift()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function deleteCookie(name)
|
|
25
|
+
{
|
|
26
|
+
document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function escapeHTML(str)
|
|
30
|
+
{
|
|
31
|
+
return str.replace(/&/g, "&")
|
|
32
|
+
.replace(/</g, "<")
|
|
33
|
+
.replace(/>/g, ">")
|
|
34
|
+
.replace(/"/g, """)
|
|
35
|
+
.replace(/'/g, "'");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function cloneElement(element)
|
|
39
|
+
{
|
|
40
|
+
if(element instanceof HTMLCanvasElement){
|
|
41
|
+
let clonedCanvas = document.createElement('canvas');
|
|
42
|
+
clonedCanvas.width = element.width;
|
|
43
|
+
clonedCanvas.height = element.height;
|
|
44
|
+
let ctx = clonedCanvas.getContext('2d');
|
|
45
|
+
ctx.drawImage(element, 0, 0);
|
|
46
|
+
return clonedCanvas
|
|
47
|
+
}
|
|
48
|
+
return element.cloneNode(true);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// error codes messages map:
|
|
52
|
+
let errorMessages = {
|
|
53
|
+
saveBadPatchData: 'Bad patch data on update.',
|
|
54
|
+
saveEntityStorageError: 'Entity storage error.',
|
|
55
|
+
saveEntityError: 'Entity could not be saved.',
|
|
56
|
+
shutdownError: 'Server could not be shutdown, missing "shutdownTime".',
|
|
57
|
+
errorView: 'Could not render view page.',
|
|
58
|
+
errorEdit: 'Could not render edit page.',
|
|
59
|
+
errorId: 'Missing entity ID on POST.'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// activate expand/collapse elements
|
|
63
|
+
let expandCollapseButtons = document.querySelectorAll('[data-expand-collapse]');
|
|
64
|
+
if(expandCollapseButtons){
|
|
65
|
+
for(let expandCollapseButton of expandCollapseButtons){
|
|
66
|
+
expandCollapseButton.addEventListener('click', (event) => {
|
|
67
|
+
let expandCollapseElement = document.querySelector(event.currentTarget.dataset.expandCollapse);
|
|
68
|
+
if(expandCollapseElement){
|
|
69
|
+
expandCollapseElement.classList.toggle('hidden');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// activate modals on click
|
|
76
|
+
let modalElements = document.querySelectorAll('[data-toggle="modal"]');
|
|
77
|
+
if(modalElements){
|
|
78
|
+
for(let modalElement of modalElements){
|
|
79
|
+
modalElement.addEventListener('click', () => {
|
|
80
|
+
let overlay = document.createElement('div');
|
|
81
|
+
overlay.classList.add('modal-overlay');
|
|
82
|
+
let modal = document.createElement('div');
|
|
83
|
+
modal.classList.add('modal');
|
|
84
|
+
modal.classList.add('clickable');
|
|
85
|
+
let clonedElement = cloneElement(modalElement);
|
|
86
|
+
clonedElement.classList.add('clickable');
|
|
87
|
+
modal.appendChild(clonedElement);
|
|
88
|
+
overlay.appendChild(modal);
|
|
89
|
+
document.body.appendChild(overlay);
|
|
90
|
+
clonedElement.addEventListener('click', () => {
|
|
91
|
+
document.body.removeChild(overlay);
|
|
92
|
+
});
|
|
93
|
+
modal.addEventListener('click', (e) => {
|
|
94
|
+
if(e.target === modal){
|
|
95
|
+
document.body.removeChild(modal.parentNode);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
overlay.addEventListener('click', (e) => {
|
|
99
|
+
if(e.target === overlay) {
|
|
100
|
+
document.body.removeChild(overlay);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// login errors:
|
|
108
|
+
if('true' === urlParams.get('login-error')){
|
|
109
|
+
let loginErrorBox = document.querySelector('form.login-form .response-error');
|
|
110
|
+
if(loginErrorBox){
|
|
111
|
+
loginErrorBox.innerHTML = 'Login error, please try again.';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// forms behavior:
|
|
116
|
+
let forms = document.querySelectorAll('form');
|
|
117
|
+
if(forms){
|
|
118
|
+
for(let form of forms){
|
|
119
|
+
form.addEventListener('submit', (event) => {
|
|
120
|
+
let submitButton = document.querySelector('input[type="submit"]');
|
|
121
|
+
submitButton.disabled = true;
|
|
122
|
+
let loadingImage = document.querySelector('.submit-container .loading');
|
|
123
|
+
if(loadingImage){
|
|
124
|
+
loadingImage.classList.remove('hidden');
|
|
125
|
+
}
|
|
126
|
+
if(form.classList.contains('form-delete') || form.classList.contains('confirmation-required')){
|
|
127
|
+
if(!confirm('Are you sure?')){
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
submitButton.disabled = false;
|
|
130
|
+
loadingImage.classList.add('hidden');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// sidebar headers click behavior:
|
|
138
|
+
let sideBarHeaders = document.querySelectorAll('.with-sub-items h3');
|
|
139
|
+
if(sideBarHeaders){
|
|
140
|
+
for(let header of sideBarHeaders){
|
|
141
|
+
header.addEventListener('click', (event) => {
|
|
142
|
+
event.currentTarget.parentNode.classList.toggle('active');
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// expand menu on load:
|
|
148
|
+
let subItemContainers = document.querySelectorAll('.with-sub-items');
|
|
149
|
+
if(subItemContainers){
|
|
150
|
+
let done = false;
|
|
151
|
+
for(let container of subItemContainers){
|
|
152
|
+
let links = container.querySelectorAll('.side-bar-item a');
|
|
153
|
+
for(let link of links){
|
|
154
|
+
let linkWithoutHost = link.href.replace(location.host, '').replace(location.protocol+'//', '');
|
|
155
|
+
if(currentPath === linkWithoutHost || 0 === currentPath.indexOf(linkWithoutHost+'/')){
|
|
156
|
+
link.parentNode.classList.add('active');
|
|
157
|
+
container.classList.add('active');
|
|
158
|
+
done = true;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if(done){
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// filters toggle visibility:
|
|
169
|
+
let filtersToggle = document.querySelector('.filters-toggle');
|
|
170
|
+
let filtersToggleContent = document.querySelector('.filters-toggle-content');
|
|
171
|
+
if(filtersToggle && filtersToggleContent){
|
|
172
|
+
filtersToggle.addEventListener('click', () => {
|
|
173
|
+
filtersToggleContent.classList.toggle('hidden');
|
|
174
|
+
});
|
|
175
|
+
let allFilters = document.querySelectorAll('.filters-toggle-content .filter input');
|
|
176
|
+
let activeFilters = Array.from(allFilters).filter(input => '' !== input.value);
|
|
177
|
+
if(0 < activeFilters.length){
|
|
178
|
+
filtersToggleContent.classList.remove('hidden');
|
|
179
|
+
let paginationLinks = document.querySelectorAll('.pagination a');
|
|
180
|
+
let filtersForm = document.querySelector('#filter-form');
|
|
181
|
+
if(paginationLinks && filtersForm){
|
|
182
|
+
for(let link of paginationLinks){
|
|
183
|
+
link.addEventListener('click', (event) => {
|
|
184
|
+
event.stopPropagation();
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
filtersForm.action = link.href;
|
|
187
|
+
filtersForm.submit();
|
|
188
|
+
return false;
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// list "select all" option:
|
|
196
|
+
let listSelect = document.querySelector('.list-select');
|
|
197
|
+
if(listSelect){
|
|
198
|
+
listSelect.addEventListener('click', (event) => {
|
|
199
|
+
let checkboxes = document.querySelectorAll('.ids-checkbox');
|
|
200
|
+
for(let checkbox of checkboxes){
|
|
201
|
+
checkbox.checked = 1 === Number(event.currentTarget.dataset.checked);
|
|
202
|
+
}
|
|
203
|
+
event.currentTarget.dataset.checked = 1 === Number(event.currentTarget.dataset.checked) ? 0 : 1;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// list delete selection:
|
|
208
|
+
let listDeleteSelection = document.querySelector('.list-delete-selection');
|
|
209
|
+
let deleteSelectionForm = document.getElementById('delete-selection-form');
|
|
210
|
+
let hiddenInput = document.querySelector('.hidden-ids-input');
|
|
211
|
+
if(listDeleteSelection && deleteSelectionForm && hiddenInput){
|
|
212
|
+
listDeleteSelection.addEventListener('click', () => {
|
|
213
|
+
if(!confirm('Are you sure?')){
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
let checkboxes = document.querySelectorAll('.ids-checkbox');
|
|
217
|
+
let ids = [];
|
|
218
|
+
for(let checkbox of checkboxes){
|
|
219
|
+
if(checkbox.checked){
|
|
220
|
+
ids.push(checkbox.value);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
hiddenInput.value = ids.join(',');
|
|
224
|
+
deleteSelectionForm.submit();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// display notifications from query params:
|
|
229
|
+
let notificationElement = document.querySelector('.notification');
|
|
230
|
+
if(notificationElement){
|
|
231
|
+
let closeNotificationElement = document.querySelector('.notification .close');
|
|
232
|
+
closeNotificationElement?.addEventListener('click', () => {
|
|
233
|
+
notificationElement.classList.remove('success', 'error');
|
|
234
|
+
});
|
|
235
|
+
let queryParams = new URLSearchParams(location.search);
|
|
236
|
+
let result = queryParams.get('result');
|
|
237
|
+
if(!result){
|
|
238
|
+
result = getCookie('result');
|
|
239
|
+
}
|
|
240
|
+
let notificationMessageElement = document.querySelector('.notification .message');
|
|
241
|
+
if(result && notificationMessageElement){
|
|
242
|
+
let notificationClass = 'success' === result ? 'success' : 'error';
|
|
243
|
+
notificationMessageElement.innerHTML = '';
|
|
244
|
+
notificationElement.classList.add(notificationClass);
|
|
245
|
+
notificationMessageElement.innerHTML = 'success' === result
|
|
246
|
+
? 'Success!'
|
|
247
|
+
: 'There was an error: '+escapeHTML(errorMessages[result] || result);
|
|
248
|
+
deleteCookie('result');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// shutdown timer:
|
|
253
|
+
let shuttingDownTimeElement = document.querySelector('.shutting-down .shutting-down-time');
|
|
254
|
+
if(shuttingDownTimeElement){
|
|
255
|
+
let shuttingDownTime = shuttingDownTimeElement.getAttribute('data-shutting-down-time');
|
|
256
|
+
if(shuttingDownTime){
|
|
257
|
+
shuttingDownTimeElement.innerHTML = escapeHTML(String(shuttingDownTime))+'s';
|
|
258
|
+
shuttingDownTime = Number(shuttingDownTime);
|
|
259
|
+
let shuttingDownTimer = setInterval(
|
|
260
|
+
() => {
|
|
261
|
+
shuttingDownTimeElement.innerHTML = escapeHTML(String(shuttingDownTime))+'s';
|
|
262
|
+
shuttingDownTime--;
|
|
263
|
+
if(0 === Number(shuttingDownTime)){
|
|
264
|
+
clearInterval(shuttingDownTimer);
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
1000
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Welcome to the Reldens CMS!
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div class="entity-edit {{&entityName}}-edit">
|
|
2
|
+
<h2>{{&templateTitle}}</h2>
|
|
3
|
+
<form name="edit-form" id="edit-form" action="{{&entitySaveRoute}}" method="post"{{&multipartFormData}}>
|
|
4
|
+
<input type="hidden" name="{{&idProperty}}" value="{{&idValue}}"/>
|
|
5
|
+
<div class="edit-field">
|
|
6
|
+
<span class="field-name">{{&idPropertyLabel}}</span>
|
|
7
|
+
<span class="field-value">
|
|
8
|
+
<input type="text" name="disabled-{{&idProperty}}" value="{{&idValue}}" disabled="disabled"/>
|
|
9
|
+
</span>
|
|
10
|
+
</div>
|
|
11
|
+
{{#editFields}}
|
|
12
|
+
<div class="edit-field">
|
|
13
|
+
<span class="field-name">{{&name}}</span>
|
|
14
|
+
<span class="field-value">{{&value}}</span>
|
|
15
|
+
</div>
|
|
16
|
+
{{/editFields}}
|
|
17
|
+
<div class="actions">
|
|
18
|
+
<input class="button button-primary button-submit" type="submit" value="Save"/>
|
|
19
|
+
<a class="button button-secondary button-back" href="{{&entityViewRoute}}">Cancel</a>
|
|
20
|
+
</div>
|
|
21
|
+
</form>
|
|
22
|
+
<div class="extra-actions">
|
|
23
|
+
{{&extraContent}}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<input type="checkbox" name="{{&fieldName}}" value="1"{{&fieldValue}}{{&fieldDisabled}}/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<input type="radio" name="{{&fieldName}}" value="{{&fieldValue}}"{{checked}}{{&fieldDisabled}}/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<input type="text" name="{{&fieldName}}" id="{{&fieldName}}" value="{{fieldValue}}"{{&required}}{{&fieldDisabled}}/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<textarea name="{{&fieldName}}" id="{{&fieldName}}"{{&required}}{{&fieldDisabled}}>{{&fieldValue}}</textarea>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{&fieldValue}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<a href="{{&fieldValue}}"{{&target}}>{{&fieldOriginalValue}}</a>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{&fieldValue}}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>{{&brandingCompanyName}}</title>
|
|
5
|
+
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Play:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet"/>
|
|
6
|
+
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.6.0/firebase-ui-auth.css"/>
|
|
7
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicons/apple-touch-icon.png"/>
|
|
8
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicons/favicon-32x32.png"/>
|
|
9
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicons/favicon-16x16.png"/>
|
|
10
|
+
<link rel="manifest" href="/site.webmanifest"/>
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
|
12
|
+
<link rel="stylesheet" type="text/css" href="{{stylesFilePath}}"/>
|
|
13
|
+
<script type="module" src="{{scriptsFilePath}}"></script>
|
|
14
|
+
</head>
|
|
15
|
+
<body class="reldens-admin-panel">
|
|
16
|
+
<div class="wrapper">
|
|
17
|
+
<div class="header">
|
|
18
|
+
<h1 class="title">
|
|
19
|
+
<a href="{{rootPath}}">{{&brandingCompanyName}}</a>
|
|
20
|
+
</h1>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="content">
|
|
23
|
+
<div class="notification">
|
|
24
|
+
<span class="close">×</span>
|
|
25
|
+
<span class="message"></span>
|
|
26
|
+
</div>
|
|
27
|
+
{{&sideBar}}
|
|
28
|
+
<div class="main-content">
|
|
29
|
+
{{&pageContent}}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="footer">
|
|
33
|
+
{{©Right}}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</body>
|
|
37
|
+
</html>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<tr class="row row-header">
|
|
2
|
+
<th class="field field-actions">
|
|
3
|
+
<div class="field-actions-container">
|
|
4
|
+
<button class="button button-primary list-select" data-checked="1">Select All/None</button>
|
|
5
|
+
<form name="delete-selection-form" id="delete-selection-form" action="{{&deletePath}}" method="post">
|
|
6
|
+
<input type="hidden" name="ids" class="hidden-ids-input"/>
|
|
7
|
+
<button class="button button-danger list-delete-selection">Delete Selection</button>
|
|
8
|
+
</form>
|
|
9
|
+
</div>
|
|
10
|
+
</th>
|
|
11
|
+
{{#fieldsHeaders}}
|
|
12
|
+
<th class="field field-{{&name}}">
|
|
13
|
+
<span>{{&value}}</span>
|
|
14
|
+
</th>
|
|
15
|
+
{{/fieldsHeaders}}
|
|
16
|
+
<th class="field field-edit">
|
|
17
|
+
<span>Edit</span>
|
|
18
|
+
</th>
|
|
19
|
+
<th class="field field-delete">
|
|
20
|
+
<span>Delete</span>
|
|
21
|
+
</th>
|
|
22
|
+
</tr>
|
|
23
|
+
{{#rows}}
|
|
24
|
+
<tr class="row">
|
|
25
|
+
<td class="field field-actions">
|
|
26
|
+
<div class="field-actions-container">
|
|
27
|
+
<input class="ids-checkbox" type="checkbox" name="idsSelection[]" value="{{&id}}"/>
|
|
28
|
+
</div>
|
|
29
|
+
</td>
|
|
30
|
+
{{#fields}}
|
|
31
|
+
<td class="field field-{{&name}}">
|
|
32
|
+
<a href="{{&viewLink}}">
|
|
33
|
+
<span>{{&value}}</span>
|
|
34
|
+
</a>
|
|
35
|
+
</td>
|
|
36
|
+
{{/fields}}
|
|
37
|
+
<td class="field field-edit">
|
|
38
|
+
<a href="{{&editLink}}">
|
|
39
|
+
<span>
|
|
40
|
+
<svg
|
|
41
|
+
class="feather feather-edit"
|
|
42
|
+
fill="none"
|
|
43
|
+
height="24"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
stroke-linecap="round"
|
|
46
|
+
stroke-linejoin="round"
|
|
47
|
+
stroke-width="2"
|
|
48
|
+
viewBox="0 0 24 24"
|
|
49
|
+
width="24"
|
|
50
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
51
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
52
|
+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
53
|
+
</svg>
|
|
54
|
+
</span>
|
|
55
|
+
</a>
|
|
56
|
+
</td>
|
|
57
|
+
<td class="field field-delete">
|
|
58
|
+
<form class="form-delete" name="delete-form-{{&id}}" id="delete-form-{{&id}}" action="{{&deleteLink}}" method="post">
|
|
59
|
+
<span>
|
|
60
|
+
<input type="hidden" name="ids[]" value="{{&id}}"/>
|
|
61
|
+
<button class="button-list-delete" type="submit">
|
|
62
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
63
|
+
<path d="M10,18a1,1,0,0,0,1-1V11a1,1,0,0,0-2,0v6A1,1,0,0,0,10,18ZM20,6H16V5a3,3,0,0,0-3-3H11A3,3,0,0,0,8,5V6H4A1,1,0,0,0,4,8H5V19a3,3,0,0,0,3,3h8a3,3,0,0,0,3-3V8h1a1,1,0,0,0,0-2ZM10,5a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1V6H10Zm7,14a1,1,0,0,1-1,1H8a1,1,0,0,1-1-1V8H17Zm-3-1a1,1,0,0,0,1-1V11a1,1,0,0,0-2,0v6A1,1,0,0,0,14,18Z" fill="#6563ff"/>
|
|
64
|
+
</svg>
|
|
65
|
+
</button>
|
|
66
|
+
</span>
|
|
67
|
+
</form>
|
|
68
|
+
</td>
|
|
69
|
+
</tr>
|
|
70
|
+
{{/rows}}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<div class="entity-list {{&entityName}}-list">
|
|
2
|
+
<h2 class="list-title">{{&templateTitle}}</h2>
|
|
3
|
+
<div class="actions">
|
|
4
|
+
<a class="button button-primary" href="{{&entityEditRoute}}">Create New</a>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="extra-actions">
|
|
7
|
+
{{&extraContent}}
|
|
8
|
+
</div>
|
|
9
|
+
<div class="sub-content filters">
|
|
10
|
+
<form class="sub-content-form filter-form" name="filter-form" id="filter-form" action="{{&entityListRoute}}" method="post">
|
|
11
|
+
<h4 class="filters-toggle">
|
|
12
|
+
<img src="/assets/admin/filters.png" alt="Filters"/>
|
|
13
|
+
<span>Filters</span>
|
|
14
|
+
</h4>
|
|
15
|
+
<div class="filters-toggle-content hidden">
|
|
16
|
+
{{#filters}}
|
|
17
|
+
<div class="sub-content-box filter">
|
|
18
|
+
<label for="filter-{{&propertyKey}}">{{&name}}</label>
|
|
19
|
+
<input type="text" id="filter-{{&propertyKey}}" name="filters[{{&propertyKey}}]"{{&value}}/>
|
|
20
|
+
</div>
|
|
21
|
+
{{/filters}}
|
|
22
|
+
<div class="actions">
|
|
23
|
+
<input class="button button-primary button-filter" type="submit" value="Filter"/>
|
|
24
|
+
<a class="button button-secondary button-clear-filter" href="{{&entityListRoute}}">Clear Filters</a>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</form>
|
|
28
|
+
</div>
|
|
29
|
+
<table class="list">
|
|
30
|
+
{{&list}}
|
|
31
|
+
</table>
|
|
32
|
+
<div class="pagination">
|
|
33
|
+
{{&pagination}}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="forms-container">
|
|
2
|
+
<div class="row">
|
|
3
|
+
<form name="login-form" id="login-form" class="login-form" action="#" method="post">
|
|
4
|
+
<h3 class="form-title">Login</h3>
|
|
5
|
+
<div class="input-box login-email">
|
|
6
|
+
<label for="email">E-mail</label>
|
|
7
|
+
<input type="text" name="email" id="email" class="required" required autocomplete="on"/>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="input-box login-password">
|
|
10
|
+
<label for="password">Password</label>
|
|
11
|
+
<input type="password" name="password" id="password" class="required" required autocomplete="off"/>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="input-box submit-container login-submit">
|
|
14
|
+
<input class="button button-primary button-login" type="submit" value="Submit"/>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="input-box response-error"></div>
|
|
17
|
+
</form>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<h2>Server Management</h2>
|
|
2
|
+
<div class="sub-content">
|
|
3
|
+
<div class="sub-content-box">
|
|
4
|
+
<h3>Server Maintenance</h3>
|
|
5
|
+
<form class="sub-content-form management-form confirmation-required" name="management-form" id="management-form" action="{{&actionPath}}" method="post">
|
|
6
|
+
<div>
|
|
7
|
+
<label for="shutdown-time">
|
|
8
|
+
<span>Shutdown server with user notifications in seconds:</span>
|
|
9
|
+
<input type="number" name="shutdown-time" id="shutdown-time" value="{{&shutdownTime}}"/>
|
|
10
|
+
</label>
|
|
11
|
+
<p class="alert">IMPORTANT: this will take down the administration panel as well, and you will be logged out.</p>
|
|
12
|
+
<div class="shutting-down">
|
|
13
|
+
<span class="shutting-down-label">{{&shuttingDownLabel}}</span>
|
|
14
|
+
<span class="shutting-down-time" data-shutting-down-time="{{&shuttingDownTime}}">
|
|
15
|
+
{{&shuttingDownTime}}
|
|
16
|
+
</span>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<input type="submit" class="button button-{{&submitType}} button-management" value="{{&submitLabel}}"/>
|
|
20
|
+
</form>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<a class="pagination-link {{&pageClass}}" href="{{&pageLink}}">{{&pageLabel}}</a>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="entity-view {{&entityName}}-view">
|
|
2
|
+
<h2>{{&templateTitle}}</h2>
|
|
3
|
+
{{#fields}}
|
|
4
|
+
<div class="view-field">
|
|
5
|
+
<span class="field-name">{{&name}}</span>
|
|
6
|
+
<span class="field-value">{{&value}}</span>
|
|
7
|
+
</div>
|
|
8
|
+
{{/fields}}
|
|
9
|
+
<div class="actions">
|
|
10
|
+
<a class="button button-primary" href="{{&entityNewRoute}}">Create New</a>
|
|
11
|
+
<a class="button button-primary" href="{{&entityEditRoute}}">Edit</a>
|
|
12
|
+
<a class="button button-secondary" href="{{&entityListRoute}}">Back</a>
|
|
13
|
+
<form class="form-delete" name="delete-form-{{&id}}" id="delete-form-{{&id}}" action="{{&entityDeleteRoute}}" method="post">
|
|
14
|
+
<span>
|
|
15
|
+
<input type="hidden" name="ids[]" value="{{&id}}"/>
|
|
16
|
+
<button class="button button-danger" type="submit">Delete</button>
|
|
17
|
+
</span>
|
|
18
|
+
</form>
|
|
19
|
+
<div class="extra-actions">
|
|
20
|
+
{{&extraContent}}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
package/bin/reldens-cms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -7,15 +7,27 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { Manager } = require('../index');
|
|
10
|
+
const { Logger } = require('@reldens/utils');
|
|
10
11
|
|
|
11
12
|
let args = process.argv.slice(2);
|
|
12
|
-
let projectRoot = args[0] ||
|
|
13
|
+
let projectRoot = args[0] || process.cwd();
|
|
13
14
|
|
|
14
|
-
let manager = new Manager({
|
|
15
|
-
|
|
16
|
-
});
|
|
15
|
+
let manager = new Manager({projectRoot});
|
|
16
|
+
Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
|
|
17
17
|
|
|
18
|
-
manager.start().
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let started = manager.start().then((result) => {
|
|
19
|
+
if(!result){
|
|
20
|
+
Logger.info('Reldens CMS started by command failed.');
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
Logger.info('Reldens CMS started by command.');
|
|
24
|
+
return true;
|
|
25
|
+
}).catch((error) => {
|
|
26
|
+
Logger.critical('Failed to start CMS:', error);
|
|
27
|
+
process.exit();
|
|
21
28
|
});
|
|
29
|
+
|
|
30
|
+
if(!started){
|
|
31
|
+
Logger.error('Reldens CMS start process failed.');
|
|
32
|
+
process.exit();
|
|
33
|
+
}
|