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,312 @@
|
|
|
1
|
+
# Scaffolding - One-Click CRUD from a Recipe
|
|
2
|
+
|
|
3
|
+
The CMS ships with **scaffolder recipes**: JSON templates that create a
|
|
4
|
+
working Collection + Form + Actions in one shot. From the page editor's CRUD
|
|
5
|
+
tutorial slideover, click "Apply template" and a complete subsystem is built,
|
|
6
|
+
wired together, and ready to use - including a copy-pastable shortcode you
|
|
7
|
+
can drop into the current page.
|
|
8
|
+
|
|
9
|
+
This is the difference between "documentation tells you how to build it" and
|
|
10
|
+
"the platform builds it for you, you change the parts that don't fit".
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Bundled recipes
|
|
15
|
+
|
|
16
|
+
| Recipe | What it builds |
|
|
17
|
+
|---|---|
|
|
18
|
+
| **Employee Onboarding** (`onboarding`) | `onboarding` collection (full-name, email, start-date, role, right-to-work file, references, status), `onboarding-form` collecting the same, and three actions for `Pending → Reviewing → Approved/Rejected` with HR email notifications |
|
|
19
|
+
| **Contact list (CRM-lite)** (`contact-list`) | `contacts` collection (name, email, phone, company, tags, notes, status), `contact-quick-add` form, one transition action for "needs follow-up" |
|
|
20
|
+
|
|
21
|
+
Recipes live in `server/services/recipes/*.json` - add your own with the
|
|
22
|
+
schema below and they appear in the picker automatically.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## How to apply a recipe
|
|
27
|
+
|
|
28
|
+
1. Edit any page in the admin
|
|
29
|
+
2. In the toolbar, click the **📖 CRUD tutorial** button (between sparkles and help-circle)
|
|
30
|
+
3. In the highlighted "Or skip ahead - scaffold a working system in one click" panel, click a template
|
|
31
|
+
4. Edit the slug fields if you want to rename anything, then click **Apply template**
|
|
32
|
+
5. On success, click **Insert into this page** to paste the ready-made shortcode at your cursor
|
|
33
|
+
|
|
34
|
+
The slideover stays open so you can apply multiple templates without
|
|
35
|
+
navigating away. Each apply is independent.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Recipe format
|
|
40
|
+
|
|
41
|
+
A recipe is a JSON file with this shape:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"slug": "onboarding",
|
|
46
|
+
"name": "Employee Onboarding",
|
|
47
|
+
"description": "Short blurb shown in the picker",
|
|
48
|
+
"icon": "user-plus",
|
|
49
|
+
|
|
50
|
+
"options": [
|
|
51
|
+
{ "name": "collectionSlug", "label": "Collection slug", "default": "onboarding", "hint": "Where records are stored" },
|
|
52
|
+
{ "name": "formSlug", "label": "Form slug", "default": "onboarding-form", "hint": "Embed with [form name=\"…\" /]" },
|
|
53
|
+
{ "name": "actionPrefix", "label": "Action prefix", "default": "onboarding", "hint": "Per-action slugs become <prefix>-review, etc." }
|
|
54
|
+
],
|
|
55
|
+
|
|
56
|
+
"collection": {
|
|
57
|
+
"title": "Onboarding",
|
|
58
|
+
"description": "New-starter records",
|
|
59
|
+
"fields": [ /* schema fields - text, number, select, multiselect, date, file, reference, etc. */ ],
|
|
60
|
+
"api": { /* api.read/create/update/delete gates */ },
|
|
61
|
+
"rowAccess": { /* per-op row-level access */ }
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"form": {
|
|
65
|
+
"title": "Employee Onboarding",
|
|
66
|
+
"fields": [ /* form fields - usually mirror the collection */ ],
|
|
67
|
+
"settings": {
|
|
68
|
+
"submitText": "Send to HR",
|
|
69
|
+
"successMessage": "Thanks - HR will review your details shortly.",
|
|
70
|
+
"honeypot": true,
|
|
71
|
+
"rateLimitPerMinute": 3,
|
|
72
|
+
"actionSlug": ""
|
|
73
|
+
},
|
|
74
|
+
"actions": {
|
|
75
|
+
"collection": { "enabled": true, "slug": "" }
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
"actions": [
|
|
80
|
+
{
|
|
81
|
+
"slug": "{{actionPrefix}}-review",
|
|
82
|
+
"title": "Move to reviewing",
|
|
83
|
+
"collection": "{{collectionSlug}}",
|
|
84
|
+
"trigger": { "label": "Mark as reviewing", "icon": "eye", "style": "primary" },
|
|
85
|
+
"transition": { "field": "status", "from": ["pending"], "to": "reviewing" },
|
|
86
|
+
"access": { "roles": ["admin", "super-admin"] },
|
|
87
|
+
"steps": [
|
|
88
|
+
{ "type": "updateField", "config": { "field": "status", "value": "reviewing" } }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
/* … more actions … */
|
|
92
|
+
],
|
|
93
|
+
|
|
94
|
+
"menus": [
|
|
95
|
+
{
|
|
96
|
+
"slug": "{{menuSlug}}",
|
|
97
|
+
"name": "Members nav",
|
|
98
|
+
"items": [{"text": "Members", "url": "/members"}],
|
|
99
|
+
"variant": "default",
|
|
100
|
+
"position": "sticky",
|
|
101
|
+
"locations": ["sidebar"]
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
|
|
105
|
+
"wireForm": true,
|
|
106
|
+
"snippet": "[form name=\"{{formSlug}}\" /]\n\n[collection slug=\"{{collectionSlug}}\" display=\"cards\" searchable transitions /]"
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Option types
|
|
111
|
+
|
|
112
|
+
Each option may carry a `type`, which decides what happens to a value the
|
|
113
|
+
user supplies:
|
|
114
|
+
|
|
115
|
+
| `type` | What happens |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `slug` (default when absent) | slugified - lower case, anything other than letters and digits becomes `-` |
|
|
118
|
+
| `email` | trimmed; refused with 400 unless it looks like an address |
|
|
119
|
+
| `password` | kept exactly as typed; never logged or echoed |
|
|
120
|
+
| `text` | trimmed |
|
|
121
|
+
|
|
122
|
+
Only `slug` options are slugified. The onboarding recipe types `hrEmail` as
|
|
123
|
+
`email` and `hrPassword` as `password` (a seed user's password must be at least
|
|
124
|
+
8 characters), and the Apply template form shows those as email and password
|
|
125
|
+
fields.
|
|
126
|
+
|
|
127
|
+
### Token substitution
|
|
128
|
+
|
|
129
|
+
Tokens of the form `{{optionName}}` are substituted with the resolved option
|
|
130
|
+
values throughout the recipe at apply time. Two important exceptions are
|
|
131
|
+
**not** substituted at scaffold time - they're left intact for action
|
|
132
|
+
execution to resolve later:
|
|
133
|
+
|
|
134
|
+
- `{{entry.…}}` - entry field values at action runtime
|
|
135
|
+
- `{{user.…}}` - executing user at action runtime
|
|
136
|
+
- `{{now}}` and `{{env.CMS_PUBLIC_*}}` - runtime constants
|
|
137
|
+
|
|
138
|
+
This means an email step like:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{ "to": "{{entry.data.email}}", "subject": "Welcome, {{entry.data.fullName}}" }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
…is preserved as-is in the created action and resolved when the action fires
|
|
145
|
+
against a real entry. The scaffolder only swaps the option-level tokens
|
|
146
|
+
(`{{collectionSlug}}`, `{{formSlug}}`, `{{actionPrefix}}`, etc.).
|
|
147
|
+
|
|
148
|
+
### `wireForm` + `snippet`
|
|
149
|
+
|
|
150
|
+
When `wireForm: true` and the recipe has at least one action created, the
|
|
151
|
+
scaffolder sets the form's `settings.actionSlug` to the first created
|
|
152
|
+
action - so submitting the form fires that action automatically.
|
|
153
|
+
|
|
154
|
+
`snippet` is a Markdown fragment returned to the caller. The tutorial UI
|
|
155
|
+
offers it as a one-click insert at the editor's cursor. It's typically a
|
|
156
|
+
form embed + a collection block so the new system is immediately visible.
|
|
157
|
+
|
|
158
|
+
### `menus`
|
|
159
|
+
|
|
160
|
+
Optional `menus` array seeds one or more menus alongside the collection,
|
|
161
|
+
form, and actions - useful when a recipe installs a feature (e.g. a
|
|
162
|
+
members area) that needs its own nav. Each entry has the same shape as a
|
|
163
|
+
`config/menus/<slug>.json` file plus an optional `locations` array of
|
|
164
|
+
slot names to wire the menu into.
|
|
165
|
+
|
|
166
|
+
Bundled-from-recipe menus are tagged with `meta.bundled: true` and
|
|
167
|
+
`meta.presetOwner: <recipeSlug>` so the admin can identify them. The
|
|
168
|
+
scaffolder pre-flights menu slug conflicts the same way it pre-flights
|
|
169
|
+
collections, forms, and actions. A `force: true` field on a `menus[]`
|
|
170
|
+
entry lets a recipe overwrite an existing slot-to-menu mapping;
|
|
171
|
+
without it, an already-mapped slot logs a warning and the new menu is
|
|
172
|
+
created but left unmapped.
|
|
173
|
+
|
|
174
|
+
### Recipe `project` block
|
|
175
|
+
|
|
176
|
+
A recipe may declare an optional `project` block. When present, the
|
|
177
|
+
scaffolder creates a Project record (see `docs/configuration.md` →
|
|
178
|
+
Projects) before any artefact is written, and every artefact produced
|
|
179
|
+
by the recipe is tagged with `meta.project: <namespace>`.
|
|
180
|
+
|
|
181
|
+
Block shape:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
"project": {
|
|
185
|
+
"name": "Members Area",
|
|
186
|
+
"description": "Members-only zone",
|
|
187
|
+
"icon": "users",
|
|
188
|
+
"rootUrl": "/{{namespace}}",
|
|
189
|
+
"sortOrder": 10
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Only `name` is required; the other fields mirror the project record schema.
|
|
194
|
+
|
|
195
|
+
Behaviour:
|
|
196
|
+
|
|
197
|
+
- **`tokens.namespace` becomes the project slug.** The same option that
|
|
198
|
+
already cascades into every artefact slug also identifies the project.
|
|
199
|
+
- **Idempotent.** If a project with that slug already exists, it is left
|
|
200
|
+
alone - re-applying the recipe does not clobber edits made to the project
|
|
201
|
+
record. If it doesn't exist, it is created.
|
|
202
|
+
- **Every produced artefact gets `meta.project: <namespace>` stamped** -
|
|
203
|
+
collections, forms, actions, menus, roles and users. API tokens and API
|
|
204
|
+
endpoints are bound to the project through their own `project` field.
|
|
205
|
+
(Recipes cannot create blocks, views or pages; tag those with a project in
|
|
206
|
+
their own editors.)
|
|
207
|
+
- **Seeded users additionally get `projects: ['<namespace>']`** on their
|
|
208
|
+
access-scope field, so a recipe-seeded user can immediately log in and
|
|
209
|
+
see only the project they were created for.
|
|
210
|
+
- **Creation failure aborts the apply.** Validation or write errors in the
|
|
211
|
+
project block fail the whole recipe before any artefact is written.
|
|
212
|
+
|
|
213
|
+
Example recipe with a project block:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"slug": "members-area",
|
|
218
|
+
"name": "Members Area",
|
|
219
|
+
"options": [{"name": "namespace", "default": "members"}],
|
|
220
|
+
"project": {
|
|
221
|
+
"name": "Members Area",
|
|
222
|
+
"description": "Members-only zone",
|
|
223
|
+
"icon": "users",
|
|
224
|
+
"rootUrl": "/{{namespace}}"
|
|
225
|
+
},
|
|
226
|
+
"collection": { ... },
|
|
227
|
+
"form": { ... },
|
|
228
|
+
"menus": [ ... ]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Recipes without a `project` block still work - they produce untagged
|
|
233
|
+
artefacts, which belong to the built-in **core** project by exclusion
|
|
234
|
+
(see `docs/configuration.md` → Projects → The core project).
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Conflict handling
|
|
239
|
+
|
|
240
|
+
The scaffolder pre-flights every target slug (collection, form, each action,
|
|
241
|
+
each menu) and refuses to apply if any already exists. The response includes
|
|
242
|
+
the full conflict list so the user can rename and retry without partial
|
|
243
|
+
creation. The recipe's project is created only after this check passes, so a
|
|
244
|
+
refused apply writes nothing.
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
HTTP 409
|
|
248
|
+
{
|
|
249
|
+
"error": "Cannot apply recipe - conflicts: …",
|
|
250
|
+
"conflicts": ["Collection \"onboarding\" already exists"]
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Actions require MongoDB (Pro)
|
|
257
|
+
|
|
258
|
+
Collections and Forms are file-based and work in every install. Actions
|
|
259
|
+
require a configured MongoDB connection (the Pro tier). When no MongoDB is
|
|
260
|
+
available the scaffolder still creates the collection and form, then reports
|
|
261
|
+
each action as skipped with the warning:
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
MongoDB not configured - action "onboarding-review" skipped (Pro feature)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The pieces that did get created are fully functional; you can either
|
|
268
|
+
configure MongoDB and re-apply (with renamed slugs) to add the actions, or
|
|
269
|
+
manage transitions manually via the admin.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## API endpoints
|
|
274
|
+
|
|
275
|
+
| Method | Path | Auth | Description |
|
|
276
|
+
|---|---|---|---|
|
|
277
|
+
| `GET` | `/api/scaffold/recipes` | `collections.create` | List bundled recipes |
|
|
278
|
+
| `GET` | `/api/scaffold/recipes/:slug` | `collections.create` | Full recipe document |
|
|
279
|
+
| `POST` | `/api/scaffold/apply` | `collections.create` + what the recipe creates | Apply a recipe; body: `{recipe, options}` |
|
|
280
|
+
|
|
281
|
+
Apply checks, before it writes anything, that the caller may create every
|
|
282
|
+
kind of thing the recipe will create, and answers 403 naming each missing
|
|
283
|
+
permission (also in `missing`): a new project `projects.create`; new roles and
|
|
284
|
+
user accounts `users.create`; menus `menus.create` (slot mapping
|
|
285
|
+
`menus.update`); a collection or form `collections.create`; actions
|
|
286
|
+
`actions.create`; API tokens `api-tokens.create`; API endpoints
|
|
287
|
+
`api-endpoints.create`. A role the recipe creates must sit below the caller's
|
|
288
|
+
level and grant only permissions the caller holds, and a seeded user's role
|
|
289
|
+
must sit below the caller's level. The level-0 role passes every check.
|
|
290
|
+
|
|
291
|
+
Apply response:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"created": { "collection": "onboarding", "form": "onboarding-form", "actions": ["onboarding-review", "onboarding-approve", "onboarding-reject"] },
|
|
296
|
+
"skipped": [],
|
|
297
|
+
"warnings": [],
|
|
298
|
+
"snippet": "[form name=\"onboarding-form\" /]\n\n[collection slug=\"onboarding\" …]"
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Writing your own recipe
|
|
305
|
+
|
|
306
|
+
Drop a JSON file into `server/services/recipes/` matching the shape above.
|
|
307
|
+
It's picked up automatically on next admin load - no restart needed.
|
|
308
|
+
|
|
309
|
+
For complex recipes that produce multiple collections (e.g. jobs + applications +
|
|
310
|
+
companies), the current format supports one collection per recipe. The
|
|
311
|
+
roadmap for multi-collection recipes lives under a `collections: [...]` and
|
|
312
|
+
`forms: [...]` array - happy to land that when there's a use case.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Roles - Structure Documentation
|
|
2
|
+
|
|
3
|
+
Roles are the CMS's user types. They control what each user can access and are stored as a **preset Collection**
|
|
4
|
+
(`roles`) so they can be managed from the admin UI without editing config files.
|
|
5
|
+
|
|
6
|
+
> This page used to describe a `user-types` collection and `server/services/userTypes.js`. Neither exists any more:
|
|
7
|
+
> roles live in the `roles` collection and are served by `server/services/roles.js`. The old Manager, Editor and
|
|
8
|
+
> Subscriber roles were retired - on startup any user still holding one is moved to `user`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Storage
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
content/collections/roles/
|
|
16
|
+
├── schema.json - field definitions (preset - rewritten on every start, do not edit)
|
|
17
|
+
└── data.json - role entries (edit via System > Roles)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Schema Fields
|
|
21
|
+
|
|
22
|
+
| Field | Type | Description |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `name` | text | Slug-style role key used in code (e.g. `blog-editor`) |
|
|
25
|
+
| `label` | text | Human-readable display name (e.g. `Blog Editor`) |
|
|
26
|
+
| `level` | number | Seniority - lower = more senior. The Super Admin role is level 0 |
|
|
27
|
+
| `permissions` | multi-select | Resources (`pages`) or single actions (`pages.read`) this role holds |
|
|
28
|
+
| `badgeClass` | select | CSS badge class for UI display |
|
|
29
|
+
| `plugin` | text | Set on roles contributed by a plugin |
|
|
30
|
+
|
|
31
|
+
Roles may also carry an `admin: {home, allow: [...]}` block that confines holders to certain admin screens (set in the
|
|
32
|
+
role editor's **Admin area**).
|
|
33
|
+
|
|
34
|
+
### Permissions
|
|
35
|
+
|
|
36
|
+
Permissions come from the permission registry (`server/services/permissionRegistry.js`, also served at
|
|
37
|
+
`GET /api/auth/permissions-registry`). Each resource has actions - normally `read`, `create`, `update` and `delete`
|
|
38
|
+
(a few differ, for example `theme` is read/update and `seo` is read/manage). A role can hold:
|
|
39
|
+
|
|
40
|
+
- a bare resource, e.g. `pages` - every action on that resource, or
|
|
41
|
+
- a single action, e.g. `pages.read`.
|
|
42
|
+
|
|
43
|
+
Core resources are grouped as:
|
|
44
|
+
|
|
45
|
+
- **Content**: pages, media, blocks, components
|
|
46
|
+
- **Structure**: navigation, menus, context-menus, projects, layouts
|
|
47
|
+
- **Data**: collections, views, actions
|
|
48
|
+
- **Configuration**: users, theme, settings, plugins, notifications, api-tokens, api-endpoints
|
|
49
|
+
- **Tools**: contacts, analytics, seo, notes, todo
|
|
50
|
+
|
|
51
|
+
Enabled plugins add their own resources on top (declared in `plugin.json` `permissions[]`).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Auth Integration
|
|
56
|
+
|
|
57
|
+
At server startup `server/services/roles.js` seeds the collection if needed, then reads `data.json` and builds
|
|
58
|
+
in-memory caches:
|
|
59
|
+
|
|
60
|
+
- **`roleMap`** - `{ roleName: { label, level, badgeClass, admin? } }` - used to validate role claims and compare
|
|
61
|
+
seniority
|
|
62
|
+
- **`permissionsMap`** - `{ 'resource' | 'resource.action': [roleName, ...] }` - used by `requirePermission()`
|
|
63
|
+
route preHandlers
|
|
64
|
+
|
|
65
|
+
A user has one primary `role` plus optional `additionalRoles`. A permission check passes if any of their roles grants
|
|
66
|
+
it; level checks use their most senior role. A level-0 role always holds every permission, including those added
|
|
67
|
+
later by plugins.
|
|
68
|
+
|
|
69
|
+
### Cache Invalidation
|
|
70
|
+
|
|
71
|
+
The cache is rebuilt whenever a role entry is created, updated or deleted through the admin (role records are
|
|
72
|
+
read and written through the collections API, `/api/collections/roles/entries`). Changes take effect
|
|
73
|
+
immediately - no server restart required.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Base Roles
|
|
78
|
+
|
|
79
|
+
Three base roles are seeded on first start and cannot be deleted:
|
|
80
|
+
|
|
81
|
+
| Name | Label | Level | Permissions |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `super-admin` | Super Admin | 0 | Everything, including permissions added by plugins |
|
|
84
|
+
| `admin` | Admin | 1 | pages, media, blocks, components, navigation, menus, projects, layouts, collections, views, actions, users, settings, notifications, plugins, api-tokens, api-endpoints, context-menus, theme |
|
|
85
|
+
| `user` | User | 2 | notifications |
|
|
86
|
+
|
|
87
|
+
On every start, permission families newly shipped in an update are added to the base roles. This is append-only:
|
|
88
|
+
permissions an admin added are kept (and a seeded permission removed from a base role comes back - use a custom role
|
|
89
|
+
to run with less). Custom roles are never touched.
|
|
90
|
+
|
|
91
|
+
The first account (created by `npm run setup` or the browser setup screen) is a Super Admin.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Adding a New Role
|
|
96
|
+
|
|
97
|
+
1. Go to **System > Roles**
|
|
98
|
+
2. Click **New role**
|
|
99
|
+
3. Fill in the name (slug), label and level, and tick the permissions this role should have in the permission
|
|
100
|
+
matrix
|
|
101
|
+
4. Save - the change takes effect immediately
|
|
102
|
+
|
|
103
|
+
To assign the role, go to **System > Users**, edit the user, and pick it as the primary role or add it as an
|
|
104
|
+
additional role. You can only manage users whose role is less senior (a higher level number) than your own.
|
|
105
|
+
|
|
106
|
+
Plugins can also ship roles (`roles/*.json` in the plugin, or `hooks.registerRole()`). A plugin role is created once
|
|
107
|
+
and then belongs to the admin: disabling the plugin keeps it, uninstalling removes it.
|