@freelygive/canvas-jsonapi 0.1.1-dev.bb316b62 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freelygive/canvas-jsonapi",
|
|
3
|
-
"version": "0.1.1
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CLI for managing content in Drupal Canvas / Acquia Source via JSON:API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,14 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"src",
|
|
11
|
-
"skills",
|
|
12
11
|
"README.md"
|
|
13
12
|
],
|
|
14
|
-
"npmScaffold": {
|
|
15
|
-
"file-mapping": {
|
|
16
|
-
".claude/skills/content-management/SKILL.md": "skills/content-management/SKILL.md"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
13
|
"engines": {
|
|
20
14
|
"node": ">=18"
|
|
21
15
|
},
|
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: content-management
|
|
3
|
-
description:
|
|
4
|
-
Managing content in Acquia Source via JSON:API including pages, media, and
|
|
5
|
-
components
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Content Management Skill
|
|
9
|
-
|
|
10
|
-
This skill provides tools for managing content in Acquia Source via JSON:API.
|
|
11
|
-
Use these commands to list, fetch, update, and create pages and other content
|
|
12
|
-
entities.
|
|
13
|
-
|
|
14
|
-
## Available Commands
|
|
15
|
-
|
|
16
|
-
All commands use the `@freelygive/canvas-jsonapi` CLI:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npx canvas-jsonapi <command> [args...]
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### List Content
|
|
23
|
-
|
|
24
|
-
List content items of a specific type:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npx canvas-jsonapi list <type>
|
|
28
|
-
npx canvas-jsonapi list --types # Discover available types
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Get Content
|
|
32
|
-
|
|
33
|
-
Fetch one or more content items and save them locally:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npx canvas-jsonapi get <type> <uuid> [<uuid>...]
|
|
37
|
-
npx canvas-jsonapi get <type> <uuid> --include <relationships>
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Examples:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
npx canvas-jsonapi get page abc-123-def
|
|
44
|
-
npx canvas-jsonapi get page abc-123 def-456 ghi-789
|
|
45
|
-
npx canvas-jsonapi get media--image uuid1 uuid2 uuid3
|
|
46
|
-
npx canvas-jsonapi get page abc-123-def --include image,owner
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Saves content to `content/<type>/<uuid>.json`. For `media--image`, automatically
|
|
50
|
-
includes the file relationship and displays the thumbnail URL and `target_id`.
|
|
51
|
-
|
|
52
|
-
### Create Content
|
|
53
|
-
|
|
54
|
-
Create a new content item from a local file:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npx canvas-jsonapi create <file-path>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
After creation, the temporary file is removed and the full entity is fetched and
|
|
61
|
-
saved with the UUID returned by the API.
|
|
62
|
-
|
|
63
|
-
### Update Content
|
|
64
|
-
|
|
65
|
-
Push changes from a local JSON file back to the API:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
npx canvas-jsonapi update <file-path>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
The file must contain valid JSON:API data with `data.type` and `data.id` fields.
|
|
72
|
-
|
|
73
|
-
### Delete Content
|
|
74
|
-
|
|
75
|
-
Delete one or more content items:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npx canvas-jsonapi delete <type> <uuid> [<uuid>...]
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Examples:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
npx canvas-jsonapi delete page abc-123-def
|
|
85
|
-
npx canvas-jsonapi delete media--image uuid1 uuid2 uuid3
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Also removes the local JSON file if it exists.
|
|
89
|
-
|
|
90
|
-
### Upload Image
|
|
91
|
-
|
|
92
|
-
Upload an image and create a media entity:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
npx canvas-jsonapi upload-image <image-path> [alt-text]
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Example:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
npx canvas-jsonapi upload-image src/stories/assets/photo.jpg "Photo description"
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Output includes:
|
|
105
|
-
|
|
106
|
-
- Media UUID
|
|
107
|
-
- File path
|
|
108
|
-
- Thumbnail URL
|
|
109
|
-
- `target_id` for use in components
|
|
110
|
-
|
|
111
|
-
### Generate UUID
|
|
112
|
-
|
|
113
|
-
Generate random UUIDs for new components:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
npx canvas-jsonapi uuid # Generate 1 UUID
|
|
117
|
-
npx canvas-jsonapi uuid 5 # Generate 5 UUIDs
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Local File Storage
|
|
121
|
-
|
|
122
|
-
Content is stored in the `/content` directory (gitignored):
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
content/
|
|
126
|
-
page/
|
|
127
|
-
abc-123-def-456.json
|
|
128
|
-
media--image/
|
|
129
|
-
img-123-456.json
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Page Structure
|
|
133
|
-
|
|
134
|
-
Pages in Acquia Source contain these key attributes:
|
|
135
|
-
|
|
136
|
-
- `title` - Page title
|
|
137
|
-
- `status` - Published status (true/false)
|
|
138
|
-
- `path` - URL path configuration
|
|
139
|
-
- `components` - Array of canvas components
|
|
140
|
-
- `metatags` - SEO metadata
|
|
141
|
-
- `include_in_search` - Whether to index for search
|
|
142
|
-
|
|
143
|
-
### Canvas Component Structure
|
|
144
|
-
|
|
145
|
-
Components are stored in `data.attributes.components` as a flat array. Nesting
|
|
146
|
-
is defined via `parent_uuid` and `slot` fields:
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
{
|
|
150
|
-
"data": {
|
|
151
|
-
"type": "page",
|
|
152
|
-
"attributes": {
|
|
153
|
-
"title": "My Page",
|
|
154
|
-
"status": true,
|
|
155
|
-
"components": [
|
|
156
|
-
{
|
|
157
|
-
"uuid": "comp-001",
|
|
158
|
-
"component_id": "js.section",
|
|
159
|
-
"inputs": { "width": "Normal" },
|
|
160
|
-
"parent_uuid": null,
|
|
161
|
-
"slot": null
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"uuid": "comp-002",
|
|
165
|
-
"component_id": "js.heading",
|
|
166
|
-
"inputs": { "text": "Title", "level": "h2" },
|
|
167
|
-
"parent_uuid": "comp-001",
|
|
168
|
-
"slot": "content"
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Note: `inputs` are automatically parsed to objects when fetched and stringified
|
|
177
|
-
when sent back to the API.
|
|
178
|
-
|
|
179
|
-
### Component Fields
|
|
180
|
-
|
|
181
|
-
| Field | Description |
|
|
182
|
-
| ------------------- | ---------------------------------------------------------- |
|
|
183
|
-
| `uuid` | Unique identifier for this component instance |
|
|
184
|
-
| `component_id` | Component type (e.g., `js.heading`, `js.card`) |
|
|
185
|
-
| `component_version` | Version hash of the component definition |
|
|
186
|
-
| `inputs` | Object containing prop values |
|
|
187
|
-
| `parent_uuid` | UUID of parent component (null for root-level) |
|
|
188
|
-
| `slot` | Slot name in parent (null for root-level, e.g., "content") |
|
|
189
|
-
|
|
190
|
-
## Media Image Handling
|
|
191
|
-
|
|
192
|
-
### Uploading Images
|
|
193
|
-
|
|
194
|
-
Images must be uploaded to the media library before referencing in components:
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
npx canvas-jsonapi upload-image image.jpg "Alt text"
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Output:
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
Uploading: image.jpg
|
|
204
|
-
Uploaded: image.jpg
|
|
205
|
-
UUID: 98eabd02-c52c-493b-8ca9-cb9d0fe70ceb
|
|
206
|
-
File: /var/www/html/pages/media--image/98eabd02-...json
|
|
207
|
-
Thumbnail: https://...
|
|
208
|
-
target_id: 31
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Media vs File Entity IDs (Critical)
|
|
212
|
-
|
|
213
|
-
When working with images, there are two different internal IDs:
|
|
214
|
-
|
|
215
|
-
| Entity Type | ID Location | Usage |
|
|
216
|
-
| ----------- | --------------------------------------------- | -------------------------- |
|
|
217
|
-
| **File** | `drupal_internal__target_id` in relationships | Internal file reference |
|
|
218
|
-
| **Media** | `resourceVersion=id%3AXX` in self link URL | **Use this in components** |
|
|
219
|
-
|
|
220
|
-
The `target_id` shown in command output is the correct media internal ID.
|
|
221
|
-
|
|
222
|
-
### Referencing Images in Components
|
|
223
|
-
|
|
224
|
-
Components that accept images (like `js.card`) use a `target_id` reference:
|
|
225
|
-
|
|
226
|
-
```json
|
|
227
|
-
{
|
|
228
|
-
"component_id": "js.card",
|
|
229
|
-
"inputs": {
|
|
230
|
-
"heading": "My Card",
|
|
231
|
-
"image": { "target_id": "31" },
|
|
232
|
-
"text": { "value": "<p>Content</p>", "format": "canvas_html_block" }
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Important:** The `target_id` must be the **media entity's internal ID**, not
|
|
238
|
-
the file's internal ID.
|
|
239
|
-
|
|
240
|
-
### Text Fields with HTML
|
|
241
|
-
|
|
242
|
-
Rich text fields use a specific format:
|
|
243
|
-
|
|
244
|
-
```json
|
|
245
|
-
{
|
|
246
|
-
"text": {
|
|
247
|
-
"value": "<p>HTML content with <a href=\"/page\">links</a>.</p>",
|
|
248
|
-
"format": "canvas_html_block"
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Allowed HTML Elements in Formatted Content
|
|
254
|
-
|
|
255
|
-
The following HTML elements are supported in formatted editor fields (like
|
|
256
|
-
article body content):
|
|
257
|
-
|
|
258
|
-
#### Inline Formatting
|
|
259
|
-
|
|
260
|
-
| Element | Usage | Example |
|
|
261
|
-
| ---------- | --------------- | ---------------------------- |
|
|
262
|
-
| `<strong>` | Bold text | `<strong>important</strong>` |
|
|
263
|
-
| `<em>` | Italic/emphasis | `<em>emphasized</em>` |
|
|
264
|
-
| `<u>` | Underlined text | `<u>underlined</u>` |
|
|
265
|
-
| `<s>` | Strikethrough | `<s>deleted</s>` |
|
|
266
|
-
| `<sup>` | Superscript | `x<sup>2</sup>` |
|
|
267
|
-
| `<sub>` | Subscript | `H<sub>2</sub>O` |
|
|
268
|
-
| `<a>` | Links | `<a href="/page">link</a>` |
|
|
269
|
-
| `<code>` | Inline code | `<code>variable</code>` |
|
|
270
|
-
|
|
271
|
-
#### Block Elements
|
|
272
|
-
|
|
273
|
-
| Element | Usage | Example |
|
|
274
|
-
| --------------- | --------------------------- | -------------------------------- |
|
|
275
|
-
| `<p>` | Paragraphs | `<p>Text content</p>` |
|
|
276
|
-
| `<h2>` - `<h6>` | Headings (h2-h6 only) | `<h2>Section Title</h2>` |
|
|
277
|
-
| `<ul>` | Unordered list | `<ul><li>Item</li></ul>` |
|
|
278
|
-
| `<ol>` | Ordered list | `<ol><li>Step 1</li></ol>` |
|
|
279
|
-
| `<blockquote>` | Highlighted external quotes | `<blockquote>Quote</blockquote>` |
|
|
280
|
-
|
|
281
|
-
#### Text Alignment Classes
|
|
282
|
-
|
|
283
|
-
Apply alignment via class attribute on block elements:
|
|
284
|
-
|
|
285
|
-
| Class | Effect |
|
|
286
|
-
| --------------------- | -------------- |
|
|
287
|
-
| `.text-align-center` | Center aligned |
|
|
288
|
-
| `.text-align-right` | Right aligned |
|
|
289
|
-
| `.text-align-justify` | Justified text |
|
|
290
|
-
|
|
291
|
-
Example:
|
|
292
|
-
|
|
293
|
-
```html
|
|
294
|
-
<p class="text-align-center">Centered paragraph</p>
|
|
295
|
-
<h2 class="text-align-right">Right-aligned heading</h2>
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
#### Embedded Media
|
|
299
|
-
|
|
300
|
-
Media items (images, videos) can be embedded within formatted content using
|
|
301
|
-
Drupal's `<drupal-media>` tag. This allows images to appear inline within
|
|
302
|
-
article body content.
|
|
303
|
-
|
|
304
|
-
**Format:**
|
|
305
|
-
|
|
306
|
-
```html
|
|
307
|
-
<drupal-media data-entity-type="media" data-entity-uuid="MEDIA-UUID"
|
|
308
|
-
> </drupal-media
|
|
309
|
-
>
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
**Steps to embed an image:**
|
|
313
|
-
|
|
314
|
-
1. First, upload the image to create a media entity:
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
npx canvas-jsonapi upload-image path/to/image.jpg "Alt text description"
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
Note the UUID returned (e.g., `c4324a2c-6a1d-43a1-98b5-bb2d73e42c54`).
|
|
321
|
-
|
|
322
|
-
2. Or find an existing media item:
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
npx canvas-jsonapi list media--image
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
3. Add the `<drupal-media>` tag to the content's `value` field:
|
|
329
|
-
|
|
330
|
-
```json
|
|
331
|
-
{
|
|
332
|
-
"content": {
|
|
333
|
-
"value": "<p>Article text here...</p><drupal-media data-entity-type=\"media\" data-entity-uuid=\"c4324a2c-6a1d-43a1-98b5-bb2d73e42c54\"> </drupal-media><p>More text after image...</p>",
|
|
334
|
-
"format": "filtered_html"
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
**Important notes:**
|
|
340
|
-
|
|
341
|
-
- The ` ` inside the tag is required (non-breaking space placeholder)
|
|
342
|
-
- Use the media entity's UUID, not the file UUID
|
|
343
|
-
- The `processed` field in the response shows the rendered HTML with actual
|
|
344
|
-
`<img>` tags - this is read-only and generated by Drupal
|
|
345
|
-
- Only edit the `value` field, never the `processed` field
|
|
346
|
-
|
|
347
|
-
### Content Formatting Best Practices
|
|
348
|
-
|
|
349
|
-
1. **Use semantic elements**: Use `<strong>` for important text, `<em>` for
|
|
350
|
-
emphasis, not just for visual styling.
|
|
351
|
-
|
|
352
|
-
2. **Blockquotes for highlighted external quotes only**: Use `<blockquote>` only
|
|
353
|
-
for external quotes that need visual emphasis (e.g., testimonials, key
|
|
354
|
-
statements from interviews). Do not use for inline quotes within regular
|
|
355
|
-
paragraphs. Use `<em>` for speaker attribution:
|
|
356
|
-
|
|
357
|
-
```html
|
|
358
|
-
<blockquote>
|
|
359
|
-
<p>"Quote text here"</p>
|
|
360
|
-
<p><em>— Speaker Name, Title</em></p>
|
|
361
|
-
</blockquote>
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
For regular inline quotes, simply use quotation marks within a `<p>` tag.
|
|
365
|
-
|
|
366
|
-
3. **Lists for related items**: Use `<ul>` or `<ol>` when listing multiple items
|
|
367
|
-
rather than comma-separated text.
|
|
368
|
-
|
|
369
|
-
4. **Headings for structure**: Use `<h2>`-`<h6>` to create clear content
|
|
370
|
-
sections.
|
|
371
|
-
|
|
372
|
-
## Workflow Examples
|
|
373
|
-
|
|
374
|
-
### Download and Edit a Page
|
|
375
|
-
|
|
376
|
-
```bash
|
|
377
|
-
npx canvas-jsonapi list page
|
|
378
|
-
npx canvas-jsonapi get page abc-123-def
|
|
379
|
-
# Edit content/page/abc-123-def.json
|
|
380
|
-
npx canvas-jsonapi update content/page/abc-123-def.json
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### Create a Page with Images
|
|
384
|
-
|
|
385
|
-
1. Upload images:
|
|
386
|
-
|
|
387
|
-
```bash
|
|
388
|
-
npx canvas-jsonapi upload-image image1.jpg "Description"
|
|
389
|
-
# Note target_id: 31
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
2. Generate UUIDs:
|
|
393
|
-
|
|
394
|
-
```bash
|
|
395
|
-
npx canvas-jsonapi uuid 3
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
3. Create page JSON at `content/page/new-my-page.json`:
|
|
399
|
-
|
|
400
|
-
```json
|
|
401
|
-
{
|
|
402
|
-
"data": {
|
|
403
|
-
"type": "page",
|
|
404
|
-
"attributes": {
|
|
405
|
-
"title": "My Page",
|
|
406
|
-
"status": true,
|
|
407
|
-
"components": [
|
|
408
|
-
{
|
|
409
|
-
"uuid": "generated-uuid",
|
|
410
|
-
"component_id": "js.card",
|
|
411
|
-
"inputs": {
|
|
412
|
-
"heading": "Card",
|
|
413
|
-
"image": { "target_id": "31" }
|
|
414
|
-
},
|
|
415
|
-
"parent_uuid": null,
|
|
416
|
-
"slot": null
|
|
417
|
-
}
|
|
418
|
-
],
|
|
419
|
-
"path": { "alias": "/my-page" },
|
|
420
|
-
"include_in_search": true
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
4. Create the page:
|
|
427
|
-
|
|
428
|
-
```bash
|
|
429
|
-
npx canvas-jsonapi create content/page/new-my-page.json
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
## Common Pitfalls
|
|
433
|
-
|
|
434
|
-
1. **Wrong ID type**: Using file's `drupal_internal__target_id` instead of
|
|
435
|
-
media's internal ID causes "image.src NULL value found" errors.
|
|
436
|
-
|
|
437
|
-
2. **Missing langcode permission**: When creating pages, omit the `langcode`
|
|
438
|
-
field as the API may reject it with permission errors.
|
|
439
|
-
|
|
440
|
-
3. **Patching limitations**: PATCH requests may not work for all fields. Create
|
|
441
|
-
a new page with a different alias if updates fail.
|
|
442
|
-
|
|
443
|
-
## Environment Variables
|
|
444
|
-
|
|
445
|
-
Required in `.env`:
|
|
446
|
-
|
|
447
|
-
- `CANVAS_SITE_URL` - Base URL of Acquia Source site
|
|
448
|
-
- `CANVAS_JSONAPI_PREFIX` - API prefix (default: "api")
|
|
449
|
-
- `CANVAS_CLIENT_ID` - OAuth client ID
|
|
450
|
-
- `CANVAS_CLIENT_SECRET` - OAuth client secret
|