domma-cms 0.93.0 → 0.94.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 (64) hide show
  1. package/admin/css/admin.css +1 -1
  2. package/admin/js/app.js +2 -2
  3. package/admin/js/templates/docs/api-actions.html +86 -64
  4. package/admin/js/templates/docs/api-authentication.html +159 -123
  5. package/admin/js/templates/docs/api-builder.html +197 -0
  6. package/admin/js/templates/docs/api-collections.html +199 -259
  7. package/admin/js/templates/docs/api-external.html +225 -0
  8. package/admin/js/templates/docs/api-forms.html +268 -0
  9. package/admin/js/templates/docs/api-layouts.html +70 -45
  10. package/admin/js/templates/docs/api-media.html +57 -80
  11. package/admin/js/templates/docs/api-navigation.html +66 -22
  12. package/admin/js/templates/docs/api-pages.html +109 -129
  13. package/admin/js/templates/docs/api-plugins.html +123 -61
  14. package/admin/js/templates/docs/api-scaffold.html +185 -0
  15. package/admin/js/templates/docs/api-settings.html +72 -64
  16. package/admin/js/templates/docs/api-users.html +74 -107
  17. package/admin/js/templates/docs/api-views.html +68 -54
  18. package/admin/js/templates/docs/components-howto.html +20 -17
  19. package/admin/js/templates/docs/components-reference.html +13 -16
  20. package/admin/js/templates/docs/components-rules.html +7 -6
  21. package/admin/js/templates/docs/components-walkthrough.html +19 -19
  22. package/admin/js/templates/docs/tutorial-crud.html +68 -38
  23. package/admin/js/templates/docs/tutorial-forms.html +51 -35
  24. package/admin/js/templates/docs/tutorial-plugin.html +132 -56
  25. package/admin/js/templates/docs/usage-actions.html +55 -14
  26. package/admin/js/templates/docs/usage-collections.html +108 -0
  27. package/admin/js/templates/docs/usage-cta-shortcode.html +14 -3
  28. package/admin/js/templates/docs/usage-dconfig.html +0 -3
  29. package/admin/js/templates/docs/usage-editions.html +213 -0
  30. package/admin/js/templates/docs/usage-media.html +22 -6
  31. package/admin/js/templates/docs/usage-navigation.html +74 -18
  32. package/admin/js/templates/docs/usage-pages.html +60 -20
  33. package/admin/js/templates/docs/usage-plugins.html +89 -17
  34. package/admin/js/templates/docs/usage-shortcodes.html +123 -70
  35. package/admin/js/templates/docs/usage-site-settings.html +50 -18
  36. package/admin/js/templates/docs/usage-tools.html +73 -0
  37. package/admin/js/templates/docs/usage-users-roles.html +99 -20
  38. package/admin/js/templates/docs/usage-views.html +36 -19
  39. package/admin/js/templates/documentation.html +153 -32
  40. package/admin/js/templates/plugin-guide.html +15 -0
  41. package/admin/js/templates/plugin-guides.html +21 -0
  42. package/admin/js/templates/pro-docs.html +53 -234
  43. package/admin/js/templates/tutorials.html +5 -4
  44. package/admin/js/views/doc-pages.js +1 -1
  45. package/admin/js/views/index.js +1 -1
  46. package/admin/js/views/plugin-guides.js +5 -0
  47. package/bin/cli.js +6 -6
  48. package/package.json +1 -1
  49. package/plugins/blog/docs/guide.md +205 -0
  50. package/plugins/blog/plugin.json +1 -1
  51. package/plugins/feedback/docs/guide.md +95 -0
  52. package/plugins/feedback/plugin.json +1 -1
  53. package/plugins/free-tier.lock.json +16 -11
  54. package/plugins/mail-reader/docs/guide.md +147 -0
  55. package/plugins/mail-reader/plugin.json +1 -1
  56. package/plugins/security/docs/guide.md +170 -0
  57. package/plugins/security/plugin.json +1 -1
  58. package/plugins/shopping-cart/docs/guide.md +191 -0
  59. package/plugins/shopping-cart/plugin.json +1 -1
  60. package/server/routes/api/documentation.js +42 -0
  61. package/server/server.js +12 -0
  62. package/server/services/docs.js +13 -2
  63. package/server/services/pluginGuides.js +255 -0
  64. package/server/services/plugins.js +8 -0
@@ -1,259 +1,78 @@
1
1
  <div class="view-header">
2
2
  <h1><span data-icon="zap"></span> Pro Documentation</h1>
3
+ <a href="#/documentation" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All usage topics</a>
3
4
  </div>
