domma-cms 1.6.60 → 1.7.1
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/CHANGELOG.md +4349 -0
- package/README.md +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/mail-compose.js +1 -0
- package/admin/js/lib/page-editor-reference.js +5 -5
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/templates/call-new.html +1 -1
- package/admin/js/templates/docs/usage-shortcodes.html +39 -0
- package/admin/js/views/call-new.js +1 -1
- package/admin/js/views/contacts.js +15 -15
- package/admin/js/views/form-submissions.js +2 -2
- package/docs/DommaFeatures.md +3005 -0
- package/docs/MultiTenant.md +176 -0
- package/docs/api-reference.md +1667 -0
- package/docs/cache.md +96 -0
- package/docs/configuration.md +1811 -0
- package/docs/editions-and-licences.md +190 -0
- package/docs/getting-started.md +201 -0
- package/docs/guides/form-triggers.md +260 -0
- package/docs/guides/mongodb-storage-adapter.md +282 -0
- package/docs/guides/multi-page-forms.md +121 -0
- package/docs/images/scribe-composer.png +0 -0
- package/docs/markdown-shortcodes.md +1738 -0
- package/docs/pentest-report-2026-04-03.md +181 -0
- package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
- package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
- package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
- package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
- package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
- package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
- package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
- package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
- package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
- package/docs/plans/2026-03-05-user-types-design.md +139 -0
- package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
- package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
- package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
- package/docs/plans/2026-06-10-core-project-design.md +240 -0
- package/docs/plugin-development.md +827 -0
- package/docs/recruitment-recipe.md +501 -0
- package/docs/scaffolding.md +312 -0
- package/docs/screenshots/calculator-floating.png +0 -0
- package/docs/screenshots/calculator-standard.png +0 -0
- package/docs/screenshots/email-settings.png +0 -0
- package/docs/screenshots/messages-count.png +0 -0
- package/docs/structure/user-types.md +107 -0
- package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
- package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
- package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
- package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
- package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
- package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
- package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
- package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
- package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
- package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
- package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
- package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
- package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
- package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
- package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
- package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
- package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
- package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
- package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
- package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
- package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
- package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
- package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
- package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
- package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
- package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
- package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
- package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
- package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
- package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
- package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
- package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
- package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
- package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
- package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
- package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
- package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
- package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
- package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
- package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
- package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
- package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
- package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
- package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
- package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
- package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
- package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
- package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
- package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
- package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
- package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
- package/docs/theming.md +320 -0
- package/package.json +2 -2
- package/plugins/_lib/admin/mail/compose-window.js +3 -1
- package/plugins/_lib/admin/mail/identity.js +18 -3
- package/plugins/_lib/admin/mail/reader-view.js +32 -3
- package/plugins/blog/CLAUDE.md +4 -0
- package/plugins/blog/admin/lib/email.js +191 -0
- package/plugins/blog/admin/views/comments.js +3 -1
- package/plugins/blog/plugin.json +2 -2
- package/plugins/blog/plugin.public.js +1 -1
- package/plugins/blog/tests/email.test.js +69 -0
- package/plugins/booking/CLAUDE.md +4 -0
- package/plugins/booking/admin/lib/email.js +191 -0
- package/plugins/booking/admin/views/panels.js +7 -3
- package/plugins/booking/plugin.json +1 -1
- package/plugins/booking/tests/email.test.js +69 -0
- package/plugins/free-tier.lock.json +27 -19
- package/plugins/shopping-cart/CLAUDE.md +4 -0
- package/plugins/shopping-cart/admin/lib/email.js +191 -0
- package/plugins/shopping-cart/admin/views/orders.js +11 -3
- package/plugins/shopping-cart/plugin.json +2 -2
- package/plugins/shopping-cart/tests/email.test.js +69 -0
- package/plugins/support/CLAUDE.md +4 -0
- package/plugins/support/admin/lib/email.js +191 -0
- package/plugins/support/admin/views/desk.js +4 -1
- package/plugins/support/admin/views/inbox.js +6 -2
- package/plugins/support/plugin.json +2 -2
- package/plugins/support/tests/email.test.js +69 -0
- package/public/js/celebrations/core/canvas.js +1 -1
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
- package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
- package/public/js/celebrations/message-attrs.mjs +1 -0
- package/public/js/celebrations/themes/anniversary.js +1 -0
- package/public/js/celebrations/themes/autumn.js +1 -0
- package/public/js/celebrations/themes/birthday.js +1 -0
- package/public/js/celebrations/themes/butterfly-flight.js +1 -0
- package/public/js/celebrations/themes/calendar.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year.js +1 -0
- package/public/js/celebrations/themes/christmas.js +1 -1
- package/public/js/celebrations/themes/easter.js +1 -0
- package/public/js/celebrations/themes/garden-drawing.js +1 -0
- package/public/js/celebrations/themes/garden.js +1 -0
- package/public/js/celebrations/themes/graduation.js +1 -0
- package/public/js/celebrations/themes/greeting.js +1 -0
- package/public/js/celebrations/themes/make-it-rain.js +1 -0
- package/public/js/celebrations/themes/money-rain.js +1 -0
- package/public/js/celebrations/themes/new-year.js +1 -0
- package/public/js/celebrations/themes/occasion-drawing.js +1 -0
- package/public/js/celebrations/themes/occasions.js +1 -0
- package/public/js/celebrations/themes/rain-control.js +1 -0
- package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
- package/public/js/celebrations/themes/sparkler-font.js +1 -0
- package/public/js/celebrations/themes/sparkler-writing.js +1 -0
- package/public/js/celebrations/themes/summer-garden.js +1 -0
- package/public/js/celebrations/themes/winter.js +1 -0
- package/public/js/celebrations/trait-data.mjs +1 -1
- package/public/js/effects.js +1 -1
- package/scripts/build.js +4 -0
- package/server/services/markdown.js +5 -2
- package/server/templates/page.html +1 -1
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Admin Sidebar via Menus Design
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-05-25
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
**Scope:** Replace the hand-rolled `admin/js/config/sidebar-config.js` flat-array sidebar with a menu rendered from the Menus system at a new built-in slot `admin-sidebar`. Plugins extend via the existing menu-locations registry. The Menus admin can edit the sidebar visually. Native `<details>`/`<summary>` provides expand/collapse for nested folder behaviour.
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
The admin sidebar is currently generated by `admin/js/config/sidebar-config.js` - a hand-rolled flat array of `heading`, `divider`, and item objects passed to `Domma.elements.sidebar(...)`. This shape:
|
|
10
|
+
|
|
11
|
+
- Forces every new feature to edit the code to add an entry, instead of editing data.
|
|
12
|
+
- Has no native nesting - "headings" are visual groupings, not collapsible folders.
|
|
13
|
+
- Permission-gates each item in code (`if (canAny('pages'))`), with the rules buried in the file.
|
|
14
|
+
- Cannot be edited by an admin in the UI.
|
|
15
|
+
- Has bespoke handling for plugin-injected items via `pluginSidebarItems` in `app.js`.
|
|
16
|
+
- Special-cased the Projects section in the recent redesign - per-project nested entries are generated procedurally rather than living as menu data.
|
|
17
|
+
|
|
18
|
+
The Menus system shipped earlier supports unbounded depth, plugin-registered slots, per-item visibility/hidden flags, and admin-editable items. The admin sidebar is the natural next consumer.
|
|
19
|
+
|
|
20
|
+
## Goals
|
|
21
|
+
|
|
22
|
+
- Render the admin sidebar from a menu stored at `config/menus/admin-sidebar.json` (mapped to a new core slot `admin-sidebar`).
|
|
23
|
+
- Each menu item gains an optional `permission` field - a resource key from `permissionRegistry`. The renderer hides items the user can't act on.
|
|
24
|
+
- Plugins register sidebar items via a new `hooks.registerSidebarItem({parent, item})` hook. Registered items are merged into the rendered tree without modifying the menu JSON.
|
|
25
|
+
- Auto-migrate the current `sidebar-config.js` output to `admin-sidebar.json` on first boot. Idempotent.
|
|
26
|
+
- Admin-editable: the existing Menus admin lists `admin-sidebar` like any other menu; the menu editor's item row gains a `permission` field.
|
|
27
|
+
- Nested folder behaviour via native `<details>`/`<summary>` - works in every browser, no upstream PR required.
|
|
28
|
+
|
|
29
|
+
## Non-Goals (YAGNI)
|
|
30
|
+
|
|
31
|
+
- Drag-and-drop reordering directly in the sidebar (use the menu editor).
|
|
32
|
+
- Per-user sidebar customisation (everyone with the same permissions sees the same sidebar).
|
|
33
|
+
- Sidebar themes / colour overrides.
|
|
34
|
+
- Removing the legacy `Domma.elements.sidebar` rendering path (kept for non-CMS Domma sites that still use it).
|
|
35
|
+
|
|
36
|
+
## Data Model
|
|
37
|
+
|
|
38
|
+
### Menu shape - adds `permission`
|
|
39
|
+
|
|
40
|
+
Existing item shape (from the Menus design):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"text": "Pages",
|
|
45
|
+
"url": "#/pages",
|
|
46
|
+
"icon": "file-text",
|
|
47
|
+
"hidden": false,
|
|
48
|
+
"visibility": "public",
|
|
49
|
+
"items": []
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Adds one field:
|
|
54
|
+
|
|
55
|
+
| Field | Type | Description |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `permission` | string | Resource key from `permissionRegistry` (e.g. `pages`, `menus`, `projects`). The renderer hides the item when the user doesn't have any action under that resource. Missing = always visible (subject to existing `visibility`). |
|
|
58
|
+
|
|
59
|
+
`visibility` (per-role/per-project) and `permission` (resource access) are different axes. Both apply at render time. Most admin items use `permission`; public-site menu items use `visibility`. Items can have both.
|
|
60
|
+
|
|
61
|
+
### Built-in slot - `admin-sidebar`
|
|
62
|
+
|
|
63
|
+
Registered by core in `server/services/menus.js` alongside `navbar`, `footer-primary`, `footer-legal`:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
registerLocation('admin-sidebar', {
|
|
67
|
+
label: 'Admin sidebar',
|
|
68
|
+
description: 'The left-side navigation in the admin panel',
|
|
69
|
+
source: 'core',
|
|
70
|
+
maxDepth: null // unbounded
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After migration, `config/menu-locations.json` includes:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"navbar": "main",
|
|
79
|
+
"footer-primary": "footer",
|
|
80
|
+
"admin-sidebar": "admin-sidebar"
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Migrated menu shape
|
|
85
|
+
|
|
86
|
+
`config/menus/admin-sidebar.json` after auto-migration mirrors the current sidebar structure, with sections (which are headings today) becoming top-level items that nest their links:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"slug": "admin-sidebar",
|
|
91
|
+
"name": "Admin sidebar",
|
|
92
|
+
"items": [
|
|
93
|
+
{
|
|
94
|
+
"text": "Overview",
|
|
95
|
+
"icon": "home",
|
|
96
|
+
"items": [
|
|
97
|
+
{"text": "Dashboard", "url": "#/", "icon": "home"}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"text": "Projects",
|
|
102
|
+
"icon": "folder",
|
|
103
|
+
"permission": "projects",
|
|
104
|
+
"items": [
|
|
105
|
+
{"text": "Manage projects", "url": "#/projects", "icon": "folder", "permission": "projects"}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"text": "Content",
|
|
110
|
+
"icon": "edit",
|
|
111
|
+
"items": [
|
|
112
|
+
{"text": "Pages", "url": "#/pages", "icon": "file-text", "permission": "pages"},
|
|
113
|
+
{"text": "Media", "url": "#/media", "icon": "image", "permission": "media"},
|
|
114
|
+
{"text": "Menus", "url": "#/menus", "icon": "menu", "permission": "menus"}
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"text": "Data",
|
|
119
|
+
"icon": "database",
|
|
120
|
+
"items": [
|
|
121
|
+
{"text": "Collections", "url": "#/collections", "icon": "database", "permission": "collections"},
|
|
122
|
+
{"text": "Forms", "url": "#/forms", "icon": "layout", "permission": "collections"},
|
|
123
|
+
{"text": "Views", "url": "#/views", "icon": "eye", "permission": "views"},
|
|
124
|
+
{"text": "Actions", "url": "#/actions", "icon": "zap", "permission": "actions"},
|
|
125
|
+
{"text": "Blocks", "url": "#/blocks", "icon": "box", "permission": "pages"},
|
|
126
|
+
{"text": "Components", "url": "#/components", "icon": "component", "permission": "components"}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"text": "System",
|
|
131
|
+
"icon": "settings",
|
|
132
|
+
"items": [
|
|
133
|
+
{"text": "Notifications", "url": "#/system/notifications", "icon": "bell", "permission": "notifications"},
|
|
134
|
+
{"text": "Roles", "url": "#/roles", "icon": "shield", "permission": "plugins"},
|
|
135
|
+
{"text": "Users", "url": "#/users", "icon": "users", "permission": "users"},
|
|
136
|
+
{"text": "Site Settings", "url": "#/settings", "icon": "settings", "permission": "settings"},
|
|
137
|
+
{"text": "Effects", "url": "#/effects", "icon": "sparkles", "permission": "settings"},
|
|
138
|
+
{"text": "Layouts", "url": "#/layouts", "icon": "layout", "permission": "layouts"},
|
|
139
|
+
{"text": "Plugins", "url": "#/plugins", "icon": "package", "permission": "plugins"},
|
|
140
|
+
{"text": "My Profile", "url": "#/my-profile", "icon": "user"}
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"text": "Documentation",
|
|
145
|
+
"icon": "book",
|
|
146
|
+
"items": [
|
|
147
|
+
{"text": "Usage", "url": "#/documentation", "icon": "book"},
|
|
148
|
+
{"text": "Tutorials", "url": "#/tutorials", "icon": "document"},
|
|
149
|
+
{"text": "API Reference", "url": "#/api-reference", "icon": "code"}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Projects - special-case rendering
|
|
157
|
+
|
|
158
|
+
Per-project entries are NOT stored in the menu JSON. The `Projects` top-level item is a placeholder (it has only the static `Manage projects` child by default). At render time, the sidebar renderer:
|
|
159
|
+
|
|
160
|
+
1. Detects the item with `id === 'projects'` (or text `Projects`)
|
|
161
|
+
2. Calls `/api/projects` to get the user's visible projects + per-project artefact counts
|
|
162
|
+
3. Synthesises a child item per project with sub-items (Overview, populated type rows, Settings) - same shape as the current procedural code
|
|
163
|
+
|
|
164
|
+
This keeps per-project entries dynamic (they react to data changes without menu edits) while letting the admin still re-order or hide the `Projects` parent in the menu editor.
|
|
165
|
+
|
|
166
|
+
## Server-side Services
|
|
167
|
+
|
|
168
|
+
### `server/services/sidebar-migration.js` (new)
|
|
169
|
+
|
|
170
|
+
`runMigration({configDir})` - idempotent one-shot. Guarded by the existence of `config/menus/admin-sidebar.json`. Steps:
|
|
171
|
+
|
|
172
|
+
1. If `config/menus/admin-sidebar.json` exists → return `{migrated: false, reason: 'already present'}`
|
|
173
|
+
2. Write the seeded menu JSON (the shape above) to `config/menus/admin-sidebar.json`
|
|
174
|
+
3. Patch `config/menu-locations.json` to add `"admin-sidebar": "admin-sidebar"`
|
|
175
|
+
4. Log success
|
|
176
|
+
|
|
177
|
+
The seeded JSON is held in code (in the migration module). Future admin edits don't touch the migration - once the file exists, the migration is permanently skipped.
|
|
178
|
+
|
|
179
|
+
### `server/services/menus.js`
|
|
180
|
+
|
|
181
|
+
- Register the `admin-sidebar` core slot at module load (alongside the existing three core slots).
|
|
182
|
+
- Update `validateMenu` to allow the `permission` field on items (no validation of the value - that's a registry lookup at render time, runtime-tolerant).
|
|
183
|
+
|
|
184
|
+
### `server/services/hooks.js`
|
|
185
|
+
|
|
186
|
+
Add the `registerSidebarItem(opts)` export. Plugin-call shape:
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
hooks.registerSidebarItem({
|
|
190
|
+
parent: 'system', // existing item id to nest under; default = root level
|
|
191
|
+
item: {
|
|
192
|
+
text: 'My Plugin',
|
|
193
|
+
url: '#/plugins/my-plugin',
|
|
194
|
+
icon: 'package',
|
|
195
|
+
permission: 'plugins'
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Storage: module-level array `_registeredSidebarItems`. Exposed via `getRegisteredSidebarItems()` for the admin renderer to consume. Not persisted - plugins re-register on each server boot via their `setupPlugin()` hook.
|
|
201
|
+
|
|
202
|
+
## Server-side Boot Wiring
|
|
203
|
+
|
|
204
|
+
In `server/server.js`, after config load and before route registration (i.e. alongside the existing menus migration call):
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
try {
|
|
208
|
+
const {runMigration: runSidebarMigration} = await import('./services/sidebar-migration.js');
|
|
209
|
+
await runSidebarMigration();
|
|
210
|
+
} catch (err) {
|
|
211
|
+
app.log.warn(`[admin-sidebar] Migration skipped: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Admin Renderer
|
|
216
|
+
|
|
217
|
+
### `admin/js/lib/sidebar-renderer.js` (new)
|
|
218
|
+
|
|
219
|
+
Drop-in replacement for the existing `Domma.elements.sidebar(...)` call. Signature:
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
export async function renderAdminSidebar({mount, user, permissions, counts}) {
|
|
223
|
+
// ...
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Behaviour:
|
|
228
|
+
|
|
229
|
+
1. Fetch the resolved admin-sidebar menu via `/api/menus/admin-sidebar` (or the slug mapped at the `admin-sidebar` slot).
|
|
230
|
+
2. Fetch `/api/projects` and per-project artefact counts (for the synthesised Projects sub-tree).
|
|
231
|
+
3. Fetch dynamically-registered plugin items (from a new endpoint `/api/sidebar/registered-items` that returns the server-side `_registeredSidebarItems` snapshot).
|
|
232
|
+
4. Merge plugin items into the tree under their declared `parent`.
|
|
233
|
+
5. Walk the tree, filtering items by `permission` (using the supplied `permissions` array).
|
|
234
|
+
6. Render as nested `<details>`/`<summary>` elements. Expansion state per-item persists to `S.set('sidebar.expanded.<itemId>', bool)`.
|
|
235
|
+
7. Apply `data-icon` + run `Domma.icons.scan()` after render.
|
|
236
|
+
8. Wire the active-item indicator via `M.subscribe('router:afterChange', ...)`.
|
|
237
|
+
|
|
238
|
+
The render is async (it fetches projects). The mount point shows a skeleton (or the last cached render) until the first response lands.
|
|
239
|
+
|
|
240
|
+
**Fallback when no menu is mapped to `admin-sidebar`:** the renderer falls back to a minimal hardcoded tree containing only `Dashboard` + `Menus` (the second so the admin can navigate to fix the broken state). A WARN-level log entry is emitted. This covers: first boot before migration runs, an admin who deletes the menu by accident, or the menu file being lost on disk.
|
|
241
|
+
|
|
242
|
+
### `admin/js/app.js`
|
|
243
|
+
|
|
244
|
+
Find the existing `Domma.elements.sidebar('#admin-sidebar', {...})` call (around line 150). Replace with:
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
const {renderAdminSidebar} = await import('./lib/sidebar-renderer.js');
|
|
248
|
+
await renderAdminSidebar({
|
|
249
|
+
mount: '#admin-sidebar',
|
|
250
|
+
user,
|
|
251
|
+
permissions,
|
|
252
|
+
counts
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`buildSidebar(permissions)` simplifies - it no longer composes the items array; the renderer does that internally from menu data.
|
|
257
|
+
|
|
258
|
+
### `admin/js/config/sidebar-config.js`
|
|
259
|
+
|
|
260
|
+
Deprecated. Keep the file with a single-line export that returns `[]` for one release (in case any external code imports it). Add a deprecation comment at the top. Delete in v2.
|
|
261
|
+
|
|
262
|
+
### `admin/js/views/menu-editor.js`
|
|
263
|
+
|
|
264
|
+
The item-row template gains a `permission` field - a `<select>` populated from `/api/auth/permissions-registry` (already exposed). Default value: empty (no permission gate). The save handler includes `permission` on each item that has a value.
|
|
265
|
+
|
|
266
|
+
### `admin/css/admin.css`
|
|
267
|
+
|
|
268
|
+
Append styles for the `<details>`-based sidebar:
|
|
269
|
+
|
|
270
|
+
```css
|
|
271
|
+
#admin-sidebar details { /* no native marker; we provide our own */ }
|
|
272
|
+
#admin-sidebar details > summary {
|
|
273
|
+
list-style: none;
|
|
274
|
+
cursor: pointer;
|
|
275
|
+
display: flex;
|
|
276
|
+
align-items: center;
|
|
277
|
+
gap: 0.5rem;
|
|
278
|
+
padding: 0.4rem 0.75rem;
|
|
279
|
+
border-radius: 4px;
|
|
280
|
+
}
|
|
281
|
+
#admin-sidebar details > summary::-webkit-details-marker { display: none; }
|
|
282
|
+
#admin-sidebar details > summary::after {
|
|
283
|
+
content: '▸';
|
|
284
|
+
margin-left: auto;
|
|
285
|
+
transition: transform 0.15s;
|
|
286
|
+
}
|
|
287
|
+
#admin-sidebar details[open] > summary::after { transform: rotate(90deg); }
|
|
288
|
+
#admin-sidebar details > div { padding-left: 1rem; } /* indent rail for children */
|
|
289
|
+
#admin-sidebar a.active { background: var(--dm-accent-bg, rgba(0,0,0,0.06)); font-weight: 600; }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Data Flow (on render)
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
Browser load
|
|
296
|
+
↓
|
|
297
|
+
admin/js/app.js buildSidebar() → renderAdminSidebar()
|
|
298
|
+
↓
|
|
299
|
+
GET /api/menu-locations → slot map
|
|
300
|
+
GET /api/menus/admin-sidebar → full tree
|
|
301
|
+
GET /api/auth/permissions → user's permissions
|
|
302
|
+
GET /api/projects → user-visible projects (for Projects branch)
|
|
303
|
+
GET /api/projects/<slug>/artefacts (parallel per project) → per-section counts
|
|
304
|
+
GET /api/sidebar/registered-items → plugin-injected items
|
|
305
|
+
↓
|
|
306
|
+
Merge plugin items + project sub-tree into menu data
|
|
307
|
+
Filter by permission
|
|
308
|
+
Render nested <details>/<summary>
|
|
309
|
+
Apply icon scan + active-state subscriber
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## API Surface
|
|
313
|
+
|
|
314
|
+
One new endpoint:
|
|
315
|
+
|
|
316
|
+
| Method | Path | Permission | Description |
|
|
317
|
+
|---|---|---|---|
|
|
318
|
+
| `GET` | `/api/sidebar/registered-items` | `menus.read` | Plugin-registered sidebar items (read snapshot of `getRegisteredSidebarItems()`) |
|
|
319
|
+
|
|
320
|
+
No new permission family - reuses `menus.read` for the registry endpoint, and the existing `permissionRegistry` keys for item gating.
|
|
321
|
+
|
|
322
|
+
## Caching
|
|
323
|
+
|
|
324
|
+
New cache tag:
|
|
325
|
+
|
|
326
|
+
| Tag | Invalidated when |
|
|
327
|
+
|---|---|
|
|
328
|
+
| `admin-sidebar` | The `admin-sidebar` menu changes, or plugin items registered/removed |
|
|
329
|
+
|
|
330
|
+
The sidebar renderer's fetches go through the existing per-role + per-user cache keying so scoped users see correctly-filtered sidebars.
|
|
331
|
+
|
|
332
|
+
## Migration / Backwards Compat
|
|
333
|
+
|
|
334
|
+
- **`Domma.elements.sidebar` API is untouched** - non-CMS Domma sites that use the sidebar component directly are unaffected.
|
|
335
|
+
- **`sidebar-config.js` becomes a stub** for one release. Anyone importing it gets `[]` and a deprecation warning.
|
|
336
|
+
- **Existing user installs auto-migrate on first boot.** No admin action required. The migration is idempotent - re-runs are no-ops.
|
|
337
|
+
- **The Projects synth code** stays in `sidebar-renderer.js` rather than being injected into menu data, so a re-run of the sidebar migration doesn't try to re-import per-project entries.
|
|
338
|
+
|
|
339
|
+
## Documentation Updates
|
|
340
|
+
|
|
341
|
+
- `docs/configuration.md` - add a section on the `admin-sidebar` slot + the `permission` item field.
|
|
342
|
+
- `docs/plugin-development.md` - document `hooks.registerSidebarItem()` alongside the existing hooks.
|
|
343
|
+
- `docs/api-reference.md` - add `/api/sidebar/registered-items`.
|
|
344
|
+
- `CLAUDE.md` - note that the admin sidebar is now menu-data-driven and editable from the Menus admin.
|
|
345
|
+
|
|
346
|
+
## Out of Scope (Recorded for Later)
|
|
347
|
+
|
|
348
|
+
- Per-user sidebar customisation
|
|
349
|
+
- Drag-and-drop reorder directly in the sidebar
|
|
350
|
+
- Sidebar themes
|
|
351
|
+
- Removing the legacy `Domma.elements.sidebar` rendering path
|
|
352
|
+
|
|
353
|
+
## Open Follow-Ups (Tracked)
|
|
354
|
+
|
|
355
|
+
- **Once `domma-js` flyout PR lands**, optionally swap the `<details>`-based render for the native flyout behaviour (cleaner mobile UX).
|
|
356
|
+
- **Sidebar visual editor** in the Menus admin - drag-drop reorder, live preview of the rendered sidebar.
|
|
357
|
+
- **Plugin items declared in plugin manifest** (`plugin.json` `sidebar: {...}`) as an alternative to the `hooks.registerSidebarItem()` runtime call - declarative for plugins that don't need dynamic items.
|
|
358
|
+
- **`roles` permission family** - the seeded menu gates the Roles item via `permission: 'plugins'` to preserve the current sidebar quirk. Once `roles.{read,create,update,delete}` exists as its own family, the seeded menu (and the migration) should switch to the proper key. Tracked separately because it touches the permission registry shape.
|