kempo 4.2.9 → 4.2.11
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/app-public/.config.json +2 -5
- package/bin/cli.js +4 -10
- package/dist/admin/accounts/groups/[name]/index.html +518 -0
- package/dist/admin/accounts/groups/index.html +378 -0
- package/dist/admin/accounts/permissions/index.html +397 -0
- package/dist/admin/accounts/users/[id]/index.html +541 -0
- package/dist/admin/accounts/users/index.html +396 -0
- package/dist/admin/content/fragments/edit/index.html +132 -0
- package/dist/admin/content/fragments/index.html +375 -0
- package/dist/admin/content/globals/edit/index.html +132 -0
- package/dist/admin/content/globals/index.html +358 -0
- package/dist/admin/content/pages/edit/index.html +132 -0
- package/dist/admin/content/pages/index.html +509 -0
- package/dist/admin/content/templates/edit/index.html +132 -0
- package/dist/admin/content/templates/index.html +429 -0
- package/dist/admin/extensions/index.html +222 -0
- package/dist/admin/index.html +130 -0
- package/dist/admin/index.page.html +1 -0
- package/dist/admin/settings/[owner]/[name]/index.html +449 -0
- package/dist/admin/settings/index.html +410 -0
- package/middleware/{extension-scope-router.js → kempo.js} +40 -25
- package/package.json +3 -3
- package/middleware/kempo-auth.js +0 -26
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
owner: system
|
|
3
|
+
locked: true
|
|
4
|
+
name:
|
|
5
|
+
author:
|
|
6
|
+
created:
|
|
7
|
+
updated:
|
|
8
|
+
-->
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
|
+
<link rel="icon" type="image/png" sizes="48x48" href="/media/icon48.png">
|
|
15
|
+
<link rel="manifest" href="/manifest.json">
|
|
16
|
+
<title>Global Content - Admin</title>
|
|
17
|
+
<link rel="stylesheet" href="/kempo-css/kempo.min.css">
|
|
18
|
+
<script src="/admin/init.js"></script>
|
|
19
|
+
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<!--
|
|
23
|
+
locked: true
|
|
24
|
+
-->
|
|
25
|
+
|
|
26
|
+
<k-aside
|
|
27
|
+
main="push"
|
|
28
|
+
state="expanded"
|
|
29
|
+
style="--aside_padding: 0;"
|
|
30
|
+
persistent-id="admin-nav"
|
|
31
|
+
>
|
|
32
|
+
<k-aside-toggle>
|
|
33
|
+
<h3 class="m0">Kempo Admin</h3>
|
|
34
|
+
</k-aside-toggle>
|
|
35
|
+
<k-aside-item
|
|
36
|
+
icon="preview"
|
|
37
|
+
href="/"
|
|
38
|
+
no-expand
|
|
39
|
+
>Site</k-aside-item>
|
|
40
|
+
<k-aside-item
|
|
41
|
+
icon="cards"
|
|
42
|
+
href="/admin"
|
|
43
|
+
no-expand
|
|
44
|
+
>Dashboard</k-aside-item>
|
|
45
|
+
<k-aside-menu
|
|
46
|
+
icon="manage-accounts"
|
|
47
|
+
label="Accounts"
|
|
48
|
+
>
|
|
49
|
+
<k-aside-item
|
|
50
|
+
icon="person"
|
|
51
|
+
href="/admin/accounts/users"
|
|
52
|
+
no-expand
|
|
53
|
+
>Users</k-aside-item>
|
|
54
|
+
<k-aside-item
|
|
55
|
+
icon="group"
|
|
56
|
+
href="/admin/accounts/groups"
|
|
57
|
+
no-expand
|
|
58
|
+
>Groups</k-aside-item>
|
|
59
|
+
<k-aside-item
|
|
60
|
+
icon="lock"
|
|
61
|
+
href="/admin/accounts/permissions"
|
|
62
|
+
no-expand
|
|
63
|
+
>Permissions</k-aside-item>
|
|
64
|
+
</k-aside-menu>
|
|
65
|
+
<k-aside-menu
|
|
66
|
+
icon="cards_stack"
|
|
67
|
+
label="Content"
|
|
68
|
+
>
|
|
69
|
+
<k-aside-item
|
|
70
|
+
icon="browse"
|
|
71
|
+
href="/admin/content/templates"
|
|
72
|
+
>Templates</k-aside-item>
|
|
73
|
+
<k-aside-item
|
|
74
|
+
icon="docs"
|
|
75
|
+
href="/admin/content/pages"
|
|
76
|
+
>Pages</k-aside-item>
|
|
77
|
+
<k-aside-item
|
|
78
|
+
icon="content"
|
|
79
|
+
href="/admin/content/fragments"
|
|
80
|
+
>Fragments</k-aside-item>
|
|
81
|
+
<k-aside-item
|
|
82
|
+
icon="globe"
|
|
83
|
+
href="/admin/content/globals"
|
|
84
|
+
>Global Content</k-aside-item>
|
|
85
|
+
</k-aside-menu>
|
|
86
|
+
<k-aside-item
|
|
87
|
+
icon="settings"
|
|
88
|
+
href="/admin/settings"
|
|
89
|
+
no-expand
|
|
90
|
+
>Settings</k-aside-item>
|
|
91
|
+
<k-aside-item
|
|
92
|
+
icon="extension"
|
|
93
|
+
href="/admin/extensions"
|
|
94
|
+
no-expand
|
|
95
|
+
>Extensions</k-aside-item>
|
|
96
|
+
<hr>
|
|
97
|
+
<k-aside-spacer></k-aside-spacer>
|
|
98
|
+
<k-theme-switcher
|
|
99
|
+
labels
|
|
100
|
+
style="--padding:var(--spacer_h);margin:var(--spacer_h) auto"
|
|
101
|
+
></k-theme-switcher>
|
|
102
|
+
</k-aside>
|
|
103
|
+
<script type="module">
|
|
104
|
+
import '/kempo-ui/components/ThemeSwitcher.js';
|
|
105
|
+
const items = document.querySelectorAll('k-aside-item');
|
|
106
|
+
const currentPath = window.location.pathname;
|
|
107
|
+
|
|
108
|
+
items.forEach(item => {
|
|
109
|
+
const href = item.getAttribute('href');
|
|
110
|
+
if (!href) return;
|
|
111
|
+
const isExact = href === '/admin' || href === '/';
|
|
112
|
+
const match = isExact
|
|
113
|
+
? (currentPath === href || currentPath === href + '/')
|
|
114
|
+
: (currentPath === href || currentPath.startsWith(href + '/'));
|
|
115
|
+
if (match) {
|
|
116
|
+
item.setAttribute('active', '');
|
|
117
|
+
const menu = item.closest('k-aside-menu');
|
|
118
|
+
if (menu) menu.setAttribute('open', '');
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<k-main>
|
|
124
|
+
|
|
125
|
+
<script type="module" src="/kempo-ui/components/Table.js"></script>
|
|
126
|
+
<script type="module" src="/kempo-ui/components/tableControls/FirstPage.js"></script>
|
|
127
|
+
<script type="module" src="/kempo-ui/components/tableControls/PrevPage.js"></script>
|
|
128
|
+
<script type="module" src="/kempo-ui/components/tableControls/NextPage.js"></script>
|
|
129
|
+
<script type="module" src="/kempo-ui/components/tableControls/LastPage.js"></script>
|
|
130
|
+
<script type="module" src="/kempo-ui/components/tableControls/PageSelect.js"></script>
|
|
131
|
+
<script type="module" src="/kempo-ui/components/tableControls/PageSize.js"></script>
|
|
132
|
+
<script type="module" src="/kempo-ui/components/tableControls/DeleteRecord.js"></script>
|
|
133
|
+
<script type="module" src="/kempo-ui/components/tableControls/DeleteSelected.js"></script>
|
|
134
|
+
<script type="module" src="/kempo-ui/components/Icon.js"></script>
|
|
135
|
+
<script type="module" src="/admin/components/TableRowLink.js"></script>
|
|
136
|
+
<script type="module" src="/kempo/components/Permission.js"></script>
|
|
137
|
+
|
|
138
|
+
<div class="d-f mb" style="align-items: center; gap: var(--spacer);">
|
|
139
|
+
<h1 class="flex mb0">Global Content</h1>
|
|
140
|
+
<k-permission has="system:globals:create">
|
|
141
|
+
<button id="createGlobalBtn" class="primary">Create Global Content</button>
|
|
142
|
+
</k-permission>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<h2>Custom</h2>
|
|
146
|
+
<k-table id="customTable" enable-pages page-size="25" page-size-options="[10,25,50,100]" placeholder="No custom global content has been created yet.">
|
|
147
|
+
<admin-table-row-link slot="before" url-pattern="/admin/content/globals/edit?id={id}" field-name="id"></admin-table-row-link>
|
|
148
|
+
<div slot="top" class="d-f full" style="align-items: center;">
|
|
149
|
+
<div>
|
|
150
|
+
<k-permission has="system:globals:delete">
|
|
151
|
+
<k-tc-delete-selected></k-tc-delete-selected>
|
|
152
|
+
</k-permission>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="d-f full" style="align-items: center;">
|
|
156
|
+
<div>
|
|
157
|
+
<k-tc-first-page></k-tc-first-page>
|
|
158
|
+
<k-tc-prev-page></k-tc-prev-page>
|
|
159
|
+
<k-tc-next-page></k-tc-next-page>
|
|
160
|
+
<k-tc-last-page></k-tc-last-page>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="flex"></div>
|
|
163
|
+
<div><k-tc-page-select></k-tc-page-select></div>
|
|
164
|
+
<div class="flex"></div>
|
|
165
|
+
<div><k-tc-page-size></k-tc-page-size></div>
|
|
166
|
+
</div>
|
|
167
|
+
</k-table>
|
|
168
|
+
|
|
169
|
+
<h2>Extensions</h2>
|
|
170
|
+
<k-table id="extensionTable" enable-pages page-size="25" page-size-options="[10,25,50,100]" placeholder="No extension global content installed.">
|
|
171
|
+
<admin-table-row-link slot="before" url-pattern="/admin/content/globals/edit?id={id}" field-name="id"></admin-table-row-link>
|
|
172
|
+
<div class="d-f full" style="align-items: center;">
|
|
173
|
+
<div>
|
|
174
|
+
<k-tc-first-page></k-tc-first-page>
|
|
175
|
+
<k-tc-prev-page></k-tc-prev-page>
|
|
176
|
+
<k-tc-next-page></k-tc-next-page>
|
|
177
|
+
<k-tc-last-page></k-tc-last-page>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="flex"></div>
|
|
180
|
+
<div><k-tc-page-select></k-tc-page-select></div>
|
|
181
|
+
<div class="flex"></div>
|
|
182
|
+
<div><k-tc-page-size></k-tc-page-size></div>
|
|
183
|
+
</div>
|
|
184
|
+
</k-table>
|
|
185
|
+
|
|
186
|
+
<h2>System</h2>
|
|
187
|
+
<k-table id="systemTable" enable-pages page-size="25" page-size-options="[10,25,50,100]" placeholder="No system global content found.">
|
|
188
|
+
<admin-table-row-link slot="before" url-pattern="/admin/content/globals/edit?id={id}" field-name="id"></admin-table-row-link>
|
|
189
|
+
<div class="d-f full" style="align-items: center;">
|
|
190
|
+
<div>
|
|
191
|
+
<k-tc-first-page></k-tc-first-page>
|
|
192
|
+
<k-tc-prev-page></k-tc-prev-page>
|
|
193
|
+
<k-tc-next-page></k-tc-next-page>
|
|
194
|
+
<k-tc-last-page></k-tc-last-page>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="flex"></div>
|
|
197
|
+
<div><k-tc-page-select></k-tc-page-select></div>
|
|
198
|
+
<div class="flex"></div>
|
|
199
|
+
<div><k-tc-page-size></k-tc-page-size></div>
|
|
200
|
+
</div>
|
|
201
|
+
</k-table>
|
|
202
|
+
|
|
203
|
+
<script type="module">
|
|
204
|
+
import { listGlobalContent, listTemplates, createGlobalContent, deleteGlobalContent, getAllCurrentUserPermissions } from '/kempo/sdk.js';
|
|
205
|
+
import { html } from '/kempo-ui/lit-all.min.js';
|
|
206
|
+
import Dialog from '/kempo-ui/components/Dialog.js';
|
|
207
|
+
import Toast from '/kempo-ui/components/Toast.js';
|
|
208
|
+
|
|
209
|
+
await window.customElements.whenDefined('k-table');
|
|
210
|
+
|
|
211
|
+
const [, permData] = await getAllCurrentUserPermissions();
|
|
212
|
+
const myPerms = new Set(permData?.permissions ?? []);
|
|
213
|
+
|
|
214
|
+
const fields = [
|
|
215
|
+
{ name: 'lockedBadge', label: '', size: 40, formatter: v => v },
|
|
216
|
+
{ name: 'name', label: 'Name', size: 200 },
|
|
217
|
+
{ name: 'location', label: 'Location', size: 150 },
|
|
218
|
+
{ name: 'priority', label: 'Priority', size: 80 },
|
|
219
|
+
{ name: 'author', label: 'Author', size: 150 },
|
|
220
|
+
{ name: 'updatedAt', label: 'Updated', size: 190, formatter: v => v ? new Date(v).toLocaleString() : '' },
|
|
221
|
+
{ name: 'createdAt', label: 'Created', size: 190, formatter: v => v ? new Date(v).toLocaleString() : '' }
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const extensionFields = [
|
|
225
|
+
{ name: 'owner', label: 'Extension', size: 120 },
|
|
226
|
+
...fields
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
const $customTable = document.getElementById('customTable');
|
|
230
|
+
const $extensionTable = document.getElementById('extensionTable');
|
|
231
|
+
const $systemTable = document.getElementById('systemTable');
|
|
232
|
+
|
|
233
|
+
const addBadge = e => ({ ...e, lockedBadge: e.locked ? '<k-icon name="lock"></k-icon>' : '' });
|
|
234
|
+
|
|
235
|
+
const loadEntries = async () => {
|
|
236
|
+
const [error, data] = await listGlobalContent();
|
|
237
|
+
if(error){
|
|
238
|
+
Toast.error(error.msg || 'Failed to load global content');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const custom = data.entries.filter(e => e.owner === 'custom').map(addBadge);
|
|
242
|
+
const extension = data.entries.filter(e => e.owner !== 'custom' && e.owner !== 'system').map(addBadge);
|
|
243
|
+
const system = data.entries.filter(e => e.owner === 'system').map(addBadge);
|
|
244
|
+
$customTable.setData({ records: custom, fields });
|
|
245
|
+
$extensionTable.setData({ records: extension, fields: extensionFields });
|
|
246
|
+
$systemTable.setData({ records: system, fields });
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
if(myPerms.has('system:globals:delete')){
|
|
250
|
+
$customTable.setAttribute('request-delete', '');
|
|
251
|
+
$customTable.setAttribute('enable-selection', '');
|
|
252
|
+
const del = document.createElement('k-tc-delete-record');
|
|
253
|
+
del.setAttribute('slot', 'after');
|
|
254
|
+
$customTable.appendChild(del);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
await loadEntries();
|
|
258
|
+
|
|
259
|
+
$customTable.addEventListener('requestDelete', ({ detail: { records, approve, reject } }) => {
|
|
260
|
+
const locked = records.filter(r => r.locked);
|
|
261
|
+
if(locked.length){
|
|
262
|
+
Toast.error(locked.length === 1 ? `"${locked[0].name}" is locked and cannot be deleted.` : `${locked.length} selected items are locked and cannot be deleted.`);
|
|
263
|
+
reject();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const count = records.length;
|
|
267
|
+
const msg = count === 1
|
|
268
|
+
? `Delete "${records[0].name}"? This action cannot be undone.`
|
|
269
|
+
: `Delete ${count} global content entries? This action cannot be undone.`;
|
|
270
|
+
Dialog.confirm(msg, async (confirmed) => {
|
|
271
|
+
if(!confirmed){ reject(); return; }
|
|
272
|
+
const ids = records.map(r => r.id);
|
|
273
|
+
const [error] = await deleteGlobalContent(ids);
|
|
274
|
+
if(error){
|
|
275
|
+
reject();
|
|
276
|
+
Toast.error(error.msg || 'Failed to delete');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
approve();
|
|
280
|
+
Toast.success(count === 1 ? 'Global content deleted' : `${count} entries deleted`);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
Create Global Content Dialog
|
|
286
|
+
*/
|
|
287
|
+
|
|
288
|
+
document.getElementById('createGlobalBtn')?.addEventListener('click', async () => {
|
|
289
|
+
const [tplError, tplData] = await listTemplates();
|
|
290
|
+
const locations = new Set();
|
|
291
|
+
if(!tplError && tplData?.templates){
|
|
292
|
+
for(const t of tplData.templates){
|
|
293
|
+
for(const loc of (t.locations || [])){
|
|
294
|
+
locations.add(typeof loc === 'string' ? loc : loc.name);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const locationOptions = [...locations].sort();
|
|
299
|
+
|
|
300
|
+
const $dialog = Dialog.create(html`
|
|
301
|
+
<div class="p">
|
|
302
|
+
<div class="mb">
|
|
303
|
+
<label class="d-b mb-sm">Name</label>
|
|
304
|
+
<input type="text" id="dlg-name" class="full" placeholder="Analytics Script" required>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="mb">
|
|
307
|
+
<label class="d-b mb-sm">Location</label>
|
|
308
|
+
<input type="text" id="dlg-location" class="full" list="dlg-location-list" placeholder="scripts" required>
|
|
309
|
+
<datalist id="dlg-location-list">
|
|
310
|
+
${locationOptions.map(l => html`<option value="${l}">`)}
|
|
311
|
+
</datalist>
|
|
312
|
+
<p class="mt-sm muted"><small>The template location to inject this content into.</small></p>
|
|
313
|
+
</div>
|
|
314
|
+
<div>
|
|
315
|
+
<label class="d-b mb-sm">Priority</label>
|
|
316
|
+
<input type="number" id="dlg-priority" class="full" value="0">
|
|
317
|
+
<p class="mt-sm muted"><small>Higher priority content is rendered first. Default is 0.</small></p>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
`, {
|
|
321
|
+
title: 'Create Global Content',
|
|
322
|
+
confirmText: 'Create',
|
|
323
|
+
confirmAction: async () => {
|
|
324
|
+
const name = $dialog.querySelector('#dlg-name').value.trim();
|
|
325
|
+
const location = $dialog.querySelector('#dlg-location').value.trim();
|
|
326
|
+
const priority = $dialog.querySelector('#dlg-priority').value.trim();
|
|
327
|
+
|
|
328
|
+
if(!name){
|
|
329
|
+
Toast.error('Name is required');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if(!location){
|
|
334
|
+
Toast.error('Location is required');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const [error, data] = await createGlobalContent({ name, location, priority });
|
|
339
|
+
|
|
340
|
+
if(error){
|
|
341
|
+
Toast.error(error.msg || 'Failed to create global content');
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
Toast.success('Global content created — redirecting to editor...');
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
window.location.href = `/admin/content/globals/edit?id=${encodeURIComponent(data.id)}`;
|
|
348
|
+
}, 1500);
|
|
349
|
+
},
|
|
350
|
+
cancelText: 'Cancel'
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
</script>
|
|
354
|
+
|
|
355
|
+
</k-main>
|
|
356
|
+
|
|
357
|
+
</body>
|
|
358
|
+
</html>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
owner: system
|
|
3
|
+
locked: true
|
|
4
|
+
name:
|
|
5
|
+
author:
|
|
6
|
+
created:
|
|
7
|
+
updated:
|
|
8
|
+
-->
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
|
+
<link rel="icon" type="image/png" sizes="48x48" href="/media/icon48.png">
|
|
15
|
+
<link rel="manifest" href="/manifest.json">
|
|
16
|
+
<title>Edit Page - Admin</title>
|
|
17
|
+
<link rel="stylesheet" href="/kempo-css/kempo.min.css">
|
|
18
|
+
<script src="/admin/init.js"></script>
|
|
19
|
+
|
|
20
|
+
<script type="module" src="/admin/components/PageEditor.js"></script>
|
|
21
|
+
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<!--
|
|
25
|
+
locked: true
|
|
26
|
+
-->
|
|
27
|
+
|
|
28
|
+
<k-aside
|
|
29
|
+
main="push"
|
|
30
|
+
state="expanded"
|
|
31
|
+
style="--aside_padding: 0;"
|
|
32
|
+
persistent-id="admin-nav"
|
|
33
|
+
>
|
|
34
|
+
<k-aside-toggle>
|
|
35
|
+
<h3 class="m0">Kempo Admin</h3>
|
|
36
|
+
</k-aside-toggle>
|
|
37
|
+
<k-aside-item
|
|
38
|
+
icon="preview"
|
|
39
|
+
href="/"
|
|
40
|
+
no-expand
|
|
41
|
+
>Site</k-aside-item>
|
|
42
|
+
<k-aside-item
|
|
43
|
+
icon="cards"
|
|
44
|
+
href="/admin"
|
|
45
|
+
no-expand
|
|
46
|
+
>Dashboard</k-aside-item>
|
|
47
|
+
<k-aside-menu
|
|
48
|
+
icon="manage-accounts"
|
|
49
|
+
label="Accounts"
|
|
50
|
+
>
|
|
51
|
+
<k-aside-item
|
|
52
|
+
icon="person"
|
|
53
|
+
href="/admin/accounts/users"
|
|
54
|
+
no-expand
|
|
55
|
+
>Users</k-aside-item>
|
|
56
|
+
<k-aside-item
|
|
57
|
+
icon="group"
|
|
58
|
+
href="/admin/accounts/groups"
|
|
59
|
+
no-expand
|
|
60
|
+
>Groups</k-aside-item>
|
|
61
|
+
<k-aside-item
|
|
62
|
+
icon="lock"
|
|
63
|
+
href="/admin/accounts/permissions"
|
|
64
|
+
no-expand
|
|
65
|
+
>Permissions</k-aside-item>
|
|
66
|
+
</k-aside-menu>
|
|
67
|
+
<k-aside-menu
|
|
68
|
+
icon="cards_stack"
|
|
69
|
+
label="Content"
|
|
70
|
+
>
|
|
71
|
+
<k-aside-item
|
|
72
|
+
icon="browse"
|
|
73
|
+
href="/admin/content/templates"
|
|
74
|
+
>Templates</k-aside-item>
|
|
75
|
+
<k-aside-item
|
|
76
|
+
icon="docs"
|
|
77
|
+
href="/admin/content/pages"
|
|
78
|
+
>Pages</k-aside-item>
|
|
79
|
+
<k-aside-item
|
|
80
|
+
icon="content"
|
|
81
|
+
href="/admin/content/fragments"
|
|
82
|
+
>Fragments</k-aside-item>
|
|
83
|
+
<k-aside-item
|
|
84
|
+
icon="globe"
|
|
85
|
+
href="/admin/content/globals"
|
|
86
|
+
>Global Content</k-aside-item>
|
|
87
|
+
</k-aside-menu>
|
|
88
|
+
<k-aside-item
|
|
89
|
+
icon="settings"
|
|
90
|
+
href="/admin/settings"
|
|
91
|
+
no-expand
|
|
92
|
+
>Settings</k-aside-item>
|
|
93
|
+
<k-aside-item
|
|
94
|
+
icon="extension"
|
|
95
|
+
href="/admin/extensions"
|
|
96
|
+
no-expand
|
|
97
|
+
>Extensions</k-aside-item>
|
|
98
|
+
<hr>
|
|
99
|
+
<k-aside-spacer></k-aside-spacer>
|
|
100
|
+
<k-theme-switcher
|
|
101
|
+
labels
|
|
102
|
+
style="--padding:var(--spacer_h);margin:var(--spacer_h) auto"
|
|
103
|
+
></k-theme-switcher>
|
|
104
|
+
</k-aside>
|
|
105
|
+
<script type="module">
|
|
106
|
+
import '/kempo-ui/components/ThemeSwitcher.js';
|
|
107
|
+
const items = document.querySelectorAll('k-aside-item');
|
|
108
|
+
const currentPath = window.location.pathname;
|
|
109
|
+
|
|
110
|
+
items.forEach(item => {
|
|
111
|
+
const href = item.getAttribute('href');
|
|
112
|
+
if (!href) return;
|
|
113
|
+
const isExact = href === '/admin' || href === '/';
|
|
114
|
+
const match = isExact
|
|
115
|
+
? (currentPath === href || currentPath === href + '/')
|
|
116
|
+
: (currentPath === href || currentPath.startsWith(href + '/'));
|
|
117
|
+
if (match) {
|
|
118
|
+
item.setAttribute('active', '');
|
|
119
|
+
const menu = item.closest('k-aside-menu');
|
|
120
|
+
if (menu) menu.setAttribute('open', '');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<k-main>
|
|
126
|
+
|
|
127
|
+
<admin-page-editor></admin-page-editor>
|
|
128
|
+
|
|
129
|
+
</k-main>
|
|
130
|
+
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|