domma-cms 1.6.60 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +4344 -0
  2. package/README.md +1 -1
  3. package/admin/dist/domma/domma-tools.css +3 -3
  4. package/admin/dist/domma/domma-tools.min.js +3 -3
  5. package/admin/js/lib/effect-defs.js +1 -1
  6. package/admin/js/lib/effects-builder.js +2 -2
  7. package/admin/js/lib/page-editor-reference.js +5 -5
  8. package/admin/js/lib/shortcode-tags.js +1 -1
  9. package/admin/js/templates/docs/usage-shortcodes.html +39 -0
  10. package/docs/DommaFeatures.md +3005 -0
  11. package/docs/MultiTenant.md +176 -0
  12. package/docs/api-reference.md +1667 -0
  13. package/docs/cache.md +96 -0
  14. package/docs/configuration.md +1811 -0
  15. package/docs/editions-and-licences.md +190 -0
  16. package/docs/getting-started.md +201 -0
  17. package/docs/guides/form-triggers.md +260 -0
  18. package/docs/guides/mongodb-storage-adapter.md +282 -0
  19. package/docs/guides/multi-page-forms.md +121 -0
  20. package/docs/images/scribe-composer.png +0 -0
  21. package/docs/markdown-shortcodes.md +1738 -0
  22. package/docs/pentest-report-2026-04-03.md +181 -0
  23. package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
  24. package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
  25. package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
  26. package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
  27. package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
  28. package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
  29. package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
  30. package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
  31. package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
  32. package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
  33. package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
  34. package/docs/plans/2026-03-05-user-types-design.md +139 -0
  35. package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
  36. package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
  37. package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
  38. package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
  39. package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
  40. package/docs/plans/2026-06-10-core-project-design.md +240 -0
  41. package/docs/plugin-development.md +827 -0
  42. package/docs/recruitment-recipe.md +501 -0
  43. package/docs/scaffolding.md +312 -0
  44. package/docs/screenshots/calculator-floating.png +0 -0
  45. package/docs/screenshots/calculator-standard.png +0 -0
  46. package/docs/screenshots/email-settings.png +0 -0
  47. package/docs/screenshots/messages-count.png +0 -0
  48. package/docs/structure/user-types.md +107 -0
  49. package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
  50. package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
  51. package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
  52. package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
  53. package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
  54. package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
  55. package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
  56. package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
  57. package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
  58. package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
  59. package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
  60. package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
  61. package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
  62. package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
  63. package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
  64. package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
  65. package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
  66. package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
  67. package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
  68. package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
  69. package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
  70. package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
  71. package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
  72. package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
  73. package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
  74. package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
  75. package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
  76. package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
  77. package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
  78. package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
  79. package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
  80. package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
  81. package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
  82. package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
  83. package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
  84. package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
  85. package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
  86. package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
  87. package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
  88. package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
  89. package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
  90. package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
  91. package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
  92. package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
  93. package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
  94. package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
  95. package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
  96. package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
  97. package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
  98. package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
  99. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
  100. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
  101. package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
  102. package/docs/theming.md +320 -0
  103. package/package.json +2 -2
  104. package/public/js/celebrations/core/canvas.js +1 -1
  105. package/public/js/celebrations/index.js +1 -1
  106. package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
  107. package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
  108. package/public/js/celebrations/message-attrs.mjs +1 -0
  109. package/public/js/celebrations/themes/anniversary.js +1 -0
  110. package/public/js/celebrations/themes/autumn.js +1 -0
  111. package/public/js/celebrations/themes/birthday.js +1 -0
  112. package/public/js/celebrations/themes/butterfly-flight.js +1 -0
  113. package/public/js/celebrations/themes/calendar.js +1 -0
  114. package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
  115. package/public/js/celebrations/themes/chinese-new-year.js +1 -0
  116. package/public/js/celebrations/themes/christmas.js +1 -1
  117. package/public/js/celebrations/themes/easter.js +1 -0
  118. package/public/js/celebrations/themes/garden-drawing.js +1 -0
  119. package/public/js/celebrations/themes/garden.js +1 -0
  120. package/public/js/celebrations/themes/graduation.js +1 -0
  121. package/public/js/celebrations/themes/greeting.js +1 -0
  122. package/public/js/celebrations/themes/make-it-rain.js +1 -0
  123. package/public/js/celebrations/themes/money-rain.js +1 -0
  124. package/public/js/celebrations/themes/new-year.js +1 -0
  125. package/public/js/celebrations/themes/occasion-drawing.js +1 -0
  126. package/public/js/celebrations/themes/occasions.js +1 -0
  127. package/public/js/celebrations/themes/rain-control.js +1 -0
  128. package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
  129. package/public/js/celebrations/themes/sparkler-font.js +1 -0
  130. package/public/js/celebrations/themes/sparkler-writing.js +1 -0
  131. package/public/js/celebrations/themes/summer-garden.js +1 -0
  132. package/public/js/celebrations/themes/winter.js +1 -0
  133. package/public/js/celebrations/trait-data.mjs +1 -1
  134. package/public/js/effects.js +1 -1
  135. package/scripts/build.js +4 -0
  136. package/server/services/markdown.js +5 -2
  137. 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.
@@ -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.