domma-cms 0.92.1 → 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.
- package/CLAUDE.md +5 -3
- package/admin/css/admin.css +1 -1
- package/admin/js/app.js +2 -2
- package/admin/js/lib/action-editor-arrange.js +1 -1
- package/admin/js/lib/api-tokens-arrange.js +2 -2
- package/admin/js/lib/block-editor-arrange.js +1 -1
- package/admin/js/lib/blocks-arrange.js +1 -1
- package/admin/js/lib/collection-entries-arrange.js +1 -1
- package/admin/js/lib/components-arrange.js +1 -1
- package/admin/js/lib/dashboard-arrange.js +1 -1
- package/admin/js/lib/dates.js +1 -0
- package/admin/js/lib/forms-arrange.js +1 -1
- package/admin/js/lib/media-arrange.js +1 -1
- package/admin/js/lib/notifications-arrange.js +1 -1
- package/admin/js/lib/pages-arrange.js +1 -1
- package/admin/js/lib/related.js +1 -1
- package/admin/js/lib/timeline-builder.js +2 -2
- package/admin/js/templates/action-editor.html +6 -5
- package/admin/js/templates/actions-list.html +1 -1
- package/admin/js/templates/contacts.html +1 -1
- package/admin/js/templates/docs/api-actions.html +86 -60
- package/admin/js/templates/docs/api-authentication.html +159 -123
- package/admin/js/templates/docs/api-builder.html +197 -0
- package/admin/js/templates/docs/api-collections.html +199 -259
- package/admin/js/templates/docs/api-external.html +225 -0
- package/admin/js/templates/docs/api-forms.html +268 -0
- package/admin/js/templates/docs/api-layouts.html +70 -45
- package/admin/js/templates/docs/api-media.html +57 -80
- package/admin/js/templates/docs/api-navigation.html +66 -22
- package/admin/js/templates/docs/api-pages.html +109 -129
- package/admin/js/templates/docs/api-plugins.html +123 -61
- package/admin/js/templates/docs/api-scaffold.html +185 -0
- package/admin/js/templates/docs/api-settings.html +72 -64
- package/admin/js/templates/docs/api-users.html +74 -107
- package/admin/js/templates/docs/api-views.html +68 -54
- package/admin/js/templates/docs/components-howto.html +20 -17
- package/admin/js/templates/docs/components-reference.html +13 -16
- package/admin/js/templates/docs/components-rules.html +7 -6
- package/admin/js/templates/docs/components-walkthrough.html +19 -19
- package/admin/js/templates/docs/tutorial-crud.html +71 -40
- package/admin/js/templates/docs/tutorial-forms.html +51 -35
- package/admin/js/templates/docs/tutorial-plugin.html +132 -56
- package/admin/js/templates/docs/usage-actions.html +61 -15
- package/admin/js/templates/docs/usage-collections.html +108 -0
- package/admin/js/templates/docs/usage-cta-shortcode.html +14 -3
- package/admin/js/templates/docs/usage-dconfig.html +0 -3
- package/admin/js/templates/docs/usage-editions.html +213 -0
- package/admin/js/templates/docs/usage-media.html +22 -6
- package/admin/js/templates/docs/usage-navigation.html +74 -18
- package/admin/js/templates/docs/usage-pages.html +60 -20
- package/admin/js/templates/docs/usage-plugins.html +89 -17
- package/admin/js/templates/docs/usage-shortcodes.html +123 -70
- package/admin/js/templates/docs/usage-site-settings.html +50 -18
- package/admin/js/templates/docs/usage-tools.html +73 -0
- package/admin/js/templates/docs/usage-users-roles.html +99 -20
- package/admin/js/templates/docs/usage-views.html +36 -19
- package/admin/js/templates/documentation.html +153 -32
- package/admin/js/templates/page-editor.html +0 -5
- package/admin/js/templates/plugin-guide.html +15 -0
- package/admin/js/templates/plugin-guides.html +21 -0
- package/admin/js/templates/pro-docs.html +53 -234
- package/admin/js/templates/tutorials.html +5 -4
- package/admin/js/views/actions-list.js +3 -3
- package/admin/js/views/analytics.js +5 -5
- package/admin/js/views/api-endpoint-editor.js +2 -2
- package/admin/js/views/block-editor.js +4 -4
- package/admin/js/views/blocks.js +4 -4
- package/admin/js/views/collection-editor.js +4 -4
- package/admin/js/views/collection-entries.js +7 -7
- package/admin/js/views/component-editor.js +2 -2
- package/admin/js/views/contacts.js +22 -20
- package/admin/js/views/context-menu-editor.js +5 -5
- package/admin/js/views/doc-pages.js +1 -1
- package/admin/js/views/form-editor.js +4 -4
- package/admin/js/views/form-submissions.js +2 -2
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/media.js +3 -3
- package/admin/js/views/menu-editor.js +13 -13
- package/admin/js/views/menu-locations.js +2 -2
- package/admin/js/views/my-profile.js +1 -1
- package/admin/js/views/page-editor.js +8 -8
- package/admin/js/views/plugin-guides.js +5 -0
- package/admin/js/views/project-detail.js +2 -2
- package/admin/js/views/project-settings.js +1 -1
- package/admin/js/views/role-editor.js +4 -4
- package/admin/js/views/search.js +2 -2
- package/admin/js/views/seo.js +17 -17
- package/admin/js/views/settings.js +3 -3
- package/admin/js/views/theme.js +3 -3
- package/admin/js/views/user-editor.js +1 -1
- package/admin/js/views/users.js +2 -2
- package/admin/js/views/view-editor.js +1 -1
- package/bin/cli.js +13 -13
- package/bin/lib/node-version.js +29 -0
- package/package.json +1 -1
- package/plugins/_lib/admin/mail/compose-window.js +3 -2
- package/plugins/_lib/admin/mail/reader-view.js +7 -6
- package/plugins/_lib/admin/mail/scheduling.js +4 -2
- package/plugins/_lib/admin/mail/templates.js +4 -4
- package/plugins/_lib/admin/ui/dates.js +85 -0
- package/plugins/blog/CLAUDE.md +31 -22
- package/plugins/blog/admin/views/blog.js +3 -2
- package/plugins/blog/admin/views/comments.js +2 -1
- package/plugins/blog/admin/views/post-editor.js +4 -4
- package/plugins/blog/blocks/blog-card-row.html +1 -1
- package/plugins/blog/blocks/blog-card.html +2 -2
- package/plugins/blog/blocks/blog-post-classic.html +2 -2
- package/plugins/blog/blocks/blog-post-essay.html +2 -2
- package/plugins/blog/blocks/blog-post-feature.html +2 -2
- package/plugins/blog/blocks/blog-post-minimal.html +2 -2
- package/plugins/blog/blocks/blog-post-sidebar.html +2 -2
- package/plugins/blog/blocks/blog-post-split.html +2 -2
- package/plugins/blog/docs/guide.md +205 -0
- package/plugins/blog/lib/layouts.js +3 -3
- package/plugins/blog/lib/page.js +2 -1
- package/plugins/blog/plugin.js +3 -3
- package/plugins/blog/plugin.json +4 -4
- package/plugins/blog/tests/layouts.test.js +6 -0
- package/plugins/feedback/CLAUDE.md +22 -3
- package/plugins/feedback/admin/lib/kit.js +6 -7
- package/plugins/feedback/admin/views/feedback.js +79 -10
- package/plugins/feedback/admin/views/send.js +28 -6
- package/plugins/feedback/docs/guide.md +95 -0
- package/plugins/feedback/lib/receiver.js +9 -2
- package/plugins/feedback/lib/sender.js +3 -2
- package/plugins/feedback/plugin.js +54 -6
- package/plugins/feedback/plugin.json +4 -4
- package/plugins/feedback/tests/api.test.js +74 -2
- package/plugins/free-tier.lock.json +49 -44
- package/plugins/mail-reader/CLAUDE.md +33 -18
- package/plugins/mail-reader/docs/guide.md +147 -0
- package/plugins/mail-reader/plugin.json +1 -1
- package/plugins/security/CLAUDE.md +4 -1
- package/plugins/security/admin/views/security.js +5 -5
- package/plugins/security/docs/guide.md +170 -0
- package/plugins/security/plugin.js +2 -1
- package/plugins/security/plugin.json +2 -1
- package/plugins/shopping-cart/CLAUDE.md +7 -1
- package/plugins/shopping-cart/admin/lib/kit.js +5 -2
- package/plugins/shopping-cart/admin/views/orders.js +4 -4
- package/plugins/shopping-cart/admin/views/overview.js +2 -2
- package/plugins/shopping-cart/docs/guide.md +191 -0
- package/plugins/shopping-cart/lib/render.js +2 -1
- package/plugins/shopping-cart/plugin.json +3 -3
- package/public/js/collection-browser.js +2 -2
- package/public/js/site.js +1 -1
- package/scripts/gen-instance-secret.js +3 -1
- package/scripts/setup.js +3 -1
- package/server/middleware/auth.js +2 -1
- package/server/routes/api/actions.js +47 -27
- package/server/routes/api/blocks.js +2 -1
- package/server/routes/api/collections.js +16 -52
- package/server/routes/api/contacts.js +66 -3
- package/server/routes/api/documentation.js +42 -0
- package/server/routes/api/notifications.js +3 -2
- package/server/routes/api/users.js +10 -6
- package/server/server.js +16 -1
- package/server/services/actions.js +110 -34
- package/server/services/adapterRegistry.js +169 -16
- package/server/services/adapters/FileAdapter.js +25 -0
- package/server/services/adapters/MongoAdapter.js +23 -0
- package/server/services/collections.js +104 -1
- package/server/services/connectionManager.js +12 -0
- package/server/services/dates.js +81 -0
- package/server/services/docs.js +13 -2
- package/server/services/markdown.js +75 -26
- package/server/services/notification-sources.js +6 -5
- package/server/services/passwordReset.js +2 -1
- package/server/services/permissionRegistry.js +3 -2
- package/server/services/pluginGuides.js +255 -0
- package/server/services/pluginInstaller.js +54 -13
- package/server/services/plugins.js +29 -1
- package/server/services/presetCollections.js +31 -5
- package/server/services/renderer.js +2 -2
- package/server/services/sidebarBadges.js +3 -1
- package/server/services/tools.js +4 -2
- package/server/templates/page.html +2 -2
|
@@ -69,118 +69,85 @@
|
|
|
69
69
|
<div class="col-12">
|
|
70
70
|
<div class="docs-body">
|
|
71
71
|
|
|
72
|
-
<p>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
<p>Users are managed with the <code>users</code> permission (Super Admin and Admin by default; each action -
|
|
73
|
+
read, create, update, delete - can be granted on its own in the role editor). The role ladder decides whom you
|
|
74
|
+
may manage: you can only create, edit, delete or reset the password of a user whose role is less senior (a higher
|
|
75
|
+
level number) than your own, so an Admin cannot manage a Super Admin or another Admin. You can always edit your
|
|
76
|
+
own account, but not raise your own role, and you cannot delete it. Passwords are never returned.</p>
|
|
77
|
+
|
|
78
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/users</span></h3>
|
|
79
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> read permission.</p>
|
|
80
|
+
<p>Every user you may see (a user confined to some projects sees the users of those projects), each with their
|
|
81
|
+
profile fields.</p>
|
|
82
|
+
<pre class="code-block"><code>// Response 200
|
|
83
|
+
[ { "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "additionalRoles": [],
|
|
84
|
+
"isActive": true, "lastLogin": "...", "profile": {} } ]</code></pre>
|
|
85
|
+
|
|
86
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/users/:id</span></h3>
|
|
87
|
+
<p class="auth-note">Requires Bearer token. The user themselves, or a role with <code>users</code> read permission.</p>
|
|
88
|
+
<p>A single user, with their profile.</p>
|
|
89
|
+
<pre class="code-block"><code>// Response 200
|
|
90
|
+
{ "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "isActive": true, "profile": {} }
|
|
90
91
|
// Error 404
|
|
91
92
|
{ "error": "User not found" }</code></pre>
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<td><code>email</code></td>
|
|
113
|
-
<td>string</td>
|
|
114
|
-
<td>Unique email address</td>
|
|
115
|
-
</tr>
|
|
116
|
-
<tr>
|
|
117
|
-
<td><code>password</code></td>
|
|
118
|
-
<td>string</td>
|
|
119
|
-
<td>Minimum 8 characters</td>
|
|
120
|
-
</tr>
|
|
121
|
-
<tr>
|
|
122
|
-
<td><code>role</code></td>
|
|
123
|
-
<td>string</td>
|
|
124
|
-
<td>Optional. Defaults to <code>user</code>.</td>
|
|
125
|
-
</tr>
|
|
126
|
-
</tbody>
|
|
127
|
-
</table>
|
|
128
|
-
<pre class="code-block"><code>// Response 201
|
|
94
|
+
<h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/users</span></h3>
|
|
95
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> create permission.</p>
|
|
96
|
+
<p>Create a user. You can only give roles (primary and additional) less senior than your own.</p>
|
|
97
|
+
<table class="table table-sm">
|
|
98
|
+
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
99
|
+
<tbody>
|
|
100
|
+
<tr><td><code>name</code></td><td>string</td><td>Display name</td></tr>
|
|
101
|
+
<tr><td><code>email</code></td><td>string</td><td>Unique email address</td></tr>
|
|
102
|
+
<tr><td><code>password</code></td><td>string</td><td>At least 8 characters, plus any rules a plugin such as
|
|
103
|
+
Security adds</td></tr>
|
|
104
|
+
<tr><td><code>role</code></td><td>string</td><td>Optional. Defaults to <code>user</code>.</td></tr>
|
|
105
|
+
<tr><td><code>additionalRoles</code></td><td>array</td><td>Optional. More roles; a permission check passes if
|
|
106
|
+
any role grants it.</td></tr>
|
|
107
|
+
<tr><td><code>projects</code></td><td>array</td><td>Optional. Project slugs the user is confined to.</td></tr>
|
|
108
|
+
<tr><td><code>isActive</code></td><td>boolean</td><td>Optional. Defaults to <code>true</code>.</td></tr>
|
|
109
|
+
<tr><td><code>profile</code></td><td>object</td><td>Optional. Profile field values.</td></tr>
|
|
110
|
+
</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
<pre class="code-block"><code>// Response 201
|
|
129
113
|
{ "id": "uuid", "name": "Bob", "email": "bob@example.com", "role": "user", "isActive": true }
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<td>New role</td>
|
|
167
|
-
</tr>
|
|
168
|
-
<tr>
|
|
169
|
-
<td><code>isActive</code></td>
|
|
170
|
-
<td>boolean</td>
|
|
171
|
-
<td>Enable or disable the account</td>
|
|
172
|
-
</tr>
|
|
173
|
-
</tbody>
|
|
174
|
-
</table>
|
|
175
|
-
<pre class="code-block"><code>// Response 200 - returns the updated user object</code></pre>
|
|
176
|
-
|
|
177
|
-
<h3><span class="method-badge method-delete">DELETE</span><span
|
|
178
|
-
class="endpoint-path">/api/users/:id</span></h3>
|
|
179
|
-
<p class="auth-note">Requires Bearer token + <code>users</code> permission.</p>
|
|
180
|
-
<p>Delete a user. Cannot delete your own account or a user with a higher role level.</p>
|
|
181
|
-
<pre class="code-block"><code>// Response 200
|
|
114
|
+
// Error 403
|
|
115
|
+
{ "error": "You cannot create a user with that role" }
|
|
116
|
+
// Error 409 - the email is already in use</code></pre>
|
|
117
|
+
|
|
118
|
+
<h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/users/:id</span></h3>
|
|
119
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> update permission.</p>
|
|
120
|
+
<p>Update a user. Send only the fields to change: <code>name</code>, <code>email</code>, <code>password</code>,
|
|
121
|
+
<code>role</code>, <code>additionalRoles</code>, <code>projects</code>, <code>isActive</code>,
|
|
122
|
+
<code>profile</code>. A new password, or switching an account off, ends that user's sessions.</p>
|
|
123
|
+
<pre class="code-block"><code>// Response 200 - the updated user, with profile
|
|
124
|
+
// Error 403
|
|
125
|
+
{ "error": "You cannot edit a user with that role" }</code></pre>
|
|
126
|
+
|
|
127
|
+
<h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/users/:id/password-reset</span></h3>
|
|
128
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> update permission. Not for your own account.</p>
|
|
129
|
+
<p>Start a password reset for someone else: email them a link (the default), or with
|
|
130
|
+
<code>{"via": "link"}</code> get the link back to pass on yourself - for a site without email. The account must
|
|
131
|
+
be active and less senior than yours.</p>
|
|
132
|
+
<pre class="code-block"><code>// Request body (optional)
|
|
133
|
+
{ "via": "email" } // or "link"
|
|
134
|
+
// Response 200
|
|
135
|
+
{ "sent": true, "expiresAt": "...", "expiresIn": "1 hour" }
|
|
136
|
+
{ "link": "https://example.com/admin/#/reset-password?token=...", "expiresAt": "...", "expiresIn": "1 hour" }
|
|
137
|
+
// Error 409 - INACTIVE, NO_SMTP (copy the link instead) or NO_ORIGIN (set the Site URL)
|
|
138
|
+
{ "error": "...", "code": "NO_SMTP" }</code></pre>
|
|
139
|
+
|
|
140
|
+
<h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/users/:id/password-reset</span></h3>
|
|
141
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> update permission.</p>
|
|
142
|
+
<p>Withdraw an open reset link, so it no longer works.</p>
|
|
143
|
+
<pre class="code-block"><code>// Response 200
|
|
144
|
+
{ "ok": true }</code></pre>
|
|
145
|
+
|
|
146
|
+
<h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/users/:id</span></h3>
|
|
147
|
+
<p class="auth-note">Requires Bearer token + <code>users</code> delete permission.</p>
|
|
148
|
+
<p>Delete a user. You cannot delete your own account or anyone as senior as you.</p>
|
|
149
|
+
<pre class="code-block"><code>// Response 200
|
|
182
150
|
{ "success": true }
|
|
183
|
-
|
|
184
151
|
// Error 403
|
|
185
152
|
{ "error": "You cannot delete your own account" }</code></pre>
|
|
186
153
|
|
|
@@ -69,27 +69,31 @@
|
|
|
69
69
|
<div class="col-12">
|
|
70
70
|
<div class="docs-body">
|
|
71
71
|
|
|
72
|
-
<p>Views work on every storage adapter - no MongoDB connection is needed
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
<p>Views work on every storage adapter - no MongoDB connection is needed: a view over a MongoDB collection runs a
|
|
73
|
+
native aggregation, and one over a file collection runs the built-in pipeline evaluator. View configs are JSON
|
|
74
|
+
files in <code>content/views/<slug>.json</code>. The admin endpoints need the <code>views</code> permission
|
|
75
|
+
for the action named (read, create, update, delete).</p>
|
|
76
|
+
|
|
77
|
+
<h3 style="margin-top:16px;font-size:15px;text-transform:uppercase;letter-spacing:.5px;opacity:.6">
|
|
78
|
+
Admin Endpoints</h3>
|
|
79
|
+
|
|
80
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views</span></h3>
|
|
81
|
+
<p class="auth-note">Requires: <code>views</code> read permission</p>
|
|
82
|
+
<p>Every view you may see, newest first.</p>
|
|
83
|
+
|
|
84
|
+
<h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/views</span></h3>
|
|
85
|
+
<p class="auth-note">Requires: <code>views</code> create permission</p>
|
|
86
|
+
<p>Create a view. Answers <code>201</code>. Pipeline stages may be <code>$match</code>, <code>$lookup</code>,
|
|
87
|
+
<code>$sort</code>, <code>$project</code>, <code>$unwind</code>, <code>$addFields</code>, <code>$group</code>,
|
|
88
|
+
<code>$count</code>, <code>$skip</code> and <code>$limit</code>; <code>$out</code>, <code>$merge</code>,
|
|
89
|
+
<code>$function</code>, <code>$accumulator</code> and <code>$graphLookup</code> are refused. Field paths are
|
|
90
|
+
document paths such as <code>data.status</code> or <code>meta.createdAt</code>.</p>
|
|
91
|
+
<pre class="code-block"><code>{
|
|
92
|
+
"title": "Active Members",
|
|
93
|
+
"slug": "active-members",
|
|
90
94
|
"connection": "default",
|
|
91
95
|
"pipeline": {
|
|
92
|
-
"source": "
|
|
96
|
+
"source": "members",
|
|
93
97
|
"stages": [
|
|
94
98
|
{ "type": "$match", "config": { "data.status": "active" } },
|
|
95
99
|
{ "type": "$sort", "config": { "meta.createdAt": -1 } },
|
|
@@ -97,46 +101,56 @@
|
|
|
97
101
|
]
|
|
98
102
|
},
|
|
99
103
|
"display": { "mode": "table", "columns": [ { "key": "data.name", "label": "Name" } ], "pageSize": 25 },
|
|
100
|
-
"access": { "roles": ["admin", "
|
|
104
|
+
"access": { "roles": ["admin"], "public": false, "rowLevel": null }
|
|
101
105
|
}</code></pre>
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<p class="auth-note">Requires: <code>views</code> permission</p>
|
|
111
|
-
<p>Update a view config. Accepts the same body shape as POST; all fields are optional.</p>
|
|
112
|
-
|
|
113
|
-
<h3><span class="method-badge method-delete">DELETE</span><span
|
|
114
|
-
class="endpoint-path">/api/views/:slug</span></h3>
|
|
115
|
-
<p class="auth-note">Requires: <code>views</code> permission</p>
|
|
116
|
-
<p>Delete a view config.</p>
|
|
117
|
-
|
|
118
|
-
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views/:slug/execute</span>
|
|
119
|
-
</h3>
|
|
120
|
-
<p class="auth-note">Requires: <code>views</code> permission</p>
|
|
121
|
-
<p>Execute the view's aggregation pipeline and return paginated results. Query params:
|
|
122
|
-
<code>page</code> (default 1), <code>limit</code> (default 25).</p>
|
|
123
|
-
<pre class="code-block"><code>// Response
|
|
124
|
-
{ "results": [ ... ], "total": 142, "page": 1, "limit": 25 }</code></pre>
|
|
107
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views/:slug</span></h3>
|
|
108
|
+
<p class="auth-note">Requires: <code>views</code> read permission</p>
|
|
109
|
+
<p>One view config.</p>
|
|
110
|
+
|
|
111
|
+
<h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/views/:slug</span></h3>
|
|
112
|
+
<p class="auth-note">Requires: <code>views</code> update permission</p>
|
|
113
|
+
<p>Update a view. The same body as POST; every field is optional.</p>
|
|
125
114
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<p>List all view configs whose <code>pipeline.source</code> matches the given collection slug.</p>
|
|
115
|
+
<h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/views/:slug</span></h3>
|
|
116
|
+
<p class="auth-note">Requires: <code>views</code> delete permission</p>
|
|
117
|
+
<p>Delete a view.</p>
|
|
130
118
|
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views/:slug/execute</span></h3>
|
|
120
|
+
<p class="auth-note">Requires: <code>views</code> read permission</p>
|
|
121
|
+
<p>Run the view and return a page of results. Query params: <code>page</code> (default 1), <code>limit</code>
|
|
122
|
+
(default 25).</p>
|
|
123
|
+
<pre class="code-block"><code>// Response
|
|
124
|
+
{ "results": [ ... ], "total": 142, "page": 1, "limit": 25 }</code></pre>
|
|
133
125
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
<h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/views/_preview</span></h3>
|
|
127
|
+
<p class="auth-note">Requires: <code>views</code> update permission</p>
|
|
128
|
+
<p>Run an unsaved pipeline (the view editor's Results tab) and return up to 50 rows.</p>
|
|
129
|
+
<pre class="code-block"><code>// Request body
|
|
130
|
+
{ "source": "members", "connection": "default", "stages": [ ... ], "limit": 10 }</code></pre>
|
|
131
|
+
|
|
132
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views/collection/:slug</span></h3>
|
|
133
|
+
<p class="auth-note">Requires: <code>views</code> read permission</p>
|
|
134
|
+
<p>The views whose <code>pipeline.source</code> is the given collection.</p>
|
|
135
|
+
|
|
136
|
+
<h3 style="margin-top:24px;font-size:15px;text-transform:uppercase;letter-spacing:.5px;opacity:.6">
|
|
137
|
+
Public Endpoint</h3>
|
|
138
|
+
|
|
139
|
+
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/views/:slug/public</span></h3>
|
|
140
|
+
<p class="auth-note">Access level: per view <code>access</code> config</p>
|
|
141
|
+
<p>Run the view for a visitor. This is the same decision <code>[view]</code> on a page makes:</p>
|
|
142
|
+
<ul>
|
|
143
|
+
<li><code>access.public: true</code> - anyone.</li>
|
|
144
|
+
<li>Otherwise a signed-in user holding any role in <code>access.roles</code>, or a more senior one (the page
|
|
145
|
+
visibility ladder; <code>=role</code> means that role exactly). A role name the site does not have admits
|
|
146
|
+
only the level-0 role.</li>
|
|
147
|
+
<li>No roles listed - the admin tier (role levels 0 and 1).</li>
|
|
148
|
+
</ul>
|
|
149
|
+
<p>A row-level rule (<code>access.rowLevel</code>: <code>owner</code>, <code>field</code> or <code>reference</code>)
|
|
150
|
+
is applied as the viewer, so each person sees only their rows and the totals count only those; an anonymous
|
|
151
|
+
visitor gets no rows. Refused: 401 when not signed in, 403 otherwise.</p>
|
|
152
|
+
<pre class="code-block"><code>// Response
|
|
153
|
+
{ "results": [ ... ], "total": 12, "page": 1, "limit": 25 }</code></pre>
|
|
140
154
|
|
|
141
155
|
</div>
|
|
142
156
|
</div>
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
<h2>Create a component</h2>
|
|
25
25
|
<ol>
|
|
26
|
-
<li>Open <a href="#/components">Data
|
|
26
|
+
<li>Open <a href="#/components">Data > Components</a> and click <strong>New component</strong> in the
|
|
27
|
+
banner.</li>
|
|
27
28
|
<li>Name it in lowercase-with-hyphens (e.g. <code>price-tag</code>). The name is permanent and
|
|
28
29
|
becomes the tag <code><dm-price-tag></code>.</li>
|
|
29
30
|
<li>Fill the <code><template></code>, <code><props></code> and <code><script></code>
|
|
30
|
-
tabs
|
|
31
|
+
tabs, and <code><style></code> if you want one (the live preview updates as you type).</li>
|
|
31
32
|
<li><strong>Save</strong>. It compiles first - fix any reported errors and it lands on disk.</li>
|
|
32
33
|
</ol>
|
|
33
34
|
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
|
|
36
37
|
<h2>Render a value</h2>
|
|
37
38
|
<p>Interpolate a prop or state value with double braces:</p>
|
|
38
|
-
<pre class="code-block"><code><template><span class="dm-price">
|
|
39
|
+
<pre class="code-block"><code><template><span class="dm-price">{{currency}}{{amount}}</span></template>
|
|
39
40
|
<props>
|
|
40
41
|
{
|
|
41
42
|
"currency": { "type": "string", "default": "£" },
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
template re-renders automatically.</p>
|
|
54
55
|
<pre class="code-block"><code><template>
|
|
55
56
|
<div class="dm-counter">
|
|
56
|
-
<strong>
|
|
57
|
+
<strong>{{count}}</strong>
|
|
57
58
|
<button data-action="inc">+</button>
|
|
58
59
|
</div>
|
|
59
60
|
</template>
|
|
@@ -83,15 +84,15 @@ onMount() { this.set({ count: this.props.initial }); }</code></pre>
|
|
|
83
84
|
<hr>
|
|
84
85
|
|
|
85
86
|
<h2>Show something conditionally</h2>
|
|
86
|
-
<pre class="code-block"><code
|
|
87
|
-
|
|
87
|
+
<pre class="code-block"><code>{{#if showReset}}<button data-action="reset">Reset</button>{{/if}}
|
|
88
|
+
{{#unless soldOut}}<button>Buy</button>{{/unless}}</code></pre>
|
|
88
89
|
|
|
89
90
|
<hr>
|
|
90
91
|
|
|
91
92
|
<h2>Loop over a list</h2>
|
|
92
|
-
<p>Pass an array prop (attribute value is JSON) and iterate with <code
|
|
93
|
+
<p>Pass an array prop (attribute value is JSON) and iterate with <code>{{#each}}</code>:</p>
|
|
93
94
|
<pre class="code-block"><code><template>
|
|
94
|
-
<ul>
|
|
95
|
+
<ul>{{#each items}}<li>{{@index}}. {{.}}</li>{{/each}}</ul>
|
|
95
96
|
</template>
|
|
96
97
|
<props>{ "items": { "type": "array", "default": [] } }</props>
|
|
97
98
|
<script>export default {};</script></code></pre>
|
|
@@ -109,7 +110,8 @@ onMount() { this.set({ count: this.props.initial }); }</code></pre>
|
|
|
109
110
|
|
|
110
111
|
<h2>Fetch data from the API</h2>
|
|
111
112
|
<p>Domma's HTTP helper <code>H</code> is available inside the script. Fetch in <code>onMount</code> and
|
|
112
|
-
push results into state
|
|
113
|
+
push results into state. <code>/api/v1/<slug></code> answers only if the collection's API read
|
|
114
|
+
access allows the visitor (collection editor, <strong>API & Export</strong> tab):</p>
|
|
113
115
|
<pre class="code-block"><code>export default {
|
|
114
116
|
data() { return { entries: [] }; },
|
|
115
117
|
async onMount() {
|
|
@@ -126,7 +128,7 @@ onMount() { this.set({ count: this.props.initial }); }</code></pre>
|
|
|
126
128
|
<pre class="code-block"><code>export default {
|
|
127
129
|
data() { return { now: '' }; },
|
|
128
130
|
_timer: null,
|
|
129
|
-
methods: { tick() { this.set({ now: new Date().
|
|
131
|
+
methods: { tick() { this.set({ now: new Date().toTimeString().slice(0, 8) }); } }, // HH:mm:ss
|
|
130
132
|
onMount() { this.tick(); this._timer = setInterval(() => this.tick(), 1000); },
|
|
131
133
|
onUnmount() { if (this._timer) clearInterval(this._timer); }
|
|
132
134
|
};</code></pre>
|
|
@@ -145,24 +147,25 @@ onMount() { this.set({ count: this.props.initial }); }</code></pre>
|
|
|
145
147
|
<h2>Use it on a page</h2>
|
|
146
148
|
<p>Either form works and auto-loads the component's module:</p>
|
|
147
149
|
<pre class="code-block"><code>[component name="counter" step="2" /]
|
|
148
|
-
|
|
149
150
|
<dm-counter step="2"></dm-counter></code></pre>
|
|
150
151
|
|
|
151
152
|
<hr>
|
|
152
153
|
|
|
153
154
|
<h2>Move a component between sites</h2>
|
|
154
155
|
<ol>
|
|
155
|
-
<li>On <a href="#/components">Components</a>, click
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
<li>On <a href="#/components">Components</a>, right-click the row (or use its menu) and choose
|
|
157
|
+
<span data-icon="download"></span> <strong>Export .dmcomponent.json</strong> - you'll download
|
|
158
|
+
<code>name.dmcomponent.json</code>. The editor has the same Export in its banner.</li>
|
|
159
|
+
<li>On the other site, click <strong>Import</strong> in the Components banner and pick that file. On a
|
|
160
|
+
name clash you'll be asked to confirm an overwrite.</li>
|
|
159
161
|
</ol>
|
|
160
162
|
|
|
161
163
|
<hr>
|
|
162
164
|
|
|
163
165
|
<h2>File it under a project</h2>
|
|
164
|
-
<p>In the editor, set the <strong>Project</strong> dropdown. That writes <code>
|
|
165
|
-
|
|
166
|
+
<p>In the editor, set the <strong>Project</strong> dropdown. That writes <code>project</code> to the
|
|
167
|
+
component's <code>.meta.json</code> sidecar, so it shows in that project's Components list
|
|
168
|
+
(<code>#/projects/<slug>/components</code>).</p>
|
|
166
169
|
|
|
167
170
|
<hr>
|
|
168
171
|
<p class="text-muted" style="font-size:.9rem;">
|
|
@@ -38,19 +38,16 @@
|
|
|
38
38
|
Block order does not matter.</p>
|
|
39
39
|
|
|
40
40
|
<pre class="code-block"><code><template>
|
|
41
|
-
<div class="dm-greeting">Hello,
|
|
41
|
+
<div class="dm-greeting">Hello, {{name}}!</div>
|
|
42
42
|
</template>
|
|
43
|
-
|
|
44
43
|
<props>
|
|
45
44
|
{
|
|
46
45
|
"name": { "type": "string", "default": "world", "label": "Name to greet" }
|
|
47
46
|
}
|
|
48
47
|
</props>
|
|
49
|
-
|
|
50
48
|
<script>
|
|
51
49
|
export default {};
|
|
52
50
|
</script>
|
|
53
|
-
|
|
54
51
|
<style>
|
|
55
52
|
.dm-greeting { font-weight: 600; }
|
|
56
53
|
</style></code></pre>
|
|
@@ -71,22 +68,22 @@ export default {};
|
|
|
71
68
|
<tr><th>Syntax</th><th>Purpose</th></tr>
|
|
72
69
|
</thead>
|
|
73
70
|
<tbody>
|
|
74
|
-
<tr><td><code
|
|
75
|
-
<tr><td><code
|
|
76
|
-
<tr><td><code
|
|
77
|
-
<tr><td><code
|
|
78
|
-
<tr><td><code
|
|
71
|
+
<tr><td><code>{{ value }}</code></td><td>Interpolate a prop or state value (HTML-escaped).</td></tr>
|
|
72
|
+
<tr><td><code>{{#if cond}}…{{/if}}</code></td><td>Render the block only when <code>cond</code> is truthy.</td></tr>
|
|
73
|
+
<tr><td><code>{{#unless cond}}…{{/unless}}</code></td><td>Render the block only when <code>cond</code> is falsy.</td></tr>
|
|
74
|
+
<tr><td><code>{{#each list}}…{{/each}}</code></td><td>Repeat the block for each item. Inside, <code>{{.}}</code> is the item (for primitives), <code>{{@index}}</code>, <code>{{@first}}</code> and <code>{{@last}}</code> are available; object items expose their own keys.</td></tr>
|
|
75
|
+
<tr><td><code>{{#with obj}}…{{/with}}</code></td><td>Scope the block to an object's keys.</td></tr>
|
|
79
76
|
<tr><td><code><slot></slot></code></td><td>Project the light-DOM content placed between the element's tags.</td></tr>
|
|
80
77
|
</tbody>
|
|
81
78
|
</table>
|
|
82
79
|
|
|
83
80
|
<pre class="code-block"><code><template>
|
|
84
81
|
<ul class="dm-list">
|
|
85
|
-
|
|
86
|
-
<li class="
|
|
87
|
-
|
|
82
|
+
{{#each items}}
|
|
83
|
+
<li class="{{#if @first}}first{{/if}}">{{@index}}. {{.}}</li>
|
|
84
|
+
{{/each}}
|
|
88
85
|
</ul>
|
|
89
|
-
|
|
86
|
+
{{#if title}}<h4>{{title}}</h4>{{/if}}
|
|
90
87
|
<slot></slot>
|
|
91
88
|
</template></code></pre>
|
|
92
89
|
|
|
@@ -122,12 +119,10 @@ export default {};
|
|
|
122
119
|
<pre class="code-block"><code>export default {
|
|
123
120
|
// Reactive state. Called WITHOUT `this` - do not read props here.
|
|
124
121
|
data() { return { count: 0 }; },
|
|
125
|
-
|
|
126
122
|
// Methods are bound to the component context (`this`).
|
|
127
123
|
methods: {
|
|
128
124
|
inc() { this.set({ count: this.data.count + this.props.step }); }
|
|
129
125
|
},
|
|
130
|
-
|
|
131
126
|
// Lifecycle - bound to `this`; props and the element are available here.
|
|
132
127
|
onMount() { this.set({ count: this.props.initial }); },
|
|
133
128
|
onUnmount() { /* clean up timers, listeners, etc. */ }
|
|
@@ -215,6 +210,7 @@ export default {};
|
|
|
215
210
|
</thead>
|
|
216
211
|
<tbody>
|
|
217
212
|
<tr><td><code>GET /api/components</code></td><td>read</td><td>List components with metadata + parsed props.</td></tr>
|
|
213
|
+
<tr><td><code>GET /api/components/_count</code></td><td>read</td><td>The count shown on the sidebar badge.</td></tr>
|
|
218
214
|
<tr><td><code>GET /api/components/:name</code></td><td>read</td><td>Raw <code>.dmc</code> source + parsed props.</td></tr>
|
|
219
215
|
<tr><td><code>GET /api/components/:name.js</code></td><td>public</td><td>Compiled browser module (what pages load).</td></tr>
|
|
220
216
|
<tr><td><code>POST /api/components/compile</code></td><td>update</td><td>Transient compile for the live preview (nothing written).</td></tr>
|
|
@@ -231,7 +227,8 @@ export default {};
|
|
|
231
227
|
|
|
232
228
|
<h3>Bundled & plugin components</h3>
|
|
233
229
|
<p>The <code>bundled</code> flag marks a component as part of fresh-install seed data. Plugins may
|
|
234
|
-
also contribute components
|
|
230
|
+
also contribute components with <code>options.hooks.registerComponent({name, source})</code> in their
|
|
231
|
+
<code>plugin.js</code> - these appear in the list
|
|
235
232
|
with a <span class="badge badge-secondary">plugin</span> badge, are served from memory, and are
|
|
236
233
|
<strong>read-only</strong> from the admin (you can't edit or delete them, and you can't save a
|
|
237
234
|
component whose name a plugin already owns).</p>
|
|
@@ -77,9 +77,10 @@
|
|
|
77
77
|
|
|
78
78
|
<div class="alert alert-warning">
|
|
79
79
|
<strong>2. Boolean coercion is string-based.</strong> Attribute values are strings. A boolean prop
|
|
80
|
-
is
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
is false only for the string <code>"false"</code> (or when the attribute is left out and the default is
|
|
81
|
+
false). A bare attribute (<code>show-reset</code>), <code>"true"</code> and any other value, including
|
|
82
|
+
<code>"no"</code> and <code>"0"</code>, coerce to <code>true</code>. To switch a boolean off, write
|
|
83
|
+
<code>"false"</code> or omit the attribute.
|
|
83
84
|
</div>
|
|
84
85
|
|
|
85
86
|
<div class="alert alert-warning">
|
|
@@ -109,8 +110,8 @@
|
|
|
109
110
|
|
|
110
111
|
<h2>Plugin-owned components are read-only</h2>
|
|
111
112
|
<ul>
|
|
112
|
-
<li>Components contributed by a plugin
|
|
113
|
-
badge, are served from memory, and <strong>cannot be edited or deleted</strong> from the admin
|
|
113
|
+
<li>Components contributed by a plugin (through <code>options.hooks.registerComponent({name, source})</code>
|
|
114
|
+
in its <code>plugin.js</code>) show a <span class="badge badge-secondary">plugin</span> badge, are served from memory, and <strong>cannot be edited or deleted</strong> from the admin
|
|
114
115
|
(attempting it returns <code>PLUGIN_OWNED</code>).</li>
|
|
115
116
|
<li>You also can't save a disk component whose name a plugin already owns - pick a different name.</li>
|
|
116
117
|
</ul>
|
|
@@ -130,7 +131,7 @@
|
|
|
130
131
|
|
|
131
132
|
<h2>Rendering & safety notes</h2>
|
|
132
133
|
<ul>
|
|
133
|
-
<li><code
|
|
134
|
+
<li><code>{{ }}</code> interpolation is HTML-escaped, so prop values can't inject markup.</li>
|
|
134
135
|
<li>The markdown sanitiser keeps a live allowlist of <code>dm-*</code> tags, refreshed whenever you
|
|
135
136
|
save or delete a component - a component you just created is immediately usable in page content.
|
|
136
137
|
<code>on*</code> handler attributes and <code>javascript:</code> URLs are still stripped globally.</li>
|