4
5
 
5
6
  <div class="tabs" id="pro-docs-tabs">
6
7
  <div class="tab-list">
7
- <button class="tab-item active">Views</button>
8
+ <button class="tab-item active">What needs Pro</button>
9
+ <button class="tab-item">Views</button>
8
10
  <button class="tab-item">Actions</button>
9
- <button class="tab-item">CTA Shortcode</button>
10
11
  </div>
12
+
11
13
  <div class="tab-content">
12
14
 
13
- <!-- ── Views tab ─────────────────────────────────────── -->
15
+ <!-- What needs Pro -->
14
16
  <div class="tab-panel active">
15
-
16
- <div class="card card-collapsible mb-4">
17
- <div class="card-header" role="button" tabindex="0">
18
- <div class="card-header-content"><h2><span data-icon="eye"></span> What is a View?</h2></div>
19
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
20
- </div>
21
- <div class="card-body docs-body">
22
- <p>A <strong>View</strong> is a saved MongoDB aggregation pipeline that runs against a collection and returns shaped results. Views are created under <strong>Data → Views</strong> and exposed via a REST endpoint that respects role-level and row-level access.</p>
23
- <p>Views require a MongoDB connection configured in <code>config/connections.json</code>.</p>
24
- </div>
25
- </div>
26
-
27
- <div class="card card-collapsible mb-4">
28
- <div class="card-header" role="button" tabindex="0">
29
- <div class="card-header-content"><h2><span data-icon="layers"></span> Allowed Pipeline Stages</h2></div>
30
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
31
- </div>
17
+ <div class="card mb-4">
32
18
  <div class="card-body docs-body">
33
- <table class="table table-sm">
34
- <thead><tr><th>Stage</th><th>Purpose</th></tr></thead>
35
- <tbody>
36
- <tr><td><code>$match</code></td><td>Filter documents by field values</td></tr>
37
- <tr><td><code>$sort</code></td><td>Order results</td></tr>
38
- <tr><td><code>$project</code></td><td>Include, exclude, or rename fields</td></tr>
39
- <tr><td><code>$lookup</code></td><td>Join from another collection</td></tr>
40
- <tr><td><code>$unwind</code></td><td>Flatten an array field</td></tr>
41
- <tr><td><code>$addFields</code></td><td>Compute new fields</td></tr>
42
- <tr><td><code>$group</code></td><td>Aggregate (count, sum, etc.)</td></tr>
43
- <tr><td><code>$limit</code></td><td>Cap results</td></tr>
44
- <tr><td><code>$skip</code></td><td>Skip N results</td></tr>
45
- <tr><td><code>$count</code></td><td>Return a document count</td></tr>
46
- </tbody>
47
- </table>
48
- <p class="text-muted" style="margin-top:.75rem;">Blocked: <code>$out</code>, <code>$merge</code>, <code>$function</code>, <code>$accumulator</code>, <code>$graphLookup</code></p>
49
- <p>Collections are prefixed <code>cms_</code> in MongoDB - use this prefix in <code>$lookup</code> <code>from</code> values.</p>
19
+ <p>Pro, for the data features, means one thing: a working <strong>MongoDB connection</strong>
20
+ (the <strong>Database connections</strong> tab on Data &gt; Collections, stored in <code>config/connections.json</code>). With one, a site gets:</p>
21
+ <ul>
22
+ <li><strong>Actions</strong> - workflow buttons, transitions and <code>[cta]</code>. Without MongoDB,
23
+ Data &gt; Actions says so and nothing can be run.</li>
24
+ <li><strong>MongoDB storage</strong> for any collection, chosen on its Storage tab. Moving a collection
25
+ either way keeps every entry's id.</li>
26
+ <li><strong>Notes and Todo on MongoDB</strong>, as their collections declare. A site whose files
27
+ still hold notes or to-dos stays on files until the Storage move settles it.</li>
28
+ </ul>
29
+ <p>Everything else works without it: collections on files, <strong>Views</strong> (a built-in
30
+ evaluator runs the same pipeline stages), forms, <code>[collection]</code> and <code>[view]</code>
31
+ displays, the Collection Browser, the API and API Builder.</p>
32
+ <p>Pro plugins (SEO Pro, Analytics Pro, Security Pro, Blog Pro and others) are a separate matter: they come
33
+ from the Marketplace with a licence. See <a href="#/docs/usage/editions">Editions &amp; Licences</a> and
34
+ <a href="#/docs/plugins">Plugin guides</a>.</p>
50
35
  </div>
51
36
  </div>
37
+ </div>
52
38
 
