@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,146 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Install and configure terminalfour-js in server-side TypeScript code, then verify the connection and choose language and cache settings.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @terminalfour/terminalfour-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package includes TypeScript declarations and supports both ESM (`import`) and CommonJS (`require`). It requires **Node 18 or later** because it uses the built-in `fetch`, `FormData`, and `Error.cause`.
|
|
12
|
+
|
|
13
|
+
## Create a client
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { T4Client } from '@terminalfour/terminalfour-js';
|
|
17
|
+
|
|
18
|
+
const t4 = new T4Client({
|
|
19
|
+
baseUrl: 'https://mysite.edu/terminalfour/rs',
|
|
20
|
+
apiToken: 'your-api-token',
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Configuration
|
|
25
|
+
|
|
26
|
+
| Option | Type | Default | Description |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `baseUrl` | `string` | required | T4 instance REST API URL |
|
|
29
|
+
| `apiToken` | `string` | required | API authentication token |
|
|
30
|
+
| `language` | `string` | `'en'` | Default language for supported operations |
|
|
31
|
+
| `concurrency` | `number` | `10` | Maximum parallel HTTP requests |
|
|
32
|
+
|
|
33
|
+
### `baseUrl` requirements
|
|
34
|
+
|
|
35
|
+
`baseUrl` must be an absolute `http` or `https` URL. The client strips trailing slashes, so these values are equivalent:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
baseUrl: 'https://mysite.edu/terminalfour/rs'
|
|
39
|
+
baseUrl: 'https://mysite.edu/terminalfour/rs/'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The constructor rejects a value that is not a parseable absolute URL or uses a protocol other than `http` or `https`:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
new T4Client({ baseUrl: 'mysite.edu/terminalfour/rs', apiToken: '...' });
|
|
46
|
+
// Error: T4Client baseUrl "mysite.edu/terminalfour/rs" is not a valid absolute URL.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Important:** An `http://` URL logs a warning because every request sends the API token in an `Authorization` header, and plain HTTP transmits it in cleartext. Always use `https://` unless you are testing against a local instance.
|
|
50
|
+
|
|
51
|
+
## Keep the client server-side
|
|
52
|
+
|
|
53
|
+
The `apiToken` grants full read/write access to your T4 instance. Browser bundles are publicly readable, so sending the token to front-end code exposes it to every visitor, regardless of your application's authentication.
|
|
54
|
+
|
|
55
|
+
The SDK prevents this by throwing when code constructs a client in a browser. You cannot disable this check.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// Browser code: throws
|
|
59
|
+
new T4Client({ baseUrl, apiToken });
|
|
60
|
+
// Error: T4Client cannot be used in a browser. The apiToken is a full-privilege
|
|
61
|
+
// credential and anything in front-end code is publicly readable...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Construct the client in an API route, serverless function, or backend service. Return only the data the browser needs:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const t4 = new T4Client({
|
|
68
|
+
baseUrl: process.env.T4_BASE_URL!,
|
|
69
|
+
apiToken: process.env.T4_API_TOKEN!,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const items = await t4.section(482).content.list();
|
|
73
|
+
// Return `items` from your server endpoint. The token stays on the server.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The guard checks for both `window` and `window.document`, so it blocks only browser environments. Node, Deno, Bun, Cloudflare Workers, similar edge runtimes, and Web Workers continue to work.
|
|
77
|
+
|
|
78
|
+
## Set the language
|
|
79
|
+
|
|
80
|
+
Configure a client default when you need a language other than `en`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const t4 = new T4Client({
|
|
84
|
+
baseUrl: 'https://mysite.edu/terminalfour/rs',
|
|
85
|
+
apiToken: 'your-api-token',
|
|
86
|
+
language: 'fr',
|
|
87
|
+
concurrency: 5,
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For sections, content, and lists, the SDK resolves language in this order:
|
|
92
|
+
|
|
93
|
+
1. Per-call `{ language }` option
|
|
94
|
+
2. Client `language`
|
|
95
|
+
3. `'en'`
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
await t4.section(482).content.list(); // uses client default 'fr'
|
|
99
|
+
await t4.section(482).content.list({ language: 'de' }); // overrides this call
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Other resources use fixed languages:
|
|
103
|
+
|
|
104
|
+
| Resource | Language |
|
|
105
|
+
|---|---|
|
|
106
|
+
| Media items | `smxx` (language-independent) |
|
|
107
|
+
| Media categories and media library | `en` |
|
|
108
|
+
| Page layouts, content layouts, channels, groups, and users | `en` |
|
|
109
|
+
|
|
110
|
+
## Inspect the T4 instance
|
|
111
|
+
|
|
112
|
+
The client provides four read-only platform methods:
|
|
113
|
+
|
|
114
|
+
| Method | Returned data | Example property |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `about()` | T4 version, uptime, OS, Java, and servlet details | `info.t4.version` is `'8.4.2-FINAL'`; `info.t4.uptime` is a `Date`; `info.os.name` is `'Linux'`; `info.java.version` is `'11.0.18'` |
|
|
117
|
+
| `database()` | Database connection details | `db.name` is `'MySQL'`; `db.version` is `'8.0.32'` |
|
|
118
|
+
| `environment()` | Environment configuration | `env['max_upload_size']` is `'50000'` |
|
|
119
|
+
| `licence()` | Licence usage | `lic.remaining` may be `14121` content items |
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const info = await t4.about();
|
|
123
|
+
const db = await t4.database();
|
|
124
|
+
const env = await t4.environment();
|
|
125
|
+
const lic = await t4.licence();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Debug requests
|
|
129
|
+
|
|
130
|
+
Set `T4_DEBUG=1` to log HTTP requests and internal warnings:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
T4_DEBUG=1 node my-script.js
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Refresh cached data
|
|
137
|
+
|
|
138
|
+
The SDK caches content type templates, list values, element type definitions, and other lookup data for five minutes. You can clear every SDK cache after changing configuration during with:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
t4.clearCache();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
**Next:** [Sections](./sections.md)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Groups and Users
|
|
2
|
+
|
|
3
|
+
Use `t4.groups` to manage memberships and `t4.users` to manage accounts, authentication methods, and custom fields.
|
|
4
|
+
|
|
5
|
+
## Groups
|
|
6
|
+
|
|
7
|
+
### List and read groups
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const groups = await t4.groups.list();
|
|
11
|
+
// [{ id, name, description, membersCount, enabled, children, parentIds }]
|
|
12
|
+
|
|
13
|
+
const group = await t4.groups.get(1);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A full group includes `name`, `description`, `enabled`, `emailAddress`, `children`, and `members`. `children` contains child group IDs. Each member includes `id`, `username`, `firstName`, `lastName`, `emailAddress`, and `userLevel`.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
for (const member of group.members) {
|
|
20
|
+
console.log(
|
|
21
|
+
member.id,
|
|
22
|
+
member.username,
|
|
23
|
+
member.firstName,
|
|
24
|
+
member.lastName,
|
|
25
|
+
member.emailAddress,
|
|
26
|
+
member.userLevel,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Create a group
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
await t4.groups.create({
|
|
35
|
+
name: 'Editors',
|
|
36
|
+
description: 'Content editors',
|
|
37
|
+
members: [38, 61], // user IDs; the SDK resolves full user objects
|
|
38
|
+
enabled: true,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A group requires at least one member.
|
|
43
|
+
|
|
44
|
+
### Update a group
|
|
45
|
+
|
|
46
|
+
#### Direct update
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
await t4.groups.update(1, { name: 'Renamed', enabled: false });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Mutable item
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const group = await t4.groups.get(1);
|
|
56
|
+
group.name = 'Renamed';
|
|
57
|
+
group.addMembers([62, 63]);
|
|
58
|
+
group.removeMembers([66]);
|
|
59
|
+
await group.save();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
After `save()`, the SDK refreshes `members` to reflect the changes.
|
|
63
|
+
|
|
64
|
+
### Delete a group
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
await t4.groups.delete(42);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Users
|
|
71
|
+
|
|
72
|
+
### List users
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const users = await t4.users.list();
|
|
76
|
+
// [{ id, username, firstName, lastName, emailAddress, userLevel, enabled, accountLocked, lastLogin }]
|
|
77
|
+
|
|
78
|
+
const admins = await t4.users.list({ userLevel: 'admin' });
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
User levels are `'admin'`, `'power-user'`, `'moderator'`, `'contributor'`, and `'visitor'`.
|
|
82
|
+
|
|
83
|
+
### Get a user
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const user = await t4.users.get(30);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Property | Example or meaning |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `username` | Account username |
|
|
92
|
+
| `firstName`, `lastName` | User's name |
|
|
93
|
+
| `emailAddress` | Email address |
|
|
94
|
+
| `userLevel` | `'contributor'` |
|
|
95
|
+
| `defaultLanguage` | `'en'` |
|
|
96
|
+
| `enabled` | Whether the account is enabled |
|
|
97
|
+
| `lastLogin` | `Date` or `null` when the user has never logged in |
|
|
98
|
+
| `groups` | `[{ id: 1, name: 'Editors' }]` |
|
|
99
|
+
| `customFields` | `{ Department: 'Engineering' }` or `null` |
|
|
100
|
+
|
|
101
|
+
### Authentication methods
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
console.log(user.authMethods);
|
|
105
|
+
// {
|
|
106
|
+
// local: true,
|
|
107
|
+
// ldap: { enabled: true, identifier: 'uid=jsmith,ou=people,dc=example,dc=com' },
|
|
108
|
+
// saml: false,
|
|
109
|
+
// cas: false,
|
|
110
|
+
// remoteuser: false,
|
|
111
|
+
// }
|
|
112
|
+
|
|
113
|
+
user.authMethods.saml = { enabled: true, identifier: 'saml-user-id' };
|
|
114
|
+
await user.save();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `local` and `remoteuser` are booleans and never have identifiers.
|
|
118
|
+
- `ldap`, `saml`, and `cas` are `boolean | { enabled: boolean; identifier: string }`.
|
|
119
|
+
|
|
120
|
+
### Create a user
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await t4.users.create({
|
|
124
|
+
username: 'new.user',
|
|
125
|
+
firstName: 'New',
|
|
126
|
+
lastName: 'User',
|
|
127
|
+
emailAddress: 'new@example.com',
|
|
128
|
+
password: 'SecureP@ss123!',
|
|
129
|
+
userLevel: 'contributor', // optional; default: 'contributor'
|
|
130
|
+
defaultLanguage: 'en', // optional; default: 'en'
|
|
131
|
+
enabled: true, // optional; default: true
|
|
132
|
+
authMethods: { local: true }, // optional; default: { local: true }
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Update a user
|
|
137
|
+
|
|
138
|
+
#### Direct update
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
await t4.users.update(30, {
|
|
142
|
+
firstName: 'Updated',
|
|
143
|
+
userLevel: 'moderator',
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Mutable item
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const user = await t4.users.get(30);
|
|
151
|
+
user.firstName = 'Updated';
|
|
152
|
+
user.password = 'NewP@ssword456!';
|
|
153
|
+
if (user.customFields) {
|
|
154
|
+
user.customFields['Department'] = 'Marketing';
|
|
155
|
+
}
|
|
156
|
+
await user.save();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Delete a user
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
await t4.users.delete(68);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
**Previous:** [Lists](./lists.md) · **Next:** [Page Layouts](./page-layouts.md)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Handlebars
|
|
2
|
+
|
|
3
|
+
Use `t4.handlebars.helpers` for custom helper functions and `t4.handlebars.partials` for reusable template fragments.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
t4.handlebars.helpers
|
|
7
|
+
t4.handlebars.partials
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Helpers and partials are stored as content items in hidden sections. Both resources expose `list()`, `get(name)`, `create()`, `update(name)`, `delete(name)`, and `purge(name)`. Any method that accepts a name also accepts a numeric ID.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
- [Shared behavior](#shared-behavior)
|
|
15
|
+
- [Helpers](#helpers)
|
|
16
|
+
- [Partials](#partials)
|
|
17
|
+
- [Errors and operational notes](#errors-and-operational-notes)
|
|
18
|
+
|
|
19
|
+
## Shared behavior
|
|
20
|
+
|
|
21
|
+
| Operation | Behavior |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `list()` | Returns summary objects with `id`, `name`, and `lastModified` |
|
|
24
|
+
| `get(nameOrId)` | Returns the full mutable item, including `code` |
|
|
25
|
+
| `create({ name, code })` | Requires both values, enforces a unique name, and creates an approved item |
|
|
26
|
+
| `update(nameOrId, data)` | Applies a direct update and saves with approved status |
|
|
27
|
+
| `save()` | Saves a mutable item with approved status |
|
|
28
|
+
| `delete(nameOrId)` | Soft deletes the item |
|
|
29
|
+
| `purge(nameOrId)` | Permanently removes the item |
|
|
30
|
+
|
|
31
|
+
## Helpers
|
|
32
|
+
|
|
33
|
+
Custom helpers are available in content layouts.
|
|
34
|
+
|
|
35
|
+
### List and get helpers
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const helpers = await t4.handlebars.helpers.list();
|
|
39
|
+
// [{ id: 100, name: 'formatDate', lastModified: Date }, ...]
|
|
40
|
+
|
|
41
|
+
const helper = await t4.handlebars.helpers.get('formatDate');
|
|
42
|
+
console.log(helper.name); // 'formatDate'
|
|
43
|
+
console.log(helper.code); // 'module.exports = function(date) { ... }'
|
|
44
|
+
console.log(helper.lastModified); // Date object
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`list()` returns summaries. Call `get(name)` for the code. You can also retrieve the same helper by ID with `t4.handlebars.helpers.get(100)`.
|
|
48
|
+
|
|
49
|
+
### Create a helper
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const helper = await t4.handlebars.helpers.create({
|
|
53
|
+
name: 'truncate',
|
|
54
|
+
code: 'function(context, options) { return context.substring(0, options.hash('len')); }',
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Direct update
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const updated = await t4.handlebars.helpers.update('formatDate', {
|
|
62
|
+
code: 'function(context, options) { return new Date(context).toISOString(); }',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Mutable item
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const helper = await t4.handlebars.helpers.get('formatDate');
|
|
70
|
+
helper.name = 'formatDateISO';
|
|
71
|
+
helper.code = 'function(context, options) { return new Date(context).toISOString().split("T")[0]; }';
|
|
72
|
+
await helper.save();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Delete or purge a helper
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
await t4.handlebars.helpers.delete('truncate'); // soft delete
|
|
79
|
+
await t4.handlebars.helpers.purge('truncate'); // permanent removal
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Partials
|
|
83
|
+
|
|
84
|
+
Partials are reusable template fragments included in content layouts with `{{> partialName}}`.
|
|
85
|
+
|
|
86
|
+
### List and get partials
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const partials = await t4.handlebars.partials.list();
|
|
90
|
+
// [{ id: 200, name: 'header', lastModified: Date }, ...]
|
|
91
|
+
|
|
92
|
+
const partial = await t4.handlebars.partials.get('header');
|
|
93
|
+
console.log(partial.name); // 'header'
|
|
94
|
+
console.log(partial.code); // '<header><h1>{{sectionName}}</h1></header>'
|
|
95
|
+
console.log(partial.lastModified); // Date object
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Create a partial
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const partial = await t4.handlebars.partials.create({
|
|
102
|
+
name: 'footer',
|
|
103
|
+
code: '<footer><p>© {{channelName}}</p></footer>',
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Direct update
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const updated = await t4.handlebars.partials.update('header', {
|
|
111
|
+
code: '<header class="main"><h1>{{sectionName}}</h1></header>',
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Mutable item
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const partial = await t4.handlebars.partials.get('header');
|
|
119
|
+
partial.name = 'site-header';
|
|
120
|
+
partial.code = '<header class="main"><h1>{{sectionName}}</h1></header>';
|
|
121
|
+
await partial.save();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Delete or purge a partial
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
await t4.handlebars.partials.delete('footer'); // soft delete
|
|
128
|
+
await t4.handlebars.partials.purge('footer'); // permanent removal
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Errors and operational notes
|
|
132
|
+
|
|
133
|
+
- A duplicate name on create produces an error.
|
|
134
|
+
- A missing name produces an error that lists available names.
|
|
135
|
+
- If multiple items have the same name, the SDK reports their IDs so you can use a numeric ID.
|
|
136
|
+
- Names are the primary interface; IDs provide a fallback when a name is ambiguous.
|
|
137
|
+
- Every create, update, and save forces approved status so Handlebars can use the item immediately.
|
|
138
|
+
- Operations always use language `en`, regardless of the client's configured language. Helpers and partials are language-independent.
|
|
139
|
+
- Hidden section and content type IDs are read from configuration endpoints on first use and cached for five minutes.
|
|
140
|
+
- `t4.clearCache()` invalidates those cached configuration values.
|
|
141
|
+
- Helpers use a `Function Code` element; partials use a `Code` element. The SDK handles the difference.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
**Previous:** [Navigation](./navigation.md) · **Next:** [Error Handling](./error-handling.md)
|
package/docs/lists.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Lists
|
|
2
|
+
|
|
3
|
+
Use `t4.lists` to manage list definitions and their items.
|
|
4
|
+
|
|
5
|
+
## List and read lists
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
const lists = await t4.lists.list();
|
|
9
|
+
// [{ id: 71, name: 'Sizes', description: 'Size options' }]
|
|
10
|
+
|
|
11
|
+
const list = await t4.lists.get(71);
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Property | Example |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `name` | `'Sizes'` |
|
|
17
|
+
| `description` | List description |
|
|
18
|
+
| `isForcedLanguage` | `false` |
|
|
19
|
+
| `isDefaultLanguage` | `false` |
|
|
20
|
+
| `primaryGroup` | `0` |
|
|
21
|
+
| `sharedGroups` | `[]` |
|
|
22
|
+
| `items` | Item definitions keyed by name |
|
|
23
|
+
|
|
24
|
+
Items are keyed by name:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
console.log(list.items);
|
|
28
|
+
// {
|
|
29
|
+
// Large: { name: 'Large', value: 'lg', selected: true },
|
|
30
|
+
// Small: { name: 'Small', value: 'sm', selected: false },
|
|
31
|
+
// }
|
|
32
|
+
|
|
33
|
+
list.items['Large'].value = 'updated';
|
|
34
|
+
list.items['Large'].selected = false;
|
|
35
|
+
await list.save();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Create a list
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const newList = await t4.lists.create({
|
|
42
|
+
name: 'Priorities',
|
|
43
|
+
description: 'Priority levels',
|
|
44
|
+
items: [
|
|
45
|
+
{ name: 'High', value: 'high', selected: true },
|
|
46
|
+
{ name: 'Medium', value: 'medium' },
|
|
47
|
+
{ name: 'Low', value: 'low' },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`isForcedLanguage` and `isDefaultLanguage` both default to `false`. They cannot both be `true`; the SDK validates this during create and save.
|
|
53
|
+
|
|
54
|
+
## Update a list
|
|
55
|
+
|
|
56
|
+
### Direct update
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
await t4.lists.update(71, {
|
|
60
|
+
name: 'Renamed',
|
|
61
|
+
description: 'Updated',
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Mutable item
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const list = await t4.lists.get(71);
|
|
69
|
+
list.name = 'Renamed';
|
|
70
|
+
list.description = 'Updated';
|
|
71
|
+
await list.save();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Add or remove items
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const list = await t4.lists.get(71);
|
|
78
|
+
|
|
79
|
+
list.addItem({ name: 'Extra Large', value: 'xl', selected: false });
|
|
80
|
+
list.removeItem('Small');
|
|
81
|
+
await list.save();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Add `sublistId` when an item opens a sublist:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
list.addItem({ name: 'Soccer', value: 'soccer', sublistId: 72 });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Delete a list
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
await t4.lists.delete(71);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
**Previous:** [Content Types](./content-types.md) · **Next:** [Groups & Users](./groups-and-users.md)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Media Types
|
|
2
|
+
|
|
3
|
+
Media types define permitted file extensions, whether files are binary or text-based, and which layouts can render them.
|
|
4
|
+
|
|
5
|
+
## List and read media types
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
const types = await t4.mediaTypes.list();
|
|
9
|
+
for (const mediaType of types) {
|
|
10
|
+
console.log(mediaType.name, mediaType.extensions, mediaType.defaultLayout);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const images = await t4.mediaTypes.get(1);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`get()` returns a mutable `MediaType`:
|
|
17
|
+
|
|
18
|
+
| Property | Type | Mutable | Description |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| `id` | `number` | no | Media type ID |
|
|
21
|
+
| `name` | `string` | yes | Display name, such as `'Image'` |
|
|
22
|
+
| `extensions` | `string[]` | yes | Permitted extensions, such as `['gif', 'jpg', 'jpeg', 'png', 'svg', 'webp']` |
|
|
23
|
+
| `binary` | `boolean` | yes | Whether files are binary rather than text-based |
|
|
24
|
+
| `parseForTags` | `boolean` | yes | Whether T4 parses tags; valid only when `binary` is `false` |
|
|
25
|
+
| `maxSize` | `string \| null` | yes | Formatted maximum size, such as `'5.0 KB'`, or `null` for unlimited |
|
|
26
|
+
| `layouts` | `MediaTypeLayout[]` | yes | Layouts with `name` and `default` |
|
|
27
|
+
| `defaultLayout` | `string` | yes | Default layout name, such as `'image/normal'` |
|
|
28
|
+
|
|
29
|
+
## Create a media type
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
const video = await t4.mediaTypes.create({
|
|
33
|
+
name: 'Video',
|
|
34
|
+
extensions: ['mp4', 'webm', 'mov'],
|
|
35
|
+
binary: true,
|
|
36
|
+
maxSize: '50 MB', // also accepts 52428800 bytes or null for unlimited
|
|
37
|
+
layouts: [
|
|
38
|
+
{ name: 'video/*', default: false },
|
|
39
|
+
{ name: 'video/looping', default: true },
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Instead of setting `default: true`, pass `defaultLayout`:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
await t4.mediaTypes.create({
|
|
48
|
+
name: 'Video',
|
|
49
|
+
extensions: ['mp4', 'webm'],
|
|
50
|
+
binary: true,
|
|
51
|
+
layouts: [
|
|
52
|
+
{ name: 'video/*', default: false },
|
|
53
|
+
{ name: 'video/looping', default: false },
|
|
54
|
+
],
|
|
55
|
+
defaultLayout: 'video/looping',
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Update a media type
|
|
60
|
+
|
|
61
|
+
### Direct update
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
await t4.mediaTypes.update(1, {
|
|
65
|
+
name: 'Image Updated',
|
|
66
|
+
extensions: ['gif', 'jpg', 'jpeg', 'png', 'svg', 'webp', 'avif'],
|
|
67
|
+
maxSize: '10 MB',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Mutable item
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const mediaType = await t4.mediaTypes.get(1);
|
|
75
|
+
mediaType.name = 'Image Updated';
|
|
76
|
+
mediaType.extensions = [...mediaType.extensions, 'avif'];
|
|
77
|
+
mediaType.maxSize = '10 MB';
|
|
78
|
+
mediaType.defaultLayout = 'image/*';
|
|
79
|
+
await mediaType.save();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Set the maximum size
|
|
83
|
+
|
|
84
|
+
`maxSize` accepts:
|
|
85
|
+
|
|
86
|
+
- A formatted string: `'2 KB'`, `'5 MB'`, or `'512 B'`
|
|
87
|
+
- A number of bytes: `2048` or `5242880`
|
|
88
|
+
- `null` for unlimited size
|
|
89
|
+
|
|
90
|
+
On read, `maxSize` is a formatted string or `null`.
|
|
91
|
+
|
|
92
|
+
## Validation rules
|
|
93
|
+
|
|
94
|
+
1. `parseForTags` cannot be `true` when `binary` is `true`. Tag parsing applies only to text-based media such as CSS, JavaScript, and PHP.
|
|
95
|
+
2. Every media type requires at least one default layout.
|
|
96
|
+
|
|
97
|
+
For a non-binary type, enable `parseForTags` to let T4 process tags in the file content:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
await t4.mediaTypes.create({
|
|
101
|
+
name: 'CSS Stylesheet',
|
|
102
|
+
extensions: ['css'],
|
|
103
|
+
binary: false,
|
|
104
|
+
parseForTags: true,
|
|
105
|
+
layouts: [{ name: 'css/*', default: true }],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
**Previous:** [Media](./media.md) · **Next:** [Channels](./channels.md)
|