@reldens/cms 0.4.0 → 0.6.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 (48) hide show
  1. package/README.md +1 -1
  2. package/admin/assets/admin/filters.png +0 -0
  3. package/admin/assets/admin/list.png +0 -0
  4. package/admin/reldens-admin-client.css +830 -0
  5. package/admin/reldens-admin-client.js +272 -0
  6. package/admin/templates/dashboard.html +1 -0
  7. package/admin/templates/default-copyright.html +5 -0
  8. package/admin/templates/edit.html +25 -0
  9. package/admin/templates/fields/edit/button.html +3 -0
  10. package/admin/templates/fields/edit/checkbox.html +1 -0
  11. package/admin/templates/fields/edit/file.html +2 -0
  12. package/admin/templates/fields/edit/radio.html +1 -0
  13. package/admin/templates/fields/edit/select.html +5 -0
  14. package/admin/templates/fields/edit/text.html +1 -0
  15. package/admin/templates/fields/edit/textarea.html +1 -0
  16. package/admin/templates/fields/view/boolean.html +1 -0
  17. package/admin/templates/fields/view/image.html +4 -0
  18. package/admin/templates/fields/view/images.html +7 -0
  19. package/admin/templates/fields/view/link.html +1 -0
  20. package/admin/templates/fields/view/links.html +6 -0
  21. package/admin/templates/fields/view/text.html +1 -0
  22. package/admin/templates/layout.html +37 -0
  23. package/admin/templates/list-content.html +70 -0
  24. package/admin/templates/list.html +35 -0
  25. package/admin/templates/login.html +19 -0
  26. package/admin/templates/management.html +22 -0
  27. package/admin/templates/maps-wizard-maps-selection.html +85 -0
  28. package/admin/templates/maps-wizard.html +341 -0
  29. package/admin/templates/objects-import.html +143 -0
  30. package/admin/templates/pagination-link.html +1 -0
  31. package/admin/templates/sidebar-header.html +4 -0
  32. package/admin/templates/sidebar-item.html +3 -0
  33. package/admin/templates/sidebar.html +11 -0
  34. package/admin/templates/skills-import.html +201 -0
  35. package/admin/templates/view.html +23 -0
  36. package/bin/reldens-cms.js +19 -7
  37. package/index.js +2 -2
  38. package/install/index.html +5 -7
  39. package/lib/entities-loader.js +45 -0
  40. package/lib/{storefront.js → frontend.js} +10 -6
  41. package/lib/installer.js +150 -49
  42. package/lib/manager.js +65 -26
  43. package/migrations/default-user.sql +2 -1
  44. package/package.json +2 -2
  45. package/templates/.env.dist +11 -11
  46. package/templates/css/styles.css +1 -1
  47. package/templates/index.js.dist +32 -0
  48. 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, "&lt;")