53
- <div class="card card-collapsible mb-4">
54
- <div class="card-header" role="button" tabindex="0">
55
- <div class="card-header-content"><h2><span data-icon="code"></span> Example Pipeline</h2></div>
56
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
57
- </div>
58
- <div class="card-body docs-body">
59
- <p>Match active entries and count related submissions:</p>
60
- <pre class="code-block"><code>[
61
- { "$match": { "data.isActive": true } },
62
- {
63
- "$lookup": {
64
- "from": "cms_feedback",
65
- "localField": "_id",
66
- "foreignField": "data.userId",
67
- "as": "submissions"
68
- }
69
- },
70
- { "$addFields": { "submissionCount": { "$size": "$submissions" } } },
71
- { "$project": { "data.name": 1, "data.email": 1, "submissionCount": 1 } },
72
- { "$sort": { "submissionCount": -1 } }
73
- ]</code></pre>
74
- </div>
75
- </div>
76
-
77
- <div class="card card-collapsible mb-4">
78
- <div class="card-header" role="button" tabindex="0">
79
- <div class="card-header-content"><h2><span data-icon="lock"></span> Access Control</h2></div>
80
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
81
- </div>
82
- <div class="card-body docs-body">
83
- <table class="table table-sm">
84
- <thead><tr><th>Setting</th><th>Description</th></tr></thead>
85
- <tbody>
86
- <tr><td>Allowed Roles</td><td>Which roles can call <code>GET /api/views/:slug/public</code></td></tr>
87
- <tr><td>Public</td><td>No authentication required when checked</td></tr>
88
- <tr><td>Row-Level - Owner</td><td>User sees only entries they created</td></tr>
89
- <tr><td>Row-Level - Field Match</td><td>A specified entry field must match a user property</td></tr>
90
- </tbody>
91
- </table>
92
- </div>
93
- </div>
94
-
95
- </div><!-- /Views tab -->
96
-
97
- <!-- ── Actions tab ───────────────────────────────────── -->
39
+ <!-- Views -->
98
40
  <div class="tab-panel">
99
-
100
- <div class="card card-collapsible mb-4">
101
- <div class="card-header" role="button" tabindex="0">
102
- <div class="card-header-content"><h2><span data-icon="zap"></span> What is an Action?</h2></div>
103
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
104
- </div>
105
- <div class="card-body docs-body">
106
- <p>An <strong>Action</strong> is a named sequence of steps that runs against a single collection entry. Actions are defined under <strong>Data → Actions</strong> and can be triggered from the entry list or via the <code>[cta]</code> shortcode on any public page.</p>
107
- <p>Actions require a MongoDB connection configured in <code>config/connections.json</code>.</p>
108
- <p>Steps run in order. If a step fails, execution stops and a partial result is returned with a <code>stepsCompleted</code> count.</p>
109
- </div>
110
- </div>
111
-
112
- <div class="card card-collapsible mb-4">
113
- <div class="card-header" role="button" tabindex="0">
114
- <div class="card-header-content"><h2><span data-icon="list"></span> Step Types</h2></div>
115
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
116
- </div>
41
+ <div class="card mb-4">
117
42
  <div class="card-body docs-body">
118
- <table class="table table-sm">
119
- <thead><tr><th>Type</th><th>What it does</th></tr></thead>
120
- <tbody>
121
- <tr><td><code>updateField</code></td><td>Sets a field on the entry to a new value</td></tr>
122
- <tr><td><code>deleteEntry</code></td><td>Permanently deletes the target entry</td></tr>
123
- <tr><td><code>moveToCollection</code></td><td>Moves the entry to a different collection</td></tr>
124
- <tr><td><code>webhook</code></td><td>POSTs entry data to an external URL</td></tr>
125
- <tr><td><code>email</code></td><td>Sends an email via the configured SMTP server</td></tr>
126
- </tbody>
127
- </table>
43
+ <p>A <strong>View</strong> is a saved pipeline over a collection (<code>$match</code>,
44
+ <code>$lookup</code>, <code>$sort</code>, <code>$project</code>, <code>$unwind</code>,
45
+ <code>$addFields</code>, <code>$group</code>, <code>$count</code>, <code>$skip</code>,
46
+ <code>$limit</code>). Views do <strong>not</strong> need MongoDB: a MongoDB collection runs a native
47
+ aggregation, any other runs the built-in evaluator.</p>
48
+ <p>Who may read a view is set on its Access tab: public, any listed role and everyone more senior, or -
49
+ with no roles - the admin tier. A row-level rule (Owner or Field match) shows each user only their
50
+ results.</p>
51
+ <p>Full guide: <a href="#/docs/usage/views">Views</a> and the <a href="#/docs/api/views">Views API</a>.</p>
128
52
  </div>
