@terminalfour/terminalfour-js 1.0.0-rc.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/LICENSE.md +106 -0
- package/README.md +169 -0
- package/dist/cjs/element-resolver.d.ts +115 -0
- package/dist/cjs/element-resolver.d.ts.map +1 -0
- package/dist/cjs/element-resolver.js +391 -0
- package/dist/cjs/element-resolver.js.map +1 -0
- package/dist/cjs/errors.d.ts +21 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +21 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/handlebars.d.ts +123 -0
- package/dist/cjs/handlebars.d.ts.map +1 -0
- package/dist/cjs/handlebars.js +306 -0
- package/dist/cjs/handlebars.js.map +1 -0
- package/dist/cjs/http-client.d.ts +21 -0
- package/dist/cjs/http-client.d.ts.map +1 -0
- package/dist/cjs/http-client.js +126 -0
- package/dist/cjs/http-client.js.map +1 -0
- package/dist/cjs/index.d.ts +37 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +55 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/media-category-ref.d.ts +78 -0
- package/dist/cjs/media-category-ref.d.ts.map +1 -0
- package/dist/cjs/media-category-ref.js +184 -0
- package/dist/cjs/media-category-ref.js.map +1 -0
- package/dist/cjs/media-library.d.ts +30 -0
- package/dist/cjs/media-library.d.ts.map +1 -0
- package/dist/cjs/media-library.js +77 -0
- package/dist/cjs/media-library.js.map +1 -0
- package/dist/cjs/models/content-item.d.ts +80 -0
- package/dist/cjs/models/content-item.d.ts.map +1 -0
- package/dist/cjs/models/content-item.js +682 -0
- package/dist/cjs/models/content-item.js.map +1 -0
- package/dist/cjs/models/media-category-item.d.ts +29 -0
- package/dist/cjs/models/media-category-item.d.ts.map +1 -0
- package/dist/cjs/models/media-category-item.js +33 -0
- package/dist/cjs/models/media-category-item.js.map +1 -0
- package/dist/cjs/models/media-item.d.ts +74 -0
- package/dist/cjs/models/media-item.d.ts.map +1 -0
- package/dist/cjs/models/media-item.js +188 -0
- package/dist/cjs/models/media-item.js.map +1 -0
- package/dist/cjs/models/section-item.d.ts +58 -0
- package/dist/cjs/models/section-item.d.ts.map +1 -0
- package/dist/cjs/models/section-item.js +166 -0
- package/dist/cjs/models/section-item.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/resources/channel-resource.d.ts +112 -0
- package/dist/cjs/resources/channel-resource.d.ts.map +1 -0
- package/dist/cjs/resources/channel-resource.js +107 -0
- package/dist/cjs/resources/channel-resource.js.map +1 -0
- package/dist/cjs/resources/content-resource.d.ts +50 -0
- package/dist/cjs/resources/content-resource.d.ts.map +1 -0
- package/dist/cjs/resources/content-resource.js +286 -0
- package/dist/cjs/resources/content-resource.js.map +1 -0
- package/dist/cjs/resources/content-type-resource.d.ts +283 -0
- package/dist/cjs/resources/content-type-resource.d.ts.map +1 -0
- package/dist/cjs/resources/content-type-resource.js +970 -0
- package/dist/cjs/resources/content-type-resource.js.map +1 -0
- package/dist/cjs/resources/group-resource.d.ts +96 -0
- package/dist/cjs/resources/group-resource.d.ts.map +1 -0
- package/dist/cjs/resources/group-resource.js +213 -0
- package/dist/cjs/resources/group-resource.js.map +1 -0
- package/dist/cjs/resources/list-resource.d.ts +111 -0
- package/dist/cjs/resources/list-resource.d.ts.map +1 -0
- package/dist/cjs/resources/list-resource.js +179 -0
- package/dist/cjs/resources/list-resource.js.map +1 -0
- package/dist/cjs/resources/media-resource.d.ts +69 -0
- package/dist/cjs/resources/media-resource.d.ts.map +1 -0
- package/dist/cjs/resources/media-resource.js +210 -0
- package/dist/cjs/resources/media-resource.js.map +1 -0
- package/dist/cjs/resources/media-type-resource.d.ts +70 -0
- package/dist/cjs/resources/media-type-resource.d.ts.map +1 -0
- package/dist/cjs/resources/media-type-resource.js +195 -0
- package/dist/cjs/resources/media-type-resource.js.map +1 -0
- package/dist/cjs/resources/navigation-resource.d.ts +664 -0
- package/dist/cjs/resources/navigation-resource.d.ts.map +1 -0
- package/dist/cjs/resources/navigation-resource.js +2349 -0
- package/dist/cjs/resources/navigation-resource.js.map +1 -0
- package/dist/cjs/resources/page-layout-resource.d.ts +83 -0
- package/dist/cjs/resources/page-layout-resource.d.ts.map +1 -0
- package/dist/cjs/resources/page-layout-resource.js +214 -0
- package/dist/cjs/resources/page-layout-resource.js.map +1 -0
- package/dist/cjs/resources/user-resource.d.ts +126 -0
- package/dist/cjs/resources/user-resource.d.ts.map +1 -0
- package/dist/cjs/resources/user-resource.js +317 -0
- package/dist/cjs/resources/user-resource.js.map +1 -0
- package/dist/cjs/section-ref.d.ts +185 -0
- package/dist/cjs/section-ref.d.ts.map +1 -0
- package/dist/cjs/section-ref.js +813 -0
- package/dist/cjs/section-ref.js.map +1 -0
- package/dist/cjs/site-structure.d.ts +18 -0
- package/dist/cjs/site-structure.d.ts.map +1 -0
- package/dist/cjs/site-structure.js +57 -0
- package/dist/cjs/site-structure.js.map +1 -0
- package/dist/cjs/t4-client.d.ts +121 -0
- package/dist/cjs/t4-client.d.ts.map +1 -0
- package/dist/cjs/t4-client.js +190 -0
- package/dist/cjs/t4-client.js.map +1 -0
- package/dist/cjs/type-registry.d.ts +31 -0
- package/dist/cjs/type-registry.d.ts.map +1 -0
- package/dist/cjs/type-registry.js +76 -0
- package/dist/cjs/type-registry.js.map +1 -0
- package/dist/cjs/types.d.ts +211 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils.d.ts +147 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +408 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/element-resolver.d.ts +115 -0
- package/dist/esm/element-resolver.d.ts.map +1 -0
- package/dist/esm/element-resolver.js +387 -0
- package/dist/esm/element-resolver.js.map +1 -0
- package/dist/esm/errors.d.ts +21 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +17 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/handlebars.d.ts +123 -0
- package/dist/esm/handlebars.d.ts.map +1 -0
- package/dist/esm/handlebars.js +300 -0
- package/dist/esm/handlebars.js.map +1 -0
- package/dist/esm/http-client.d.ts +21 -0
- package/dist/esm/http-client.d.ts.map +1 -0
- package/dist/esm/http-client.js +122 -0
- package/dist/esm/http-client.js.map +1 -0
- package/dist/esm/index.d.ts +37 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/media-category-ref.d.ts +78 -0
- package/dist/esm/media-category-ref.d.ts.map +1 -0
- package/dist/esm/media-category-ref.js +180 -0
- package/dist/esm/media-category-ref.js.map +1 -0
- package/dist/esm/media-library.d.ts +30 -0
- package/dist/esm/media-library.d.ts.map +1 -0
- package/dist/esm/media-library.js +73 -0
- package/dist/esm/media-library.js.map +1 -0
- package/dist/esm/models/content-item.d.ts +80 -0
- package/dist/esm/models/content-item.d.ts.map +1 -0
- package/dist/esm/models/content-item.js +676 -0
- package/dist/esm/models/content-item.js.map +1 -0
- package/dist/esm/models/media-category-item.d.ts +29 -0
- package/dist/esm/models/media-category-item.d.ts.map +1 -0
- package/dist/esm/models/media-category-item.js +29 -0
- package/dist/esm/models/media-category-item.js.map +1 -0
- package/dist/esm/models/media-item.d.ts +74 -0
- package/dist/esm/models/media-item.d.ts.map +1 -0
- package/dist/esm/models/media-item.js +184 -0
- package/dist/esm/models/media-item.js.map +1 -0
- package/dist/esm/models/section-item.d.ts +58 -0
- package/dist/esm/models/section-item.d.ts.map +1 -0
- package/dist/esm/models/section-item.js +162 -0
- package/dist/esm/models/section-item.js.map +1 -0
- package/dist/esm/resources/channel-resource.d.ts +112 -0
- package/dist/esm/resources/channel-resource.d.ts.map +1 -0
- package/dist/esm/resources/channel-resource.js +102 -0
- package/dist/esm/resources/channel-resource.js.map +1 -0
- package/dist/esm/resources/content-resource.d.ts +50 -0
- package/dist/esm/resources/content-resource.d.ts.map +1 -0
- package/dist/esm/resources/content-resource.js +282 -0
- package/dist/esm/resources/content-resource.js.map +1 -0
- package/dist/esm/resources/content-type-resource.d.ts +283 -0
- package/dist/esm/resources/content-type-resource.d.ts.map +1 -0
- package/dist/esm/resources/content-type-resource.js +964 -0
- package/dist/esm/resources/content-type-resource.js.map +1 -0
- package/dist/esm/resources/group-resource.d.ts +96 -0
- package/dist/esm/resources/group-resource.d.ts.map +1 -0
- package/dist/esm/resources/group-resource.js +208 -0
- package/dist/esm/resources/group-resource.js.map +1 -0
- package/dist/esm/resources/list-resource.d.ts +111 -0
- package/dist/esm/resources/list-resource.d.ts.map +1 -0
- package/dist/esm/resources/list-resource.js +174 -0
- package/dist/esm/resources/list-resource.js.map +1 -0
- package/dist/esm/resources/media-resource.d.ts +69 -0
- package/dist/esm/resources/media-resource.d.ts.map +1 -0
- package/dist/esm/resources/media-resource.js +206 -0
- package/dist/esm/resources/media-resource.js.map +1 -0
- package/dist/esm/resources/media-type-resource.d.ts +70 -0
- package/dist/esm/resources/media-type-resource.d.ts.map +1 -0
- package/dist/esm/resources/media-type-resource.js +190 -0
- package/dist/esm/resources/media-type-resource.js.map +1 -0
- package/dist/esm/resources/navigation-resource.d.ts +664 -0
- package/dist/esm/resources/navigation-resource.d.ts.map +1 -0
- package/dist/esm/resources/navigation-resource.js +2344 -0
- package/dist/esm/resources/navigation-resource.js.map +1 -0
- package/dist/esm/resources/page-layout-resource.d.ts +83 -0
- package/dist/esm/resources/page-layout-resource.d.ts.map +1 -0
- package/dist/esm/resources/page-layout-resource.js +209 -0
- package/dist/esm/resources/page-layout-resource.js.map +1 -0
- package/dist/esm/resources/user-resource.d.ts +126 -0
- package/dist/esm/resources/user-resource.d.ts.map +1 -0
- package/dist/esm/resources/user-resource.js +312 -0
- package/dist/esm/resources/user-resource.js.map +1 -0
- package/dist/esm/section-ref.d.ts +185 -0
- package/dist/esm/section-ref.d.ts.map +1 -0
- package/dist/esm/section-ref.js +808 -0
- package/dist/esm/section-ref.js.map +1 -0
- package/dist/esm/site-structure.d.ts +18 -0
- package/dist/esm/site-structure.d.ts.map +1 -0
- package/dist/esm/site-structure.js +53 -0
- package/dist/esm/site-structure.js.map +1 -0
- package/dist/esm/t4-client.d.ts +121 -0
- package/dist/esm/t4-client.d.ts.map +1 -0
- package/dist/esm/t4-client.js +186 -0
- package/dist/esm/t4-client.js.map +1 -0
- package/dist/esm/type-registry.d.ts +31 -0
- package/dist/esm/type-registry.d.ts.map +1 -0
- package/dist/esm/type-registry.js +72 -0
- package/dist/esm/type-registry.js.map +1 -0
- package/dist/esm/types.d.ts +211 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils.d.ts +147 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +355 -0
- package/dist/esm/utils.js.map +1 -0
- package/docs/channels.md +62 -0
- package/docs/content-types.md +313 -0
- package/docs/content.md +199 -0
- package/docs/error-handling.md +86 -0
- package/docs/getting-started.md +146 -0
- package/docs/groups-and-users.md +167 -0
- package/docs/handlebars.md +145 -0
- package/docs/lists.md +98 -0
- package/docs/media-types.md +111 -0
- package/docs/media.md +169 -0
- package/docs/navigation/a-to-z.md +62 -0
- package/docs/navigation/breadcrumbs.md +67 -0
- package/docs/navigation/css-selector.md +53 -0
- package/docs/navigation/generate-file.md +48 -0
- package/docs/navigation/keyword-search.md +134 -0
- package/docs/navigation/language-switcher.md +37 -0
- package/docs/navigation/link-menu.md +105 -0
- package/docs/navigation/pagination.md +80 -0
- package/docs/navigation/previous-next-fulltext.md +43 -0
- package/docs/navigation/publish-to-one-file.md +93 -0
- package/docs/navigation/related-content.md +79 -0
- package/docs/navigation/related-section-branch.md +31 -0
- package/docs/navigation/return-to-index.md +35 -0
- package/docs/navigation/section-details.md +51 -0
- package/docs/navigation/section-iterator.md +33 -0
- package/docs/navigation/section-meta-info.md +39 -0
- package/docs/navigation/site-map.md +58 -0
- package/docs/navigation/top-content.md +80 -0
- package/docs/navigation/top-stories.md +47 -0
- package/docs/navigation.md +134 -0
- package/docs/page-layouts.md +71 -0
- package/docs/sections.md +224 -0
- package/docs/typescript.md +124 -0
- package/package.json +64 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Content Types
|
|
2
|
+
|
|
3
|
+
Use `t4.contentTypes` to inspect and manage content type fields and layouts.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [List and read content types](#list-and-read-content-types)
|
|
8
|
+
- [Create a content type](#create-a-content-type)
|
|
9
|
+
- [Update a content type](#update-a-content-type)
|
|
10
|
+
- [Add or remove fields](#add-or-remove-fields)
|
|
11
|
+
- [Manage content layouts](#manage-content-layouts)
|
|
12
|
+
|
|
13
|
+
## List and read content types
|
|
14
|
+
|
|
15
|
+
### List content types
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const types = await t4.contentTypes.list();
|
|
19
|
+
for (const contentType of types) {
|
|
20
|
+
console.log(contentType.id, contentType.name, Object.keys(contentType.fields).length);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Get a content type
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
const news = await t4.contentTypes.get(44);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The returned `ContentType` includes these main properties:
|
|
31
|
+
|
|
32
|
+
| Property | Example or purpose |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `name` | `'News Article'` |
|
|
35
|
+
| `description` | Content type description |
|
|
36
|
+
| `minUserLevel` | `'contributor'` |
|
|
37
|
+
| `workflow` | Assigned workflow |
|
|
38
|
+
| `directEdit` | Direct edit setting |
|
|
39
|
+
| `primaryGroup` | Primary group |
|
|
40
|
+
| `sharedGroups` | Shared groups |
|
|
41
|
+
| `fields` | Field definitions keyed by name |
|
|
42
|
+
|
|
43
|
+
Each entry in `news.fields` can include:
|
|
44
|
+
|
|
45
|
+
| Property | Meaning |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `type` | Element type, such as `'Plain Text'` |
|
|
48
|
+
| `required` | Whether a value is required |
|
|
49
|
+
| `maxSize` | Maximum size, such as `200` |
|
|
50
|
+
| `shown` | Whether the field is shown |
|
|
51
|
+
| `listId` | List ID; `0` for non-list fields |
|
|
52
|
+
| `listName` | Resolved list name when `listId > 0`; otherwise `''` |
|
|
53
|
+
| `useAsFilename` | Whether the field supplies the filename |
|
|
54
|
+
| `config` | Repeater settings, including `contentTypeName`, `minRepeats`, and `maxRepeats` |
|
|
55
|
+
| `editor` | HTML editor, such as `'TinyMCE'` or `'Standard Textarea'` |
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
for (const [name, field] of Object.entries(news.fields)) {
|
|
59
|
+
console.log(name, field.type, field.required, field.maxSize);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Create a content type
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const contentType = await t4.contentTypes.create({
|
|
67
|
+
name: 'Blog Post',
|
|
68
|
+
description: 'A blog post with title and body',
|
|
69
|
+
elements: [
|
|
70
|
+
{ name: 'Title', type: 'Plain Text', required: true, maxSize: 200 },
|
|
71
|
+
{ name: 'Body', type: 'HTML' },
|
|
72
|
+
{ name: 'Summary', type: 'Plain Text', maxSize: 500, shown: false },
|
|
73
|
+
],
|
|
74
|
+
minUserLevel: 'contributor',
|
|
75
|
+
directEdit: true,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The SDK always inserts a `Name` element as the first element.
|
|
80
|
+
|
|
81
|
+
### List-based fields
|
|
82
|
+
|
|
83
|
+
`Select Box`, `Radio Button`, `Check Box`, `Multiple Select`, `Multi-select List`, `Cascading List`, and `Keyword Selector` fields require `listId`:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
await t4.contentTypes.create({
|
|
87
|
+
name: 'Categorised Article',
|
|
88
|
+
elements: [
|
|
89
|
+
{ name: 'Title', type: 'Plain Text', required: true },
|
|
90
|
+
{ name: 'Body', type: 'HTML' },
|
|
91
|
+
{ name: 'Category', type: 'Select Box', listId: 71 },
|
|
92
|
+
{ name: 'Tags', type: 'Check Box', listId: 72 },
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Repeater fields
|
|
98
|
+
|
|
99
|
+
A Repeater requires the sub-content type that defines its fields:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
await t4.contentTypes.create({
|
|
103
|
+
name: 'Page with Slides',
|
|
104
|
+
elements: [
|
|
105
|
+
{ name: 'Title', type: 'Plain Text', required: true },
|
|
106
|
+
{
|
|
107
|
+
name: 'Slides',
|
|
108
|
+
type: 'Repeater',
|
|
109
|
+
repeater: {
|
|
110
|
+
contentTypeId: 99, // required: content type that defines repeater fields
|
|
111
|
+
layout: 'text/slides', // optional; default: ''
|
|
112
|
+
minRepeats: 1, // optional; default: 0
|
|
113
|
+
maxRepeats: 10, // optional; default: 100
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### HTML editors
|
|
121
|
+
|
|
122
|
+
Set `editor` to choose the editor shown in the T4 content editing UI:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
await t4.contentTypes.create({
|
|
126
|
+
name: 'Article',
|
|
127
|
+
elements: [
|
|
128
|
+
{ name: 'Title', type: 'Plain Text', required: true },
|
|
129
|
+
{ name: 'Body', type: 'HTML', editor: 'TinyMCE' },
|
|
130
|
+
{ name: 'Notes', type: 'HTML', editor: 'Standard Textarea' },
|
|
131
|
+
],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The editor name must exist on the T4 instance. An invalid name produces an error that lists the available options.
|
|
136
|
+
|
|
137
|
+
## Update a content type
|
|
138
|
+
|
|
139
|
+
### Direct update
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
await t4.contentTypes.update(44, {
|
|
143
|
+
name: 'Renamed',
|
|
144
|
+
description: 'Updated',
|
|
145
|
+
directEdit: false,
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Add and remove fields in the same call:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
await t4.contentTypes.update(44, {
|
|
153
|
+
addFields: [
|
|
154
|
+
{ name: 'Subtitle', type: 'Plain Text', maxSize: 200 },
|
|
155
|
+
{
|
|
156
|
+
name: 'Slides',
|
|
157
|
+
type: 'Repeater',
|
|
158
|
+
repeater: {
|
|
159
|
+
contentTypeId: 99,
|
|
160
|
+
layout: 'text/slides',
|
|
161
|
+
minRepeats: 1,
|
|
162
|
+
maxRepeats: 10,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
removeFields: ['Old Field'],
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Mutable item
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const contentType = await t4.contentTypes.get(44);
|
|
174
|
+
contentType.name = 'Renamed';
|
|
175
|
+
contentType.description = 'Updated description';
|
|
176
|
+
contentType.minUserLevel = 'moderator';
|
|
177
|
+
contentType.fields['Title'].maxSize = 300;
|
|
178
|
+
contentType.fields['Title'].required = false;
|
|
179
|
+
contentType.fields['Body'].editor = 'Standard Textarea';
|
|
180
|
+
await contentType.save();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Set an HTML field's `editor` to `null` when you want to use the instance default.
|
|
184
|
+
|
|
185
|
+
## Add or remove fields
|
|
186
|
+
|
|
187
|
+
`addField()` is asynchronous because it resolves the element type name to an ID. Field additions and removals take effect when you call `save()`.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const contentType = await t4.contentTypes.get(44);
|
|
191
|
+
|
|
192
|
+
await contentType.addField({
|
|
193
|
+
name: 'Subtitle',
|
|
194
|
+
type: 'Plain Text',
|
|
195
|
+
description: 'Optional subtitle',
|
|
196
|
+
maxSize: 200,
|
|
197
|
+
required: false,
|
|
198
|
+
shown: true,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await contentType.addField({
|
|
202
|
+
name: 'Category',
|
|
203
|
+
type: 'Select Box',
|
|
204
|
+
listId: 71,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await contentType.addField({
|
|
208
|
+
name: 'Slides',
|
|
209
|
+
type: 'Repeater',
|
|
210
|
+
repeater: {
|
|
211
|
+
contentTypeId: 99,
|
|
212
|
+
layout: 'text/slides',
|
|
213
|
+
minRepeats: 1,
|
|
214
|
+
maxRepeats: 10,
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await contentType.addField({
|
|
219
|
+
name: 'Body',
|
|
220
|
+
type: 'HTML',
|
|
221
|
+
editor: 'TinyMCE',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
contentType.removeField('Old Field');
|
|
225
|
+
await contentType.save();
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Delete a content type through the resource:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
await t4.contentTypes.delete(44);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## System content types
|
|
235
|
+
|
|
236
|
+
System content types are managed by T4 and back core features. Removing or renaming their elements can break the instance, so the SDK blocks both on `save()` (and through `update({ removeFields })`):
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const systemType = await t4.contentTypes.get(systemTypeId);
|
|
240
|
+
systemType.removeField('Some Element');
|
|
241
|
+
await systemType.save();
|
|
242
|
+
// Error: Cannot remove element "Some Element" from content type "..." because it
|
|
243
|
+
// is a system content type. Removing elements from system content types is not allowed.
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
On a system content type you can still:
|
|
247
|
+
|
|
248
|
+
- Add new elements with `addField()` or `update({ addFields })`
|
|
249
|
+
- Change an element's `maxSize`
|
|
250
|
+
- Change the content type's `description` and any element's `description`
|
|
251
|
+
|
|
252
|
+
Two system content types are exempt, because removing and renaming their elements is safe: the **Section Metadata** content type and the **Extended User** content type. On those two, removal and renaming work exactly like a regular content type.
|
|
253
|
+
|
|
254
|
+
The check runs when you call `save()` or `update()`, not when you call `removeField()`. Because `removeField()` only stages the change in memory, nothing is sent to T4 when the guard blocks a save.
|
|
255
|
+
|
|
256
|
+
## Manage content layouts
|
|
257
|
+
|
|
258
|
+
Access layouts through `layouts` on a `ContentType`. Layout operations use names; layout IDs are not exposed.
|
|
259
|
+
|
|
260
|
+
### List and read layouts
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const contentType = await t4.contentTypes.get(44);
|
|
264
|
+
const layouts = await contentType.layouts.list();
|
|
265
|
+
// [{ name: 'text/html', lastModified: Date }]
|
|
266
|
+
|
|
267
|
+
const layout = await contentType.layouts.get('text/html');
|
|
268
|
+
console.log(layout.name); // 'text/html'
|
|
269
|
+
console.log(layout.code); // '<h1>{{Title}}</h1>'
|
|
270
|
+
console.log(layout.lastModified); // Date
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Create a layout
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
await contentType.layouts.create({
|
|
277
|
+
name: 'text/json',
|
|
278
|
+
code: '{ "title": "{{Title}}" }',
|
|
279
|
+
syntax: 'HTML/XML', // optional; default: 'HTML/XML'
|
|
280
|
+
processor: 'handlebars', // optional; default: 'handlebars'
|
|
281
|
+
extension: 'json', // optional; default: ''
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Processor options are `'handlebars'`, `'t4-tags'`, and `'programmable-layouts'`. The default is `'handlebars'`. The SDK enforces unique layout names.
|
|
286
|
+
|
|
287
|
+
### Direct update
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
await contentType.layouts.update('text/html', {
|
|
291
|
+
code: '<div>{{Title}}</div>',
|
|
292
|
+
processor: 't4-tags',
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Mutable item
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
const layout = await contentType.layouts.get('text/html');
|
|
300
|
+
layout.code = '<div>Updated</div>';
|
|
301
|
+
layout.name = 'text/renamed';
|
|
302
|
+
await layout.save();
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Delete a layout
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
await contentType.layouts.delete('text/old-layout');
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
**Previous:** [Content](./content.md) · **Next:** [Lists](./lists.md)
|
package/docs/content.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Content
|
|
2
|
+
|
|
3
|
+
Content operations are scoped to a section through `t4.section(id).content`.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [List and read content](#list-and-read-content)
|
|
8
|
+
- [Create content](#create-content)
|
|
9
|
+
- [Update content](#update-content)
|
|
10
|
+
- [Approve, duplicate, move, or remove](#approve-duplicate-move-or-remove)
|
|
11
|
+
- [Element values](#element-values)
|
|
12
|
+
- [Values returned on read](#values-returned-on-read)
|
|
13
|
+
|
|
14
|
+
## List and read content
|
|
15
|
+
|
|
16
|
+
### List content
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const items = await t4.section(482).content.list();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each summary contains `id`, `name`, `status`, `contentTypeID`, `version`, `lastModified`, `publishDate`, `expiryDate`, `reviewDate`, and `archiveSection`. Summaries do not contain `fields`; call `content.get(id)` to retrieve a full item with resolved fields.
|
|
23
|
+
|
|
24
|
+
### Get a content item
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
const item = await t4.section(482).content.get(9132);
|
|
28
|
+
console.log(item.name);
|
|
29
|
+
console.log(item.status); // 'approved', 'pending', 'draft', 'inactive'
|
|
30
|
+
console.log(item.fields); // { Title: 'Hello', Category: 'Featured', ... }
|
|
31
|
+
console.log(item.publishDate); // Date object or null
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`get()` returns a mutable `ContentItem`:
|
|
35
|
+
|
|
36
|
+
| Property | Type | Mutable | Description |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `id` | `number` | no | Content ID |
|
|
39
|
+
| `name` | `string` | yes | Content name |
|
|
40
|
+
| `contentTypeID` | `number` | no | Content type ID |
|
|
41
|
+
| `language` | `string` | no | Language code |
|
|
42
|
+
| `status` | `string` | yes | `'approved'`, `'pending'`, `'draft'`, or `'inactive'` |
|
|
43
|
+
| `version` | `number` | no | Version number |
|
|
44
|
+
| `lastModified` | `Date \| null` | no | Last modification date |
|
|
45
|
+
| `publishDate` | `Date \| null` | yes | Publish date |
|
|
46
|
+
| `expiryDate` | `Date \| null` | yes | Expiry date |
|
|
47
|
+
| `reviewDate` | `Date \| null` | yes | Review date |
|
|
48
|
+
| `archiveSection` | `number \| null` | yes | Archive section ID |
|
|
49
|
+
| `fields` | `Record<string, unknown>` | yes | Resolved content fields |
|
|
50
|
+
|
|
51
|
+
## Create content
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const article = await t4.section(482).content.create({
|
|
55
|
+
type: 44,
|
|
56
|
+
name: 'My Article',
|
|
57
|
+
status: 'draft', // default: 'pending'
|
|
58
|
+
fields: {
|
|
59
|
+
Title: 'Breaking News',
|
|
60
|
+
Body: '<p>Article content.</p>',
|
|
61
|
+
Category: 'Featured', // list value by name
|
|
62
|
+
'Publish Date': new Date(),
|
|
63
|
+
'Hero Image': './photo.jpg', // uploads the file automatically
|
|
64
|
+
Related: { sectionId: 500, linkText: 'More' }, // SS link
|
|
65
|
+
},
|
|
66
|
+
publishDate: new Date('2025-07-01'),
|
|
67
|
+
expiryDate: new Date('2025-12-31'),
|
|
68
|
+
archiveSection: 500,
|
|
69
|
+
owner: 38,
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The SDK maps field names to element keys, resolves list values to IDs, uploads files, creates link records, and builds the full request body. An unknown field name produces an error that lists the valid fields.
|
|
74
|
+
|
|
75
|
+
Before creating or updating content, inspect the content type when you need its field constraints:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const type = await t4.contentTypes.get(contentTypeId);
|
|
79
|
+
console.log(type.fields);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Each field includes `name`, `type`, `required`, `maxSize`, `listId`, `listName`, and repeater configuration. Check these values to confirm maximum lengths, list assignments for Select Box or Radio Button fields, required fields, and Repeater sub-fields.
|
|
83
|
+
|
|
84
|
+
## Update content
|
|
85
|
+
|
|
86
|
+
### Direct update
|
|
87
|
+
|
|
88
|
+
Pass only the values to change. `update()` fetches the existing item, merges your changes, and posts the full body:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
await t4.section(482).content.update(9132, {
|
|
92
|
+
name: 'Updated Name',
|
|
93
|
+
fields: { Title: 'New Title' },
|
|
94
|
+
status: 'approved',
|
|
95
|
+
publishDate: new Date('2025-08-01'),
|
|
96
|
+
expiryDate: null, // clear the value
|
|
97
|
+
archiveSection: null, // clear the value
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Mutable item
|
|
102
|
+
|
|
103
|
+
Retrieve an item when you need to inspect or change several current values:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const article = await t4.section(482).content.get(9132);
|
|
107
|
+
article.name = 'Updated Name';
|
|
108
|
+
article.fields['Title'] = 'New Title';
|
|
109
|
+
article.status = 'approved';
|
|
110
|
+
article.archiveSection = 236;
|
|
111
|
+
await article.save();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`save()` defaults the status to `'pending'` to match T4's approval workflow. Set `item.status = 'approved'` before saving if the item must remain approved.
|
|
115
|
+
|
|
116
|
+
## Approve, duplicate, move, or remove
|
|
117
|
+
|
|
118
|
+
### Approve one item
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const article = await t4.section(482).content.get(9132);
|
|
122
|
+
article.fields['Title'] = 'Reviewed Title';
|
|
123
|
+
await article.approve(); // saves with status 'approved'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Approve all pending items
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const count = await t4.section(482).content.approveAll();
|
|
130
|
+
console.log(`Approved ${count} items`);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`approveAll()` lists the section content, filters pending items, and sends one bulk approval request. It returns `0` when no items are pending.
|
|
134
|
+
|
|
135
|
+
### Duplicate an item
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const item = await t4.section(482).content.get(9132);
|
|
139
|
+
|
|
140
|
+
await item.duplicate(); // same section: appends "(1)" to avoid a name collision
|
|
141
|
+
await item.duplicate(500); // another section: keeps the original name
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
For duplicates in the same section, the SDK checks existing names and chooses the next available `(n)` suffix.
|
|
145
|
+
|
|
146
|
+
### Delete, purge, or move
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
await t4.section(482).content.delete(9132); // soft delete
|
|
150
|
+
await t4.section(482).content.purge(9132); // permanent removal
|
|
151
|
+
|
|
152
|
+
const item = await t4.section(482).content.get(9132);
|
|
153
|
+
await item.move(500); // move to section 500
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Element values
|
|
157
|
+
|
|
158
|
+
| Type | Pass | SDK sends or performs |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| Plain Text | `"text"` | Pass-through |
|
|
161
|
+
| HTML | `"<p>html</p>"` | Reverts SS link anchors to T4 tags on save; otherwise pass-through |
|
|
162
|
+
| Date | `new Date()`, timestamp, or string | Millisecond timestamp |
|
|
163
|
+
| Select Box | `"Large"` | `listId:itemId` |
|
|
164
|
+
| Radio Button | `"Large"` | `listId:itemId` |
|
|
165
|
+
| Checkbox | `["Large", "Small"]` | `listId:id1,id2` |
|
|
166
|
+
| Multiple Select | `["Large", "Small"]` | `listId:id1,id2` |
|
|
167
|
+
| Multi-Select List | `["Large", "Small"]` | `listId:id1;listId:id2` |
|
|
168
|
+
| Cascading List | `["Soccer", "Liverpool"]` | Resolves sublists automatically |
|
|
169
|
+
| Media | `10928` | String ID |
|
|
170
|
+
| Media (inline) | `{ file: './photo.jpg', name: 'Photo', category: 391 }` | Uploads to the media library and uses the returned ID |
|
|
171
|
+
| File / Image | `"./path.jpg"`, URL, Blob, or `{ file, filename }` | Uploads through `/upload/` |
|
|
172
|
+
| Section/Content Link | `{ sectionId, contentId?, linkText? }` | Creates an SS record and T4 tag |
|
|
173
|
+
| Decimal | `3.14` | Pass-through |
|
|
174
|
+
| Whole Number | `42` | Pass-through |
|
|
175
|
+
| Content Owner | `38` | String; resolves to user details on read |
|
|
176
|
+
| Group Select | `[41, 34, 40]` | Comma-separated value; resolves to group objects on read |
|
|
177
|
+
| Keyword Selector | `{ or: ["Large", { and: ["Small", "Other"] }] }` | Formats OR/AND groups |
|
|
178
|
+
| Repeater | `[{ name: 'Slide 1', fields: { Heading: 'Hi' } }]` | Full nested resolution |
|
|
179
|
+
|
|
180
|
+
## Values returned on read
|
|
181
|
+
|
|
182
|
+
The SDK converts raw API values before assigning them to `ContentItem.fields`:
|
|
183
|
+
|
|
184
|
+
| Element | Returned value |
|
|
185
|
+
|---|---|
|
|
186
|
+
| List | Names, such as `"Large"` instead of `"1:2"` |
|
|
187
|
+
| Date | `Date` object |
|
|
188
|
+
| Media | Object with `id`, `name`, `filename`, `description`, `mediaType`, `downloadLink`, `path`, `fileSize`, and `lastModified` |
|
|
189
|
+
| File / Image | Object with `filename`, `fileSize`, and `downloadLink` |
|
|
190
|
+
| SS link | `{ sectionId, contentId?, linkText, path }` |
|
|
191
|
+
| HTML SS link | Inline `<a href="#" data-t4-sslink="..." data-section-id="..." data-content-id="...">linkText</a>` converted from a T4 `<t4 sslink_id="..." />` tag |
|
|
192
|
+
| Content Owner | User object with `id`, `type`, `username`, `firstName`, `lastName`, and `emailAddress` |
|
|
193
|
+
| Group Select | Array of `{ id, name, selected }` objects |
|
|
194
|
+
| Keyword Selector | `{ or: [...] }` structure |
|
|
195
|
+
| Repeater | Array of `{ name, fields }` with recursively resolved fields |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
**Previous:** [Sections](./sections.md) · **Next:** [Content Types](./content-types.md)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
3
|
+
The SDK distinguishes API failures from client-side validation errors. Enable debug logging when you need request-level detail.
|
|
4
|
+
|
|
5
|
+
## Handle API errors
|
|
6
|
+
|
|
7
|
+
API failures throw `T4ApiError`. Check its properties to identify the failed request and response:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { T4Client, T4ApiError } from '@terminalfour/terminalfour-js';
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await t4.section(482).content.get(99999);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error instanceof T4ApiError) {
|
|
16
|
+
console.error(error.statusCode); // 404
|
|
17
|
+
console.error(error.statusText); // '404'
|
|
18
|
+
console.error(error.requestMethod); // 'GET'
|
|
19
|
+
console.error(error.requestUrl); // full URL
|
|
20
|
+
console.error(error.responseBody); // parsed response body
|
|
21
|
+
console.error(error.cause); // original error for network failures
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Property | Description |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `statusCode` | HTTP status code |
|
|
29
|
+
| `statusText` | HTTP status text |
|
|
30
|
+
| `requestMethod` | Request method |
|
|
31
|
+
| `requestUrl` | Full request URL |
|
|
32
|
+
| `responseBody` | Parsed response body |
|
|
33
|
+
| `cause` | Original error for network failures |
|
|
34
|
+
|
|
35
|
+
## Handle validation errors
|
|
36
|
+
|
|
37
|
+
Client-side validation throws standard `Error` instances. Messages include the value that failed and valid options when available.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
await t4.section(482).content.create({
|
|
41
|
+
type: 44,
|
|
42
|
+
name: 'Test',
|
|
43
|
+
fields: { Nonexistent: 'value' },
|
|
44
|
+
});
|
|
45
|
+
// Error: Unknown field "Nonexistent" on this content type.
|
|
46
|
+
// Valid fields are: "Title", "Body", "Category"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Other examples:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
Error: Invalid list value "Medium" for field "Size".
|
|
53
|
+
Valid options are: "Large", "Small"
|
|
54
|
+
|
|
55
|
+
Error: Username is required
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The final error can result from a call such as:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
await t4.users.create({ username: '', ... });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Enable debug logging
|
|
65
|
+
|
|
66
|
+
Set `T4_DEBUG=1` to print HTTP requests and internal warnings:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
T4_DEBUG=1 node my-script.js
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Graceful degradation paths, including failed media lookups and group name resolution, log through `debugWarn`. These messages appear only when `T4_DEBUG=1`.
|
|
73
|
+
|
|
74
|
+
## Clear stale cache data
|
|
75
|
+
|
|
76
|
+
After changing content types, lists, or other configuration, invalidate every SDK cache:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
t4.clearCache();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This clears cached content type templates, list values, element types, meta tags, group trees, and media types immediately.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
**Previous:** [Handlebars](./handlebars.md) · **Next:** [TypeScript Reference](./typescript.md)
|