Relatude Help

Commands

A command is POST {Path}/command, with the token, and the JSON body { type, payload }:

{
  "type": "query",
  "payload": {
    "query": "IProduct.Where(n => n.Price < @P0).OrderBy(n => n.Name).Page(0, 20)",
    "parameters": [{ "name": "@P0", "type": "decimal", "value": 500 }],
    "context": { "culture": "en-US" }
  }
}

It answers { "kind": "nodes", "items": [ … ], "totalCount": 42, "pageIndex": 0, "pageSize": 20, … }.

Pass values as typed parameters, never as literals. The query language has no literals for decimals or dates, and n.Price > 100 against a decimal property compares with 0. A parameter is { name, type, value }. Its type is string, int, long, double, float, decimal, bool, datetime, datetimeoffset, timespan, guid or geo, or an array such as guid[]. The SDK's query builder does this for you, and its toRequest() shows the text it writes (chapter 19).

Types, properties and relation ends are named by CodeName or by id. CodeNames are case-sensitive.

Reading and querying

Command Payload Answers
whoami { userId, anonymous, name, email, groups, admin }
schema the node types with their properties, and the cultures
query { query, parameters } the result, with its kind (Nodes and values on the wire)
get { ids } { items }: the nodes the user can read, in the order asked for
exists { id } whether the node exists and the user can read it
count { type? } how many nodes of the type and its subtypes the user can read
get-related { id, property } { items }: the related nodes, in the relation's order
get-url { id } the node's URL
parse-url { url } { id, culture } of the page the URL addresses, or null
file-url { id, property, adjustment? } a file's URL, or that of a resized or converted image
content-link { id, property?, adjustment? } { link, url }: a stored link (#…) to a node or its file, to put in HTML
content-links { links } { items }: what each stored link points at, or that it is missing or restricted
node-meta { id } what the store keeps beside the values: address, URL, dates, revision, culture, site, access, release, expiry
meta-choices { groups, collections }: the access groups and sites a node can be given
timestamp the database's transaction timestamp, which moves with every write
commands every command the server knows

schema gives a client what it needs to build an editor. Each type has its icon (as SVG), title, description and canBeDraft. Each property has its kind and defaultValue, and one that holds other nodes has display: List, Table, Inline, Cards or Thumbnail.

Writing nodes

Command Payload Answers
insert { type, id?, values, ifNotExists?, draft? } the stored node; with draft, a draft readers do not see
update { id, values, force?, ifExists? } the node; only the properties given are written, and null resets one
upsert { type, id, values } the node, updated or inserted
delete { ids, ifExists? } { deleted }
relate, unrelate, set-relation, clear-relation { id, property, to } true; to is one id or a list
move-relation { id, property, items, position \| offset \| before \| after } true; position is "top" or "bottom"
set-relation-order { id, property, items } true
reset-property { id, property } the node, the property back at its default
add-to-property, multiply-property { id, property, value } the node, a number changed in the store
change-type { id, type } the node, as a node of the new type
update-address, update-display-name { id, address }, { id, displayName } the node
update-meta { id, meta } the node, with its access, site, release, expiry or deleted changed
transaction { actions: [{ op, … }] } { results }: each action's answer, in order

Every write runs through NodeStore.Execute, so the store's transaction plugins run exactly as they do for a write from C#.

transaction runs its actions in one transaction: all of them happen, or none. Each action is a write command, named by op, with that command's payload. An action can use a node an earlier one inserts:

{
  "type": "transaction",
  "payload": {
    "actions": [
      { "op": "insert", "type": "INewsList", "id": "0f8fad5b-d9cb-469f-a165-70867728950e", "values": { "Name": "News" } },
      { "op": "insert", "type": "IArticle", "values": { "Name": "Hello world", "Parent": "0f8fad5b-d9cb-469f-a165-70867728950e" } }
    ]
  }
}
  • insert fills the properties it is not given with the type's defaults. In a context with a culture, the node's first version is in that culture. With a collection, the node is in that site.
  • A relation end takes an id, or a list of ids for a "many" end, and is set to exactly those.
  • update-meta's meta takes readAccess, editViewAccess, editAccess, publishAccess, collectionId, releaseUtc, expireUtc and deleted. Null clears a value: an access group then comes from the type's or the database's default, and a node with no collectionId is in no site.

The content tree

The CMS interface's content tree runs on these commands. They read a one-to-many relation as parents and children: the one named in relation (class name, full name or id), else the CMS setting Content.Tree, else RTree.

Command Payload Answers
tree { relation?, parent?, search?, root?, hidden? } { relation, items, sites?, root? }: a parent's children, the top, or the nodes whose names hold search
tree-path { id, relation? } { relation, path, site? }: the ids from the top down to the node's parent
tree-move { id \| ids, parent?, before?, after?, children?, relation? } { id, ids }: the nodes moved
tree-copy as tree-move a process that copies; its result is { id, copied, filesNotCopied }
tree-count { id \| ids, children?, relation? } { nodes, warnAt }: how many nodes a copy would make, or a delete take
tree-count-below { ids, relation? } a process that counts what is below each node; its result is { counted, below }
tree-delete { ids, children?, relation? } { deleted }, or { process } for a large delete
tree-create { type, name?, parent?, after?, relation?, values?, draft? } { id }: a new node in the tree, made as insert makes it
  • Items are { $id, $type, $displayName, $children, $status }, $children counting the children the tree shows. A search result also has $path, the way down to it.
  • Sites. When sites (ISite) can be in the relation, as in RTree, the tree is one site's: root picks it. Only a site can go at the top.
  • Hidden pages. A page whose ShowInTree is off is left out, with everything below it. hidden: true lists them too, marked $hidden.
  • Moving. A node goes into parent as its last child, before or after a sibling, or to the top. With children, the default, its branch goes with it. Without, its children take its place.
  • Copying. A copy has the node's values, access and files, and the links it can share: the "one" end of a one-to-many relation, and many-to-many links. A copy that is cancelled or fails removes what it made. Nothing limits its size, but the interface counts a copy first and warns past CmsApiOptions.TreeCopyWarnAt (10 000 nodes; 0 never warns).
  • Deleting. tree-delete takes each node with everything below it, and is refused (403) when the user cannot read some of that. With children: false it deletes one node, and its children take its place.
  • Addresses. A node moved, copied or created gets an address the store makes unique, such as news-2. tree-create makes it from the node's name.

Revisions, cultures and sites

Command Payload Answers
revisions { id } { id, enabled, items }: the node's revisions in every culture
create-revision { id, revision?, type?, culture? } { id, revision }: a copy of a revision, a draft unless type says otherwise
set-revision-type { id, revision?, type } { id, revision }: the revision made Published, Preliminary (a draft) or Archived
set-revision-culture { id, revision?, culture } { id, revision }: the revision moved to another culture, "" for none
delete-revision { id, revision } { id, revision }; 409 for the node's last revision
cultures [{ code, name, englishName }]: the cultures content can be written in
add-culture { code, site? } { code, name }; with site, the culture is also one of the site's languages
sites { supported, types, items }: the sites the user can read, each with its cultures
history { id, revision?, max? } { id, revision, items, more }: a revision's earlier versions, newest first
history-version { id, at?, revision? } the node as the version at (from history) had it
history-restore { id, at, revision? } { id, revision, restored }: that version's values, written back
preview { instance, revisions, versions?, now? } { instance, parameter } (Previews of drafts)

revision can be left out for a node with one version. Publishing a revision archives the culture's published one. history-restore writes values only, not files, passwords or relations, into the revision the context's revisions names.

// a Norwegian draft, copied from the English revision; answers the new revision's id
{ "type": "create-revision", "payload": { "id": "…", "revision": "…", "culture": "nb-NO" } }

// publish it
{ "type": "set-revision-type", "payload": { "id": "…", "revision": "…", "type": "Published" } }

File properties

Command Payload Answers
file-info { id, property } the file as the user reads it, or null
file-copy { id, property, from: { id, property } } the file: another node's file, copied into this property on the server
file-delete { id, property } true: the file is taken out of the property

A file is never a value, and update refuses one. Upload it with POST {Path}/upload?id=…&property=…&name=…, the file as the body, up to CmsApiOptions.MaxUploadBytes (1 GB). Add context=, a payload context as JSON, to upload into a particular revision; the file commands use the context's revision too. file-copy sends no bytes through the client. file-delete leaves the stored file in place, as another revision or a copy of the node may hold it, and the store's clean-up of unreferenced files removes it later.

Processes and maintenance

Command Payload Answers
processes { items }: the user's processes, oldest first
process-pause, process-resume, process-cancel { id } the process: held at its next checkpoint, let go on, or stopped
process-dismiss { id } the finished process, forgotten
maintenance { canRun, tasks }: the maintenance tasks, each { id, title, description, asks, warning }
maintenance-run { task, fix?, site?, culture? } a process that checks, or with fix also repairs

The process commands answer 404 for another user's process, and 400 for one that cannot do what is asked. maintenance-run is for administrators (403), runs one task at a time per user (409), and needs the site and culture the task asks for. Its result counts the nodes it checked, found, fixed and failed on, and items names the first 100 found, each with its problem. Processes shows how to write a process, and Sites describes the tasks.

Editing together

Command Payload Answers
collab-join { nodeId, revision?, connectionId, participant, live? } { first, version, participants }: the caller has the node open
collab-live { nodeId, revision?, participant, live } { first, version, participants }: starts or stops editing together
collab-leave { nodeId, revision?, participant } null: the caller has closed the node
collab-send { nodeId, revision?, participant, messages: [{ data, to? }] } null: the messages passed to the other live participants

Each node blade of the CMS interface joins its node's room, so it knows who else has the node open. A participant is an id the client chooses. connectionId is the id the event stream sent in connected. A node with revisions has a room per revision.

first is true for a participant that went live while no one else was: it brings the document. The server keeps no document. It passes the messages (base64, at most 8 MB per send) to the other live participants, or to the one named in to, and never back to the sender's connection. The CMS interface sends Yjs updates this way. Participants leave by themselves when their stream closes.

Skins, forms and releases

Command Payload Answers
skins { default, skins, canEdit, ai }: the installation's own skins of the interface, and its default
skin-save, skin-delete, skin-default { skin }, { key }, { key } the skin saved; null; { default }
skin-logo { data } { logo }: an uploaded logo picture, the value a logo token takes
skin-generate { prompt, tokens, current } an unsaved skin made by the AI; 503 without an AI
form-layout { type, layout } a type's form layout, saved; null for the default form
ui-release, ui-releases the interface release served and chosen; the releases to choose from
ui-release-set { channel, version } what the installation serves after the change
cms-modules { modules, interfacePlugins } (chapter 18)

The commands here that change something are for administrators. Every skin value is checked before it is saved (Skins), and a skin cannot take a built-in skin's key.