129
53
  </div>
54
+ </div>
130
55
 
131
- <div class="card card-collapsible mb-4">
132
- <div class="card-header" role="button" tabindex="0">
133
- <div class="card-header-content"><h2><span data-icon="tag"></span> Template Variables</h2></div>
134
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
135
- </div>
136
- <div class="card-body docs-body">
137
- <p>All step field values support <code>&#123;&#123;...&#125;&#125;</code> interpolation:</p>
138
- <table class="table table-sm">
139
- <thead><tr><th>Variable</th><th>Value</th></tr></thead>
140
- <tbody>
141
- <tr><td><code>&#123;&#123;entry.data.fieldName&#125;&#125;</code></td><td>Any field from the target entry</td></tr>
142
- <tr><td><code>&#123;&#123;entry.id&#125;&#125;</code></td><td>The entry's UUID</td></tr>
143
- <tr><td><code>&#123;&#123;now&#125;&#125;</code></td><td>Current ISO 8601 timestamp</td></tr>
144
- <tr><td><code>&#123;&#123;user.name&#125;&#125;</code></td><td>Name of the triggering user</td></tr>
145
- <tr><td><code>&#123;&#123;user.email&#125;&#125;</code></td><td>Email of the triggering user</td></tr>
146
- <tr><td><code>&#123;&#123;user.id&#125;&#125;</code></td><td>UUID of the triggering user</td></tr>
147
- <tr><td><code>&#123;&#123;env.CMS_PUBLIC_*&#125;&#125;</code></td><td>Public environment variables</td></tr>
148
- </tbody>
149
- </table>
150
- </div>
151
- </div>
152
-
153
- <div class="card card-collapsible mb-4">
154
- <div class="card-header" role="button" tabindex="0">
155
- <div class="card-header-content"><h2><span data-icon="code"></span> Step Examples</h2></div>
156
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
157
- </div>
158
- <div class="card-body docs-body">
159
- <p><strong>updateField</strong> - mark an entry as approved:</p>
160
- <pre class="code-block"><code>{ "field": "status", "value": "approved" }</code></pre>
161
- <p><strong>webhook</strong> - notify an external service:</p>
162
- <pre class="code-block"><code>{
163
- "url": "https://hooks.example.com/notify",
164
- "headers": { "X-Secret": "{{env.CMS_PUBLIC_HOOK_SECRET}}" }
165
- }</code></pre>
166
- <p><strong>email</strong> - send confirmation to applicant:</p>
167
- <pre class="code-block"><code>{
168
- "to": "{{entry.data.email}}",
169
- "subject": "Your application was approved",
170
- "body": "Hi {{entry.data.name}}, your application has been approved."
171
- }</code></pre>
172
- </div>
173
- </div>
174
-
175
- </div><!-- /Actions tab -->
176
-
177
- <!-- ── CTA Shortcode tab ──────────────────────────────── -->
56
+ <!-- Actions -->
178
57
  <div class="tab-panel">
