domma-cms 0.25.14 → 0.25.16
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/admin/js/app.js +2 -2
- package/admin/js/templates/api-reference.html +68 -1233
- package/admin/js/templates/docs/api-actions.html +146 -0
- package/admin/js/templates/docs/api-authentication.html +202 -0
- package/admin/js/templates/docs/api-collections.html +347 -0
- package/admin/js/templates/docs/api-layouts.html +123 -0
- package/admin/js/templates/docs/api-media.html +158 -0
- package/admin/js/templates/docs/api-navigation.html +99 -0
- package/admin/js/templates/docs/api-pages.html +214 -0
- package/admin/js/templates/docs/api-plugins.html +136 -0
- package/admin/js/templates/docs/api-settings.html +148 -0
- package/admin/js/templates/docs/api-users.html +189 -0
- package/admin/js/templates/docs/api-views.html +142 -0
- package/admin/js/templates/docs/components-howto.html +175 -0
- package/admin/js/templates/docs/components-reference.html +259 -0
- package/admin/js/templates/docs/components-rules.html +167 -0
- package/admin/js/templates/docs/components-walkthrough.html +167 -0
- package/admin/js/templates/docs/tutorial-crud.html +344 -0
- package/admin/js/templates/docs/tutorial-forms.html +93 -0
- package/admin/js/templates/docs/tutorial-plugin.html +234 -0
- package/admin/js/templates/docs/usage-actions.html +122 -0
- package/admin/js/templates/docs/usage-cta-shortcode.html +124 -0
- package/admin/js/templates/docs/usage-dconfig.html +148 -0
- package/admin/js/templates/docs/usage-media.html +25 -0
- package/admin/js/templates/docs/usage-navigation.html +31 -0
- package/admin/js/templates/docs/usage-pages.html +68 -0
- package/admin/js/templates/docs/usage-plugins.html +36 -0
- package/admin/js/templates/docs/usage-shortcodes.html +810 -0
- package/admin/js/templates/docs/usage-site-settings.html +52 -0
- package/admin/js/templates/docs/usage-users-roles.html +84 -0
- package/admin/js/templates/docs/usage-views.html +105 -0
- package/admin/js/templates/documentation.html +60 -1522
- package/admin/js/templates/tutorials.html +46 -659
- package/admin/js/views/api-reference.js +1 -1
- package/admin/js/views/doc-pages.js +1 -0
- package/admin/js/views/documentation.js +1 -1
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/tutorials.js +1 -1
- package/config/menus/admin-sidebar.json +76 -6
- package/package.json +1 -1
- package/public/js/site.js +1 -1
- package/server/server.js +3 -1
- package/server/services/menus.js +18 -0
- package/server/services/renderer.js +2 -0
- package/server/services/sidebar-migration.js +145 -3
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="document"></span> Writing a Plugin</h1>
|
|
3
|
+
<a href="#/tutorials" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All tutorials</a>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<div class="docs-body">
|
|
9
|
+
|
|
10
|
+
<p>Plugins live in the <code>plugins/</code> directory. Each plugin is a self-contained folder with
|
|
11
|
+
three required files and optional <code>admin/</code> and <code>public/</code> subdirectories.</p>
|
|
12
|
+
|
|
13
|
+
<h3>Directory structure</h3>
|
|
14
|
+
<pre class="code-block"><code>plugins/
|
|
15
|
+
my-plugin/
|
|
16
|
+
plugin.json ← manifest (required)
|
|
17
|
+
plugin.js ← Fastify plugin (required)
|
|
18
|
+
config.js ← settings defaults (required)
|
|
19
|
+
admin/
|
|
20
|
+
views/
|
|
21
|
+
my-view.js ← admin SPA view (optional)
|
|
22
|
+
templates/
|
|
23
|
+
my-view.html ← view template (optional)
|
|
24
|
+
public/
|
|
25
|
+
inject-head.html ← injected into <head> on every page (optional)
|
|
26
|
+
inject-body.html ← injected before </body> on every page (optional)
|
|
27
|
+
data/ ← plugin data store (optional, not publicly served)</code></pre>
|
|
28
|
+
|
|
29
|
+
<hr>
|
|
30
|
+
|
|
31
|
+
<h3>1. plugin.json — the manifest</h3>
|
|
32
|
+
<p>All fields below are <strong>required</strong>. Missing any will cause the plugin to be skipped on
|
|
33
|
+
startup with a warning in the server log.</p>
|
|
34
|
+
<pre class="code-block"><code>{
|
|
35
|
+
"name": "my-plugin",
|
|
36
|
+
"displayName": "My Plugin",
|
|
37
|
+
"version": "1.0.0",
|
|
38
|
+
"description": "A short description shown on the Plugins page.",
|
|
39
|
+
"author": "Your Name",
|
|
40
|
+
"date": "2026-03-01",
|
|
41
|
+
"icon": "star"
|
|
42
|
+
}</code></pre>
|
|
43
|
+
|
|
44
|
+
<p>Optional fields:</p>
|
|
45
|
+
<table class="table table-sm">
|
|
46
|
+
<thead>
|
|
47
|
+
<tr>
|
|
48
|
+
<th>Field</th>
|
|
49
|
+
<th>Type</th>
|
|
50
|
+
<th>Description</th>
|
|
51
|
+
</tr>
|
|
52
|
+
</thead>
|
|
53
|
+
<tbody>
|
|
54
|
+
<tr>
|
|
55
|
+
<td><code>inject.head</code></td>
|
|
56
|
+
<td>string</td>
|
|
57
|
+
<td>Path (relative to plugin root) to an HTML snippet injected into <code><head></code>.</td>
|
|
58
|
+
</tr>
|
|
59
|
+
<tr>
|
|
60
|
+
<td><code>inject.bodyEnd</code></td>
|
|
61
|
+
<td>string</td>
|
|
62
|
+
<td>Path to an HTML snippet injected before <code></body></code>.</td>
|
|
63
|
+
</tr>
|
|
64
|
+
<tr>
|
|
65
|
+
<td><code>admin.sidebar</code></td>
|
|
66
|
+
<td>array</td>
|
|
67
|
+
<td>Sidebar items to add to the admin panel.</td>
|
|
68
|
+
</tr>
|
|
69
|
+
<tr>
|
|
70
|
+
<td><code>admin.routes</code></td>
|
|
71
|
+
<td>array</td>
|
|
72
|
+
<td>SPA routes to register in the admin router.</td>
|
|
73
|
+
</tr>
|
|
74
|
+
<tr>
|
|
75
|
+
<td><code>admin.views</code></td>
|
|
76
|
+
<td>object</td>
|
|
77
|
+
<td>View modules to dynamically import into the admin SPA.</td>
|
|
78
|
+
</tr>
|
|
79
|
+
</tbody>
|
|
80
|
+
</table>
|
|
81
|
+
|
|
82
|
+
<hr>
|
|
83
|
+
|
|
84
|
+
<h3>2. plugin.js — the Fastify plugin</h3>
|
|
85
|
+
<p>This is the server-side entry point. It must export a default <strong>async function</strong> that
|
|
86
|
+
Fastify will call with <code>(fastify, options)</code>.</p>
|
|
87
|
+
<p>The CMS injects auth middleware through <code>options.auth</code> — always destructure from there
|
|
88
|
+
rather than importing directly.</p>
|
|
89
|
+
|
|
90
|
+
<pre class="code-block"><code>import { getPluginSettings, savePluginState } from '../../server/services/plugins.js';
|
|
91
|
+
|
|
92
|
+
export default async function myPlugin(fastify, options) {
|
|
93
|
+
const { authenticate, requireAdmin } = options.auth;
|
|
94
|
+
|
|
95
|
+
// Public endpoint — no auth needed
|
|
96
|
+
fastify.get('/hello', async () => {
|
|
97
|
+
return { message: 'Hello from my plugin!' };
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Admin-only endpoint
|
|
101
|
+
fastify.get('/settings', { preHandler: [authenticate, requireAdmin] }, async () => {
|
|
102
|
+
return getPluginSettings('my-plugin');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
fastify.put('/settings', { preHandler: [authenticate, requireAdmin] }, async (request) => {
|
|
106
|
+
savePluginState('my-plugin', { settings: request.body });
|
|
107
|
+
return { ok: true };
|
|
108
|
+
});
|
|
109
|
+
}</code></pre>
|
|
110
|
+
|
|
111
|
+
<p>Routes are registered under the prefix <code>/api/plugins/{name}</code> automatically. You do not
|
|
112
|
+
set the prefix yourself — it is always locked to your plugin's directory name.</p>
|
|
113
|
+
|
|
114
|
+
<hr>
|
|
115
|
+
|
|
116
|
+
<h3>3. config.js — settings defaults</h3>
|
|
117
|
+
<p>Export a plain object of default settings. These are merged with any user overrides stored in
|
|
118
|
+
<code>config/plugins.json</code> when <code>getPluginSettings()</code> is called.</p>
|
|
119
|
+
|
|
120
|
+
<pre class="code-block"><code>export default {
|
|
121
|
+
greeting: 'Hello, world!',
|
|
122
|
+
enableFeature: true,
|
|
123
|
+
maxItems: 10
|
|
124
|
+
};</code></pre>
|
|
125
|
+
|
|
126
|
+
<p><code>config.js</code> is only loaded for <strong>enabled</strong> plugins. Side-effect code here
|
|
127
|
+
will not run for disabled plugins.</p>
|
|
128
|
+
|
|
129
|
+
<hr>
|
|
130
|
+
|
|
131
|
+
<h3>4. Admin views (optional)</h3>
|
|
132
|
+
<p>To add a page to the admin panel, declare the route and view in <code>plugin.json</code>:</p>
|
|
133
|
+
|
|
134
|
+
<pre class="code-block"><code>"admin": {
|
|
135
|
+
"sidebar": [
|
|
136
|
+
{
|
|
137
|
+
"id": "my-plugin",
|
|
138
|
+
"text": "My Plugin",
|
|
139
|
+
"icon": "star",
|
|
140
|
+
"url": "#/plugins/my-plugin",
|
|
141
|
+
"section": "#/plugins/my-plugin"
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"routes": [
|
|
145
|
+
{
|
|
146
|
+
"path": "/plugins/my-plugin",
|
|
147
|
+
"view": "plugin-my-plugin",
|
|
148
|
+
"title": "My Plugin - Domma CMS"
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"views": {
|
|
152
|
+
"plugin-my-plugin": {
|
|
153
|
+
"entry": "my-plugin/admin/views/my-view.js",
|
|
154
|
+
"exportName": "myPluginView"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}</code></pre>
|
|
158
|
+
|
|
159
|
+
<p>The view file follows the standard Domma view pattern — a <code>templateUrl</code> and an
|
|
160
|
+
<code>onMount($container)</code> function:</p>
|
|
161
|
+
|
|
162
|
+
<pre class="code-block"><code>// admin/views/my-view.js
|
|
163
|
+
export const myPluginView = {
|
|
164
|
+
templateUrl: '/plugins/my-plugin/admin/templates/my-view.html',
|
|
165
|
+
|
|
166
|
+
async onMount($container) {
|
|
167
|
+
const res = await fetch('/api/plugins/my-plugin/settings', {
|
|
168
|
+
headers: { 'Authorization': 'Bearer ' + (S.get('auth_token') || '') }
|
|
169
|
+
});
|
|
170
|
+
const settings = await res.json();
|
|
171
|
+
|
|
172
|
+
$container.find('#greeting').text(settings.greeting);
|
|
173
|
+
Domma.icons.scan();
|
|
174
|
+
}
|
|
175
|
+
};</code></pre>
|
|
176
|
+
|
|
177
|
+
<p>The template is a plain HTML fragment (no <code><html></code> wrapper). Use the same card and
|
|
178
|
+
form patterns as the rest of the admin panel:</p>
|
|
179
|
+
|
|
180
|
+
<pre class="code-block"><code><!-- admin/templates/my-view.html -->
|
|
181
|
+
<div class="view-header">
|
|
182
|
+
<h1><span data-icon="star"></span> My Plugin</h1>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="card">
|
|
186
|
+
<div class="card-body">
|
|
187
|
+
<p id="greeting">Loading…</p>
|
|
188
|
+
</div>
|
|
189
|
+
</div></code></pre>
|
|
190
|
+
|
|
191
|
+
<hr>
|
|
192
|
+
|
|
193
|
+
<h3>5. Injection snippets (optional)</h3>
|
|
194
|
+
<p>HTML snippets declared in <code>inject.head</code> and <code>inject.bodyEnd</code> are read from
|
|
195
|
+
the plugin's <code>public/</code> directory and inserted into every public page. Use this for
|
|
196
|
+
analytics scripts, stylesheets, or widgets.</p>
|
|
197
|
+
|
|
198
|
+
<pre class="code-block"><code><!-- public/inject-body.html -->
|
|
199
|
+
<script>
|
|
200
|
+
(function () {
|
|
201
|
+
// This runs on every public page
|
|
202
|
+
fetch('/api/plugins/my-plugin/hello')
|
|
203
|
+
.then(r => r.json())
|
|
204
|
+
.then(d => console.log(d.message));
|
|
205
|
+
})();
|
|
206
|
+
</script></code></pre>
|
|
207
|
+
|
|
208
|
+
<p>Snippet paths are validated — they must stay within the plugin's own directory. Paths containing
|
|
209
|
+
<code>..</code> are blocked.</p>
|
|
210
|
+
|
|
211
|
+
<hr>
|
|
212
|
+
|
|
213
|
+
<h3>6. Registering and testing</h3>
|
|
214
|
+
<ol>
|
|
215
|
+
<li>Create the <code>plugins/my-plugin/</code> directory with all three required files.</li>
|
|
216
|
+
<li>Restart the server — you should see <code>[plugins] Loaded N plugins: …, my-plugin</code> in
|
|
217
|
+
the log.
|
|
218
|
+
</li>
|
|
219
|
+
<li>Go to the <a href="#/plugins">Plugins page</a> and enable your plugin.</li>
|
|
220
|
+
<li>Restart the server again to register the routes.</li>
|
|
221
|
+
<li>Verify your endpoint: <code>GET /api/plugins/my-plugin/hello</code></li>
|
|
222
|
+
</ol>
|
|
223
|
+
|
|
224
|
+
<p class="text-muted" style="font-size:.9rem">Tip: use <code>npm run dev</code> during development —
|
|
225
|
+
the server restarts automatically on file changes.</p>
|
|
226
|
+
|
|
227
|
+
<hr>
|
|
228
|
+
<p class="text-muted" style="font-size:.9rem;">
|
|
229
|
+
Next: <a href="#/tutorials/forms">Form Follow-Up →</a>
|
|
230
|
+
</p>
|
|
231
|
+
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="book"></span> Actions</h1>
|
|
3
|
+
<a href="#/documentation" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All usage topics</a>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<div class="docs-body">
|
|
9
|
+
|
|
10
|
+
<p>Actions are admin-designed sequential workflow operations triggered against individual collection
|
|
11
|
+
entries. When an action is assigned to a collection, a trigger button appears per row in the
|
|
12
|
+
entry list. Actions require a MongoDB connection (pro mode).</p>
|
|
13
|
+
|
|
14
|
+
<h3>Creating an Action</h3>
|
|
15
|
+
<ol>
|
|
16
|
+
<li>Navigate to <strong>Data → Actions</strong> and click <strong>New Action</strong>.</li>
|
|
17
|
+
<li><strong>General tab</strong> — enter a title and select the target collection.</li>
|
|
18
|
+
<li><strong>Trigger tab</strong> — configure the button label, icon, and optional confirmation
|
|
19
|
+
message.
|
|
20
|
+
</li>
|
|
21
|
+
<li><strong>Steps tab</strong> — add steps in order. Each step runs sequentially; if one fails the
|
|
22
|
+
action stops.
|
|
23
|
+
</li>
|
|
24
|
+
<li><strong>Access tab</strong> — select which roles can trigger this action.</li>
|
|
25
|
+
<li>Click <strong>Save Action</strong>. The button will appear on the collection's entry list.
|
|
26
|
+
</li>
|
|
27
|
+
</ol>
|
|
28
|
+
|
|
29
|
+
<h3>Phase 1 Step Types</h3>
|
|
30
|
+
<table class="table table-sm">
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th>Step</th>
|
|
34
|
+
<th>What it does</th>
|
|
35
|
+
<th>Config fields</th>
|
|
36
|
+
</tr>
|
|
37
|
+
</thead>
|
|
38
|
+
<tbody>
|
|
39
|
+
<tr>
|
|
40
|
+
<td><code>updateField</code></td>
|
|
41
|
+
<td>Set a field on the entry to a new value</td>
|
|
42
|
+
<td><code>field</code>, <code>value</code></td>
|
|
43
|
+
</tr>
|
|
44
|
+
<tr>
|
|
45
|
+
<td><code>deleteEntry</code></td>
|
|
46
|
+
<td>Permanently delete the entry</td>
|
|
47
|
+
<td>None</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<td><code>moveToCollection</code></td>
|
|
51
|
+
<td>Create the entry in a target collection then delete it from the source</td>
|
|
52
|
+
<td><code>targetCollection</code></td>
|
|
53
|
+
</tr>
|
|
54
|
+
<tr>
|
|
55
|
+
<td><code>webhook</code></td>
|
|
56
|
+
<td>HTTP request to an external URL</td>
|
|
57
|
+
<td><code>url</code>, <code>method</code>, <code>body</code> (JSON)</td>
|
|
58
|
+
</tr>
|
|
59
|
+
<tr>
|
|
60
|
+
<td><code>email</code></td>
|
|
61
|
+
<td>Send an email via the configured SMTP transport</td>
|
|
62
|
+
<td><code>to</code>, <code>subject</code>, <code>template</code></td>
|
|
63
|
+
</tr>
|
|
64
|
+
</tbody>
|
|
65
|
+
</table>
|
|
66
|
+
|
|
67
|
+
<h3>Template Variables</h3>
|
|
68
|
+
<p>Step config fields support <code>{{variable}}</code> interpolation:</p>
|
|
69
|
+
<table class="table table-sm">
|
|
70
|
+
<thead>
|
|
71
|
+
<tr>
|
|
72
|
+
<th>Variable</th>
|
|
73
|
+
<th>Resolves to</th>
|
|
74
|
+
</tr>
|
|
75
|
+
</thead>
|
|
76
|
+
<tbody>
|
|
77
|
+
<tr>
|
|
78
|
+
<td><code>{{entry.data.fieldName}}</code></td>
|
|
79
|
+
<td>A field value from the current entry</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td><code>{{entry.id}}</code></td>
|
|
83
|
+
<td>The entry's ID</td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td><code>{{now}}</code></td>
|
|
87
|
+
<td>Current timestamp (ISO 8601)</td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td><code>{{user.name}}</code></td>
|
|
91
|
+
<td>Name of the user who triggered the action</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td><code>{{user.email}}</code></td>
|
|
95
|
+
<td>Email of the triggering user</td>
|
|
96
|
+
</tr>
|
|
97
|
+
<tr>
|
|
98
|
+
<td><code>{{env.CMS_PUBLIC_*}}</code></td>
|
|
99
|
+
<td>Environment variables prefixed <code>CMS_PUBLIC_</code> only</td>
|
|
100
|
+
</tr>
|
|
101
|
+
</tbody>
|
|
102
|
+
</table>
|
|
103
|
+
<p><strong>Example</strong> — approve an application and notify by email:</p>
|
|
104
|
+
<pre class="code-block"><code>Step 1: updateField field=status value=approved
|
|
105
|
+
Step 2: updateField field=approvedAt value={{now}}
|
|
106
|
+
Step 3: email to={{entry.data.email}}
|
|
107
|
+
subject=Your application has been approved
|
|
108
|
+
template=Congratulations {{entry.data.name}}, your application is approved.</code></pre>
|
|
109
|
+
|
|
110
|
+
<h3>Partial Execution</h3>
|
|
111
|
+
<p>Actions are <strong>not transactional</strong>. If a step fails, the action stops and returns the
|
|
112
|
+
number of steps completed so far (<code>stepsCompleted</code>). Steps that already ran are
|
|
113
|
+
<strong>not rolled back</strong>. Design step order with this in mind — put irreversible steps
|
|
114
|
+
(delete, email) last.</p>
|
|
115
|
+
|
|
116
|
+
<h3>After a deleteEntry step</h3>
|
|
117
|
+
<p>If an action contains a <code>deleteEntry</code> step, subsequent steps will fail because the entry
|
|
118
|
+
no longer exists. Place <code>deleteEntry</code> as the last step.</p>
|
|
119
|
+
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="book"></span> CTA Shortcode</h1>
|
|
3
|
+
<a href="#/documentation" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All usage topics</a>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<div class="docs-body">
|
|
9
|
+
|
|
10
|
+
<p>The <code>[cta]</code> shortcode places an action-trigger button in any public page. Clicking it
|
|
11
|
+
calls
|
|
12
|
+
<code>POST /api/actions/:slug/public</code> with the entry ID, using the logged-in user's JWT.
|
|
13
|
+
If the user is not logged in, a warning toast is shown instead.</p>
|
|
14
|
+
|
|
15
|
+
<h3>Syntax & Attributes</h3>
|
|
16
|
+
<p><strong>Wrapping form:</strong></p>
|
|
17
|
+
<pre class="code-block"><code>[cta action="slug" entry="entry-id" icon="check" confirm="Are you sure?"]Button label[/cta]</code></pre>
|
|
18
|
+
<p><strong>Self-closing form:</strong></p>
|
|
19
|
+
<pre class="code-block"><code>[cta action="slug" entry="entry-id" label="Button label" /]</code></pre>
|
|
20
|
+
<table class="table table-sm" style="margin-top:1rem;">
|
|
21
|
+
<thead>
|
|
22
|
+
<tr>
|
|
23
|
+
<th>Attribute</th>
|
|
24
|
+
<th>Required</th>
|
|
25
|
+
<th>Default</th>
|
|
26
|
+
<th>Description</th>
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
<tr>
|
|
31
|
+
<td><code>action</code></td>
|
|
32
|
+
<td>Yes</td>
|
|
33
|
+
<td>—</td>
|
|
34
|
+
<td>Action slug</td>
|
|
35
|
+
</tr>
|
|
36
|
+
<tr>
|
|
37
|
+
<td><code>entry</code></td>
|
|
38
|
+
<td>Yes</td>
|
|
39
|
+
<td>—</td>
|
|
40
|
+
<td>Entry UUID to act on</td>
|
|
41
|
+
</tr>
|
|
42
|
+
<tr>
|
|
43
|
+
<td><code>label</code></td>
|
|
44
|
+
<td>No</td>
|
|
45
|
+
<td><code>"Run"</code></td>
|
|
46
|
+
<td>Button text (self-closing only)</td>
|
|
47
|
+
</tr>
|
|
48
|
+
<tr>
|
|
49
|
+
<td><code>style</code></td>
|
|
50
|
+
<td>No</td>
|
|
51
|
+
<td><code>"primary"</code></td>
|
|
52
|
+
<td>primary · secondary · ghost · danger</td>
|
|
53
|
+
</tr>
|
|
54
|
+
<tr>
|
|
55
|
+
<td><code>icon</code></td>
|
|
56
|
+
<td>No</td>
|
|
57
|
+
<td>—</td>
|
|
58
|
+
<td>Domma icon name</td>
|
|
59
|
+
</tr>
|
|
60
|
+
<tr>
|
|
61
|
+
<td><code>size</code></td>
|
|
62
|
+
<td>No</td>
|
|
63
|
+
<td>—</td>
|
|
64
|
+
<td>sm · md · lg</td>
|
|
65
|
+
</tr>
|
|
66
|
+
<tr>
|
|
67
|
+
<td><code>confirm</code></td>
|
|
68
|
+
<td>No</td>
|
|
69
|
+
<td>—</td>
|
|
70
|
+
<td>Confirmation prompt before executing</td>
|
|
71
|
+
</tr>
|
|
72
|
+
</tbody>
|
|
73
|
+
</table>
|
|
74
|
+
|
|
75
|
+
<h3>Collection Integration</h3>
|
|
76
|
+
<p>Add per-entry buttons to any <code>[collection]</code> shortcode:</p>
|
|
77
|
+
<pre class="code-block"><code>[collection slug="applications" display="cards" title-field="name"
|
|
78
|
+
cta="approve-application"
|
|
79
|
+
cta-label="Approve"
|
|
80
|
+
cta-icon="check"
|
|
81
|
+
cta-style="primary"
|
|
82
|
+
cta-confirm="Approve this application?" /]</code></pre>
|
|
83
|
+
<table class="table table-sm" style="margin-top:1rem;">
|
|
84
|
+
<thead>
|
|
85
|
+
<tr>
|
|
86
|
+
<th>Attribute</th>
|
|
87
|
+
<th>Default</th>
|
|
88
|
+
<th>Description</th>
|
|
89
|
+
</tr>
|
|
90
|
+
</thead>
|
|
91
|
+
<tbody>
|
|
92
|
+
<tr>
|
|
93
|
+
<td><code>cta</code></td>
|
|
94
|
+
<td>—</td>
|
|
95
|
+
<td>Action slug — enables per-entry buttons</td>
|
|
96
|
+
</tr>
|
|
97
|
+
<tr>
|
|
98
|
+
<td><code>cta-label</code></td>
|
|
99
|
+
<td><code>"Run"</code></td>
|
|
100
|
+
<td>Button label</td>
|
|
101
|
+
</tr>
|
|
102
|
+
<tr>
|
|
103
|
+
<td><code>cta-icon</code></td>
|
|
104
|
+
<td>—</td>
|
|
105
|
+
<td>Domma icon name</td>
|
|
106
|
+
</tr>
|
|
107
|
+
<tr>
|
|
108
|
+
<td><code>cta-style</code></td>
|
|
109
|
+
<td><code>"primary"</code></td>
|
|
110
|
+
<td>Button variant</td>
|
|
111
|
+
</tr>
|
|
112
|
+
<tr>
|
|
113
|
+
<td><code>cta-confirm</code></td>
|
|
114
|
+
<td>—</td>
|
|
115
|
+
<td>Confirmation prompt</td>
|
|
116
|
+
</tr>
|
|
117
|
+
</tbody>
|
|
118
|
+
</table>
|
|
119
|
+
<p>All three display modes support CTA buttons: <strong>cards</strong> (card footer),
|
|
120
|
+
<strong>list</strong> (inline), <strong>table</strong> (dedicated column).</p>
|
|
121
|
+
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="book"></span> DConfig</h1>
|
|
3
|
+
<a href="#/documentation" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All usage topics</a>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<div class="docs-body">
|
|
9
|
+
|
|
10
|
+
<p>DConfig lets you wire up declarative behaviour on any public page — no JavaScript required. Define
|
|
11
|
+
event
|
|
12
|
+
handlers and class toggles in JSON; the page runtime applies them automatically on load.</p>
|
|
13
|
+
|
|
14
|
+
<p>DConfig can be set in two ways, and both are merged at runtime (inline shortcode wins on selector
|
|
15
|
+
conflict):</p>
|
|
16
|
+
<ul>
|
|
17
|
+
<li><strong>Page editor</strong> — the <em>DConfig</em> collapsible section accepts a JSON blob
|
|
18
|
+
stored in page
|
|
19
|
+
frontmatter and injected as <code>window.__CMS_DCONFIG__</code>.
|
|
20
|
+
</li>
|
|
21
|
+
<li><strong>Inline shortcode</strong> — place one or more <code>[dconfig]…[/dconfig]</code> blocks
|
|
22
|
+
directly in
|
|
23
|
+
the page body Markdown.
|
|
24
|
+
</li>
|
|
25
|
+
</ul>
|
|
26
|
+
|
|
27
|
+
<h3>Schema</h3>
|
|
28
|
+
<pre class="code-block"><code>{
|
|
29
|
+
"#selector": {
|
|
30
|
+
"events": {
|
|
31
|
+
"eventName": {
|
|
32
|
+
"target": "#target-selector",
|
|
33
|
+
"action": "value"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}</code></pre>
|
|
38
|
+
|
|
39
|
+
<p>Multiple selectors and multiple event types can appear in a single config block.</p>
|
|
40
|
+
|
|
41
|
+
<h3>Supported events</h3>
|
|
42
|
+
<table class="table table-sm">
|
|
43
|
+
<thead>
|
|
44
|
+
<tr>
|
|
45
|
+
<th>Event</th>
|
|
46
|
+
<th>Description</th>
|
|
47
|
+
</tr>
|
|
48
|
+
</thead>
|
|
49
|
+
<tbody>
|
|
50
|
+
<tr>
|
|
51
|
+
<td><code>click</code></td>
|
|
52
|
+
<td>Fires when the selector element is clicked.</td>
|
|
53
|
+
</tr>
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
|
|
57
|
+
<h3>Supported actions</h3>
|
|
58
|
+
<table class="table table-sm">
|
|
59
|
+
<thead>
|
|
60
|
+
<tr>
|
|
61
|
+
<th>Action key</th>
|
|
62
|
+
<th>Value type</th>
|
|
63
|
+
<th>Description</th>
|
|
64
|
+
</tr>
|
|
65
|
+
</thead>
|
|
66
|
+
<tbody>
|
|
67
|
+
<tr>
|
|
68
|
+
<td><code>toggleClass</code></td>
|
|
69
|
+
<td><code>string</code></td>
|
|
70
|
+
<td>Adds the class if absent, removes it if present, on the <code>target</code> element.
|
|
71
|
+
Typical use:
|
|
72
|
+
show/hide content with a <code>hidden</code> class.
|
|
73
|
+
</td>
|
|
74
|
+
</tr>
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>
|
|
77
|
+
|
|
78
|
+
<p><em>Additional actions (addClass, removeClass, setAttribute, scroll-to) are planned for future
|
|
79
|
+
releases.</em></p>
|
|
80
|
+
|
|
81
|
+
<h3>Targeting shortcode elements with <code>id</code></h3>
|
|
82
|
+
<p>All shortcodes support an <code>id</code> attribute, which is written directly onto the generated
|
|
83
|
+
element.
|
|
84
|
+
Use this instead of raw HTML when you want DConfig to target a card, column, or grid:</p>
|
|
85
|
+
<pre class="code-block"><code>[card id="my-panel" title="Hidden Details"]
|
|
86
|
+
Content goes here.
|
|
87
|
+
[/card]
|
|
88
|
+
|
|
89
|
+
[dconfig]
|
|
90
|
+
{
|
|
91
|
+
"#my-btn": {
|
|
92
|
+
"events": {
|
|
93
|
+
"click": { "target": "#my-panel", "toggleClass": "hidden" }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
[/dconfig]</code></pre>
|
|
98
|
+
<p>Supported on: <code>[card]</code>, <code>[col]</code>, <code>[row]</code>, <code>[grid]</code>.</p>
|
|
99
|
+
|
|
100
|
+
<h3>Using the <code>[dconfig]</code> shortcode</h3>
|
|
101
|
+
<pre class="code-block"><code>[dconfig]
|
|
102
|
+
{
|
|
103
|
+
"#my-btn": {
|
|
104
|
+
"events": {
|
|
105
|
+
"click": { "target": "#my-panel", "toggleClass": "hidden" }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
[/dconfig]
|
|
110
|
+
|
|
111
|
+
<button id="my-btn" class="btn btn-primary">Toggle</button>
|
|
112
|
+
|
|
113
|
+
<div id="my-panel" class="card hidden">
|
|
114
|
+
<div class="card-body">Hidden content.</div>
|
|
115
|
+
</div></code></pre>
|
|
116
|
+
|
|
117
|
+
<h3>Multiple selectors</h3>
|
|
118
|
+
<p>Any number of selectors can appear in one block. Selectors are matched with
|
|
119
|
+
<code>document.querySelector</code> — use IDs, classes, or attribute selectors.</p>
|
|
120
|
+
<pre class="code-block"><code>[dconfig]
|
|
121
|
+
{
|
|
122
|
+
"#show-btn": {
|
|
123
|
+
"events": {
|
|
124
|
+
"click": { "target": "#panel", "toggleClass": "hidden" }
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"#hide-btn": {
|
|
128
|
+
"events": {
|
|
129
|
+
"click": { "target": "#panel", "toggleClass": "hidden" }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
[/dconfig]</code></pre>
|
|
134
|
+
|
|
135
|
+
<h3>Using the page editor DConfig section</h3>
|
|
136
|
+
<p>The same JSON (without the shortcode tags) can be pasted directly into the <em>DConfig</em> card in
|
|
137
|
+
the page
|
|
138
|
+
editor. This is useful when the config should apply to the whole page without appearing in the
|
|
139
|
+
body content.
|
|
140
|
+
Inline <code>[dconfig]</code> shortcodes are applied last and will win on any shared selector key.
|
|
141
|
+
</p>
|
|
142
|
+
|
|
143
|
+
<p>See live demos on the <a href="/resources/interactive" target="_blank">Interactive Resources
|
|
144
|
+
page</a>.</p>
|
|
145
|
+
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="book"></span> Media</h1>
|
|
3
|
+
<a href="#/documentation" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> All usage topics</a>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<div class="docs-body">
|
|
9
|
+
|
|
10
|
+
<p>Media files are stored in <code>content/media/</code> and served publicly at <code>/media/{filename}</code>.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<h3>Using media in pages</h3>
|
|
14
|
+
<p>Reference uploaded files by their public URL in Markdown:</p>
|
|
15
|
+
<pre class="code-block"><code>
|
|
16
|
+
|
|
17
|
+
<img src="/media/my-image.jpg" alt="Alt text"></code></pre>
|
|
18
|
+
|
|
19
|
+
<h3>Upload limits</h3>
|
|
20
|
+
<p>The maximum file size is configured in <code>config/server.json</code> under <code>uploads.maxFileSize</code>
|
|
21
|
+
(bytes). Default is 10 MB.</p>
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|