33
+ .replace(/>/g, "&gt;")
34
+ .replace(/"/g, "&quot;")
35
+ .replace(/'/g, "&#039;");
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 Administration Panel!
@@ -0,0 +1,5 @@
1
+ <div class="copyright">
2
+ <a href="https://www.dwdeveloper.com/" target="_blank">
3
+ by D<span class="text-black text-lowercase">w</span><span class="text-capitalize">Developer</span>
4
+ </a>
5
+ </div>
@@ -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,3 @@
1
+ <button name="{{&fieldName}}" id="{{&fieldName}}"{{&fieldDisabled}}>
2
+ {{&fieldValue}}
3
+ </button>
@@ -0,0 +1 @@
1
+ <input type="checkbox" name="{{&fieldName}}" value="1"{{&fieldValue}}{{&fieldDisabled}}/>
@@ -0,0 +1,2 @@
1
+ <p>{{&fieldValue}}</p>
2
+ <input type="file" name="{{&fieldName}}" id="{{&fieldName}}"{{&required}}{{&fieldDisabled}}{{&multiple}}/>
@@ -0,0 +1 @@
1
+ <input type="radio" name="{{&fieldName}}" value="{{&fieldValue}}"{{checked}}{{&fieldDisabled}}/>
@@ -0,0 +1,5 @@
1
+ <select name="{{&fieldName}}" id="{{&fieldName}}"{{&required}}{{&fieldDisabled}}>
2
+ {{#fieldValue}}
3
+ <option value="{{&value}}"{{&selected}}>{{&label}}</option>
4
+ {{/fieldValue}}
5
+ </select>
@@ -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,4 @@
1
+ <a href="{{&fieldValue}}"{{&target}}>
2
+ <p>{{&fieldValue}}</p>
3
+ <img src="{{&fieldValue}}" alt="{{&fieldValue}}"/>
4
+ </a>
@@ -0,0 +1,7 @@
1
+ {{#fieldValue}}
2
+ <a href="{{&fieldValuePart}}"{{&target}}>
3
+ <p>{{&fieldOriginalValuePart}}</p>
4
+ <img src="{{&fieldValuePart}}" alt="{{&fieldValuePart}}"/>
5
+ </a>
6
+ <br/>
7
+ {{/fieldValue}}
@@ -0,0 +1 @@
1
+ <a href="{{&fieldValue}}"{{&target}}>{{&fieldOriginalValue}}</a>
@@ -0,0 +1,6 @@
1
+ {{#fieldValue}}
2
+ <a href="{{&fieldValuePart}}"{{&target}}>
3
+ {{&fieldOriginalValuePart}}
4
+ </a>
5
+ <br/>
6
+ {{/fieldValue}}
@@ -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">&times;</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
+ {{&copyRight}}
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,85 @@
1
+ <h2>Maps Wizard</h2>
2
+ <div class="sub-content maps-wizard">
3
+ <div class="sub-content-box">
4
+ <form class="sub-content-form maps-wizard-form confirmation-required"
5
+ name="maps-wizard-form"
6
+ id="maps-wizard-form"
7
+ action="{{&actionPath}}"
8
+ method="post"
9
+ enctype="multipart/form-data">
10
+ <input type="hidden" name="mainAction" id="mainAction" value="import"/>
11
+ <input type="hidden" name="generatedMapsHandler" id="generatedMapsHandler" value="{{&generatedMapsHandler}}"/>
12
+ <input type="hidden" name="importAssociationsForChangePoints" id="importAssociationsForChangePoints" value="{{&importAssociationsForChangePoints}}"/>
13
+ <input type="hidden" name="importAssociationsRecursively" id="importAssociationsRecursively" value="{{&importAssociationsRecursively}}"/>
14
+ <input type="hidden" name="automaticallyExtrudeMaps" id="automaticallyExtrudeMaps" value="{{&automaticallyExtrudeMaps}}"/>
15
+ <input type="hidden" name="verifyTilesetImage" id="verifyTilesetImage" value="{{&verifyTilesetImage}}"/>
16
+ <input type="hidden" name="handlerParams" id="handlerParams" value="{{handlerParams}}"/>
17
+ <div class="main-action-container maps-selection">
18
+ <h3>Select the maps to be imported</h3>
19
+ <p>Notes:<p>
20
+ <p>
21
+ - The Game Server requires a reboot in order to make the maps available on the game.<br/>
22
+ - If you refresh the page you will get a new set of random maps generated.<br/>
23
+ - Generated sub-maps will be automatically imported, if you don't want to import those you can download all the related maps (the main map and the sub-map) and edit change them to your needs.<br/>
24
+ </p>
25
+ <ul class="wizard-options-container">
26
+ {{#maps}}
27
+ <li class="wizard-map-option-container">
28
+ <div class="selector-box">
29
+ <input class="map-wizard-option" type="checkbox" name="selectedMaps[]" id="maps-wizard-map-option-{{&key}}" value="{{&key}}"/>
30
+ <input type="hidden" name="map-title-{{&key}}" id="map-title-{{&key}}" value="{{&key}}"/>
31
+ <label class="main-option" for="maps-wizard-map-option-{{&key}}">
32
+ {{&key}}
33
+ </label>
34
+ <p>Downloads:</p>
35
+ <p>
36
+ - <a href="{{&mapJson}}" target="_blank">{{&mapJson}}</a><br/>
37
+ - <a href="{{&mapImage}}" target="_blank">{{&mapImage}}</a>
38
+ </p>
39
+ </div>
40
+ <canvas
41
+ class="mapCanvas"
42
+ data-toggle="modal"
43
+ width="{{&mapWidth}}"
44
+ height="{{&mapHeight}}"
45
+ data-tile-width="{{&tileWidth}}"
46
+ data-tile-height="{{&tileHeight}}"
47
+ data-image-key="{{&mapImage}}"
48
+ data-map-json="{{&mapJson}}">
49
+ </canvas>
50
+ {{#hasSubMaps}}
51
+ <div class="sub-maps">
52
+ <p class="sub-map-option-description clickable" data-expand-collapse=".sub-maps-container-{{&key}}">
53
+ <img class="icon-sm" src="/assets/admin/filters.png" alt="Associated maps"/>
54
+ <span>Associated maps generated</span>
55
+ </p>
56
+ <div class="sub-maps-container sub-maps-container-{{&key}} hidden">
57
+ {{#subMaps}}
58
+ <p>{{&key}}</p>
59
+ <p>Downloads:</p>
60
+ <p>
61
+ - <a href="{{&mapJson}}" target="_blank">{{&mapJson}}</a><br/>
62
+ - <a href="{{&mapImage}}" target="_blank">{{&mapImage}}</a>
63
+ </p>
64
+ <canvas
65
+ class="mapCanvas"
66
+ data-toggle="modal"
67
+ width="{{&mapWidth}}"
68
+ height="{{&mapHeight}}"
69
+ data-tile-width="{{&tileWidth}}"
70
+ data-tile-height="{{&tileHeight}}"
71
+ data-image-key="{{&mapImage}}"
72
+ data-map-json="{{&mapJson}}">
73
+ </canvas>
74
+ {{/subMaps}}
75
+ </div>
76
+ </div>
77
+ {{/hasSubMaps}}
78
+ </li>
79
+ {{/maps}}
80
+ </ul>
81
+ </div>
82
+ <input type="submit" class="button button-primary button-maps-wizard" value="Import Selected Maps"/>
83
+ </form>
84
+ </div>
85
+ </div>