179
-
180
- <div class="card card-collapsible mb-4">
181
- <div class="card-header" role="button" tabindex="0">
182
- <div class="card-header-content"><h2><span data-icon="mouse-pointer"></span> What is the CTA shortcode?</h2></div>
183
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
184
- </div>
185
- <div class="card-body docs-body">
186
- <p>The <code>[cta]</code> shortcode places an action-trigger button in any public page. Clicking it calls <code>POST /api/actions/:slug/public</code> with the entry ID, using the logged-in user's JWT.</p>
187
- <p>If the user is not logged in, a warning toast is shown instead.</p>
188
- </div>
189
- </div>
190
-
191
- <div class="card card-collapsible mb-4">
192
- <div class="card-header" role="button" tabindex="0">
193
- <div class="card-header-content"><h2><span data-icon="code"></span> Syntax &amp; Attributes</h2></div>
194
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
195
- </div>
196
- <div class="card-body docs-body">
197
- <p><strong>Wrapping form:</strong></p>
198
- <pre class="code-block"><code>[cta action="slug" entry="entry-id" icon="check" confirm="Are you sure?"]Button label[/cta]</code></pre>
199
- <p><strong>Self-closing form:</strong></p>
200
- <pre class="code-block"><code>[cta action="slug" entry="entry-id" label="Button label" /]</code></pre>
201
- <table class="table table-sm" style="margin-top:1rem;">
202
- <thead><tr><th>Attribute</th><th>Required</th><th>Default</th><th>Description</th></tr></thead>
203
- <tbody>
204
- <tr><td><code>action</code></td><td>Yes</td><td>-</td><td>Action slug</td></tr>
205
- <tr><td><code>entry</code></td><td>Yes</td><td>-</td><td>Entry UUID to act on</td></tr>
206
- <tr><td><code>label</code></td><td>No</td><td><code>"Run"</code></td><td>Button text (self-closing only)</td></tr>
207
- <tr><td><code>style</code></td><td>No</td><td><code>"primary"</code></td><td>primary · secondary · ghost · danger</td></tr>
208
- <tr><td><code>icon</code></td><td>No</td><td>-</td><td>Domma icon name</td></tr>
209
- <tr><td><code>size</code></td><td>No</td><td>-</td><td>sm · md · lg</td></tr>
210
- <tr><td><code>confirm</code></td><td>No</td><td>-</td><td>Confirmation prompt before executing</td></tr>
211
- </tbody>
212
- </table>
213
- </div>
214
- </div>
215
-
216
- <div class="card card-collapsible mb-4">
217
- <div class="card-header" role="button" tabindex="0">
218
- <div class="card-header-content"><h2><span data-icon="grid"></span> Collection Integration</h2></div>
219
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
220
- </div>
221
- <div class="card-body docs-body">
222
- <p>Add per-entry buttons to any <code>[collection]</code> shortcode:</p>
223
- <pre class="code-block"><code>[collection slug="applications" display="cards" title-field="name"
224
- cta="approve-application"
225
- cta-label="Approve"
226
- cta-icon="check"
227
- cta-style="primary"
228
- cta-confirm="Approve this application?" /]</code></pre>
229
- <table class="table table-sm" style="margin-top:1rem;">
230
- <thead><tr><th>Attribute</th><th>Default</th><th>Description</th></tr></thead>
231
- <tbody>
232
- <tr><td><code>cta</code></td><td>-</td><td>Action slug - enables per-entry buttons</td></tr>
233
- <tr><td><code>cta-label</code></td><td><code>"Run"</code></td><td>Button label</td></tr>
234
- <tr><td><code>cta-icon</code></td><td>-</td><td>Domma icon name</td></tr>
235
- <tr><td><code>cta-style</code></td><td><code>"primary"</code></td><td>Button variant</td></tr>
236
- <tr><td><code>cta-confirm</code></td><td>-</td><td>Confirmation prompt</td></tr>
237
- </tbody>
238
- </table>
239
- <p>All three display modes support CTA buttons: <strong>cards</strong> (card footer), <strong>list</strong> (inline), <strong>table</strong> (dedicated column).</p>
240
- </div>
241
- </div>
242
-
243
- <div class="card card-collapsible mb-4">
244
- <div class="card-header" role="button" tabindex="0">
245
- <div class="card-header-content"><h2><span data-icon="book-open"></span> Full Reference</h2></div>
246
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
247
- </div>
58
+ <div class="card mb-4">
248
59
  <div class="card-body docs-body">
249
- <p>See the public documentation for live examples and the complete attribute reference:</p>
250
- <a href="/resources/pro/cta" target="_blank" class="btn btn-ghost btn-sm">
251
- <span data-icon="external-link"></span> CTA Shortcode Reference
252
- </a>
60
+ <p>An <strong>Action</strong> is a named sequence of steps (update a field, delete, move to or create in
61
+ another collection, webhook, email, notify) run against one entry. Actions need MongoDB.</p>
62
+ <ul>
63
+ <li>Any role the action lists is enough to run it, and everyone more senior. No roles means admins
64
+ only; a role the site does not have admits only the level-0 role.</li>
65
+ <li>A transition makes the action a workflow step, offered only in the states it starts from. An
66
+ interactive <code>[collection ... transitions]</code> shows the buttons that apply to each row,
67
+ including on a <code>scope="mine"</code> block. A move that no longer applies is refused with 409.</li>
68
+ <li>Steps are not rolled back if a later one fails - put deletes and emails last.</li>
69
+ </ul>
70
+ <p>Full guides: <a href="#/docs/usage/actions">Actions</a>,
71
+ <a href="#/docs/usage/cta-shortcode">CTA Shortcode</a> and the
72
+ <a href="#/docs/api/actions">Actions API</a>.</p>
253
73
  </div>
254
74
  </div>
75
+ </div>
255
76
 
256
- </div><!-- /CTA tab -->
257
-
258
- </div><!-- .tab-content -->
259
- </div><!-- .tabs -->
77
+ </div>
78
+ </div>
@@ -28,8 +28,8 @@
28
28
  <div class="card-body">
29
29
  <h3 style="margin-top:0;"><span data-icon="package"></span> Writing a Plugin</h3>
30
30
  <p class="text-muted" style="margin-bottom:0;">
31
- Extend the CMS with your own server routes, admin pages and page-injection snippets.
32
- The three required files, the Fastify entry point, and how to register and test.
31
+ Extend the CMS with your own server routes, admin pages and page-injection snippets:
32
+ the manifest, the Fastify entry point, switching it on and testing it.
33
33
  </p>
34
34
  </div>
35
35
  </a>
@@ -50,8 +50,9 @@
50
50
 
51
51
  <p class="text-muted" style="font-size:.9rem;">
52
52
  Looking for reusable custom elements? See <a href="#/docs/components">Documentation → Components</a>.
53
- For task-by-task usage, see <a href="#/documentation">Usage</a>; for the REST surface, see
54
- <a href="#/api-reference">API Reference</a>.
53
+ For task-by-task usage, see <a href="#/documentation">Documentation</a>; for the REST surface, see
54
+ <a href="#/api-reference">API Reference</a>; for installed Marketplace plugins, see
55
+ <a href="#/docs/plugins">Plugin guides</a>.
55
56
  </p>
56
57
 
57
58
  </div>
@@ -1 +1 @@
1
- import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,n){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof n=="function"&&n(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const n=o.find("#tutorial-scaffolder-mount").get(0);n&&i(n,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions"],s=o=>o.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");for(const o of a)docViews["usage"+s(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+s(o)]=t(`api-${o}`);
1
+ import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,s){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof s=="function"&&s(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const s=o.find("#tutorial-scaffolder-mount").get(0);s&&i(s,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode","editions","collections","tools"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions","forms","scaffold","external","builder"],n=o=>o.split("-").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("");for(const o of a)docViews["usage"+n(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+n(o)]=t(`api-${o}`);
@@ -1 +1 @@
1
- import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{themeView as e}from"./theme.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{pluginCodeView as V}from"./plugin-code.js";import{documentationView as a}from"./documentation.js";import{tutorialsView as d}from"./tutorials.js";import{docViews as l}from"./doc-pages.js";import{apiReferenceView as u}from"./api-reference.js";import{collectionsView as E}from"./collections.js";import{collectionEditorView as g}from"./collection-editor.js";import{collectionEntriesView as v}from"./collection-entries.js";import{formsView as b}from"./forms.js";import{formEditorView as h}from"./form-editor.js";import{formSubmissionsView as y}from"./form-submissions.js";import{viewsListView as j}from"./views-list.js";import{viewEditorView as k}from"./view-editor.js";import{viewPreviewView as L}from"./view-preview.js";import{actionsListView as x}from"./actions-list.js";import{actionEditorView as M}from"./action-editor.js";import{proDocsView as D}from"./pro-docs.js";import{blocksView as P}from"./blocks.js";import{blockEditorView as R}from"./block-editor.js";import{componentsView as S}from"./components.js";import{componentEditorView as C}from"./component-editor.js";import{myProfileView as T}from"./my-profile.js";import{rolesView as U}from"./roles.js";import{roleEditorView as q}from"./role-editor.js";import{effectsView as z}from"./effects.js";import{searchView as A}from"./search.js";import{notesView as B}from"./notes.js";import{todoView as F}from"./todo.js";import{analyticsView as G}from"./analytics.js";import{seoView as H}from"./seo.js";import{contactsView as I}from"./contacts.js";import{menusView as J}from"./menus.js";import{contextMenusView as K}from"./context-menus.js";import{contextMenuEditorView as N}from"./context-menu-editor.js";import{menuEditorView as O}from"./menu-editor.js";import{menuLocationsView as Q}from"./menu-locations.js";import{projectsView as W}from"./projects.js";import{projectDetailView as X}from"./project-detail.js";import{projectSettingsView as Y}from"./project-settings.js";import{apiTokensView as Z}from"./api-tokens.js";import{apiEndpointsView as _}from"./api-endpoints.js";import{apiEndpointEditorView as $}from"./api-endpoint-editor.js";const oo={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:W,projectDetail:X,projectSettings:Y,apiTokens:Z,apiEndpoints:_,apiEndpointEditor:$,dashboard:o,pages:i,pageEditor:r,settings:t,theme:e,layouts:p,media:s,login:n,users:f,userEditor:w,plugins:c,pluginCode:V,documentation:a,tutorials:d,apiReference:u,collections:E,collectionEditor:g,collectionEntries:v,forms:b,formEditor:h,formSubmissions:y,viewsList:j,viewEditor:k,viewPreview:L,actionsList:x,actionEditor:M,proDocs:D,blocks:P,blockEditor:R,components:S,componentEditor:C,myProfile:T,roles:U,roleEditor:q,effects:z,search:A,notes:B,todo:F,analytics:G,seo:H,contacts:I,notifications:m,menus:J,contextMenus:K,contextMenuEditor:N,menuEditor:O,menuLocations:Q,menusRedirect:oo,...l};
1
+ import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{themeView as e}from"./theme.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as n}from"./media.js";import{loginView as s}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{pluginCodeView as V}from"./plugin-code.js";import{documentationView as d}from"./documentation.js";import{tutorialsView as a}from"./tutorials.js";import{docViews as l}from"./doc-pages.js";import{pluginGuidesView as u,pluginGuideView as E}from"./plugin-guides.js";import{apiReferenceView as g}from"./api-reference.js";import{collectionsView as v}from"./collections.js";import{collectionEditorView as b}from"./collection-editor.js";import{collectionEntriesView as h}from"./collection-entries.js";import{formsView as y}from"./forms.js";import{formEditorView as j}from"./form-editor.js";import{formSubmissionsView as k}from"./form-submissions.js";import{viewsListView as L}from"./views-list.js";import{viewEditorView as x}from"./view-editor.js";import{viewPreviewView as M}from"./view-preview.js";import{actionsListView as D}from"./actions-list.js";import{actionEditorView as G}from"./action-editor.js";import{proDocsView as P}from"./pro-docs.js";import{blocksView as R}from"./blocks.js";import{blockEditorView as S}from"./block-editor.js";import{componentsView as C}from"./components.js";import{componentEditorView as T}from"./component-editor.js";import{myProfileView as U}from"./my-profile.js";import{rolesView as q}from"./roles.js";import{roleEditorView as z}from"./role-editor.js";import{effectsView as A}from"./effects.js";import{searchView as B}from"./search.js";import{notesView as F}from"./notes.js";import{todoView as H}from"./todo.js";import{analyticsView as I}from"./analytics.js";import{seoView as J}from"./seo.js";import{contactsView as K}from"./contacts.js";import{menusView as N}from"./menus.js";import{contextMenusView as O}from"./context-menus.js";import{contextMenuEditorView as Q}from"./context-menu-editor.js";import{menuEditorView as W}from"./menu-editor.js";import{menuLocationsView as X}from"./menu-locations.js";import{projectsView as Y}from"./projects.js";import{projectDetailView as Z}from"./project-detail.js";import{projectSettingsView as _}from"./project-settings.js";import{apiTokensView as $}from"./api-tokens.js";import{apiEndpointsView as oo}from"./api-endpoints.js";import{apiEndpointEditorView as io}from"./api-endpoint-editor.js";const ro={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:Y,projectDetail:Z,projectSettings:_,apiTokens:$,apiEndpoints:oo,apiEndpointEditor:io,dashboard:o,pages:i,pageEditor:r,settings:t,theme:e,layouts:p,media:n,login:s,users:f,userEditor:w,plugins:c,pluginCode:V,documentation:d,tutorials:a,apiReference:g,collections:v,collectionEditor:b,collectionEntries:h,forms:y,formEditor:j,formSubmissions:k,viewsList:L,viewEditor:x,viewPreview:M,actionsList:D,actionEditor:G,proDocs:P,blocks:R,blockEditor:S,components:C,componentEditor:T,myProfile:U,roles:q,roleEditor:z,effects:A,search:B,notes:F,todo:H,analytics:I,seo:J,contacts:K,notifications:m,menus:N,contextMenus:O,contextMenuEditor:Q,menuEditor:W,menuLocations:X,menusRedirect:ro,pluginGuides:u,pluginGuide:E,...l};
@@ -0,0 +1,5 @@
1
+ const a=t=>String(t??"").replace(/[&<>"']/g,n=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[n]),l=t=>/^[a-z0-9-]+$/i.test(String(t||""))?t:"package";export function parseGuideHash(t){const n=String(t||"").match(/^#\/docs\/plugins\/([^/?#]+)(?:\/([^/?#]+))?/);return n?{name:decodeURIComponent(n[1]),page:n[2]?decodeURIComponent(n[2]):null}:null}function d(){Domma.icons.scan(),Domma.syntax?.scan?.()}export const pluginGuidesView={templateUrl:"/admin/js/templates/plugin-guides.html",async onMount(t){const n=t.find("#pg-grid").get(0);let o=[];try{o=(await H.get("/api/documentation/plugins"))?.guides??[]}catch{o=[]}if(n?.isConnected){if(!o.length){t.find("#pg-empty").get(0)?.removeAttribute("hidden"),d();return}n.innerHTML=o.map(e=>{const s=e.pages.length>1?`<p class="pg-card-pages">${e.pages.map(p=>a(p.title)).join(" \xB7 ")}</p>`:"";return`<a href="#/docs/plugins/${encodeURIComponent(e.name)}" class="card pg-card">
2
+ <div class="card-body">
3
+ <h3><span data-icon="${a(l(e.icon))}"></span> ${a(e.displayName)}</h3>
4
+ <p class="text-muted">${a(e.description)}</p>${s}
5
+ </div></a>`}).join(""),d()}}},pluginGuideView={templateUrl:"/admin/js/templates/plugin-guide.html",async onMount(t){const n=parseGuideHash(location.hash),o=t.find("#pg-body").get(0);if(!n||!o)return;let e=null,s="";try{const i=`/api/documentation/plugins/${encodeURIComponent(n.name)}`+(n.page?`/${encodeURIComponent(n.page)}`:"");e=await H.get(i)}catch(i){s=i?.status===403||/403|forbidden/i.test(String(i?.message))?"Your role cannot use this plugin, so its guide is not shown.":"There is no guide here. The plugin may be disabled, or the page may have moved."}if(!o.isConnected)return;if(!e?.page){o.innerHTML=`<p class="text-muted">${a(s||"There is no guide here.")}</p><p><a href="#/docs/plugins">See all plugin guides</a></p>`,d();return}t.find("#pg-title").get(0).textContent=`${e.displayName} - ${e.page.title}`;const p=t.find("#pg-icon").get(0);p&&(p.innerHTML=`<span data-icon="${a(l(e.icon))}"></span>`),document.title=`${e.displayName} guide - Domma CMS`;const c=[e.version&&`Version ${e.version}`,e.tier==="free"?"Free plugin":e.tier==="paid"?"Paid plugin":""].filter(Boolean).join(" \xB7 ");if(t.find("#pg-meta").get(0).textContent=[c,e.description].filter(Boolean).join(" - "),e.toolUrl){const i=t.find("#pg-tool").get(0);i.setAttribute("href",e.toolUrl),t.find("#pg-tool-text").get(0).textContent=`Open ${e.displayName}`,i.removeAttribute("hidden")}if(e.pages.length>1){const i=t.find("#pg-pages").get(0);i.innerHTML=e.pages.map(g=>{const r=g.slug===e.page.slug;return`<a href="#/docs/plugins/${encodeURIComponent(e.name)}/${encodeURIComponent(g.slug)}" class="btn btn-sm ${r?"btn-secondary":"btn-ghost"}"${r?' aria-current="page"':""}>${a(g.title)}</a>`}).join(""),i.removeAttribute("hidden")}o.innerHTML=e.page.html,d()}};
package/bin/cli.js CHANGED
@@ -24,14 +24,14 @@ const PACKAGE_ROOT = path.resolve(__dirname, '..');
24
24
  // Node version check
25
25
  // ---------------------------------------------------------------------------
26
26
 
27
- // The minimum comes from package.json `engines`. Below it, a WARNING rather
28
- // than a refusal: the manager's fleet tooling (tools/fleet-update.mjs) runs
29
- // `cli.js update` on the Node it runs on, which is older than engines today,
30
- // and a hard stop here would halt every fleet roll. Make it exit(1) once the
31
- // fleet is on the engines version.
27
+ // The minimum comes from package.json `engines`, and below it the CLI stops:
28
+ // scaffolding or updating on an older Node leaves a site that will not run.
29
+ // The fleet (the manager and every site it forks) runs on Node 24, so fleet
30
+ // rolls pass this check.
32
31
  const NODE_RANGE = JSON.parse(readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8')).engines?.node;
33
32
  if (!nodeVersionOk(process.versions.node, NODE_RANGE)) {
34
- console.warn(`\n ⚠ Domma CMS needs Node.js ${minNodeMajor(NODE_RANGE)} or higher (this is ${process.versions.node}). Continuing, but upgrade Node.\n`);
33
+ console.error(`\n ✗ Domma CMS requires Node.js ${minNodeMajor(NODE_RANGE)} or higher (this is ${process.versions.node}). Upgrade Node and run this again.\n`);
34
+ process.exit(1);
35
35
  }
36
36
 
37
37
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.93.0",
3
+ "version": "0.94.0",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",