Skip to content

Organization

Two system collections handle non-content organizational state in CairnCMS: folders structure the file library into a tree, and presets store the per-user, per-role, or global layout configurations that the admin app uses for collection views and saved bookmarks. Both follow the standard CRUD shape with no bespoke endpoints.

A folder organizes files in the file library. The collection is a self-referencing tree: each row optionally points at a parent folder, and directus_files.folder references a folder. Files in CairnCMS are not strictly required to live in a folder; folders are an organizational convenience for the admin app, not a structural requirement.

MethodPathPurpose
GET/foldersList folders.
SEARCH/foldersRead folders with the request body.
GET/folders/<id>Read a single folder.
POST/foldersCreate one or many folders.
PATCH/foldersUpdate many folders (three body shapes).
PATCH/folders/<id>Update a single folder.
DELETE/foldersDelete many folders.
DELETE/folders/<id>Delete a single folder.
  • id (UUID) — primary key.
  • parent — UUID of the parent folder, or null for top-level folders. The relation is self-referencing on directus_folders, so a folder’s parent must already exist when the row is created.
  • name — display name. Folders within the same parent should have unique names by convention; the platform does not enforce it at the schema level.

The folder tree is built client-side by traversing the parent references. Cycles are not validated; do not point a folder at one of its descendants.

Deleting a folder does not cascade to child folders or to the files inside it. directus_files.folder is set to null on file rows that referenced the deleted folder, and child folders become top-level (their parent becomes null). To delete a folder and its contents, batch-delete the children explicitly first.

A preset stores a layout configuration for a collection: which fields are visible, the active filter, the sort order, the active layout type, and the search term. Presets serve two related purposes:

  • View defaults — the layout state a user sees when they open a collection. The admin app picks the most-specific preset that applies (user, then role, then global) and uses it as the starting point.
  • Bookmarks — saved layout configurations a user can switch between. A row with a non-null bookmark value shows up in the bookmarks selector.
MethodPathPurpose
GET/presetsList presets.
SEARCH/presetsRead presets with the request body.
GET/presets/<id>Read a single preset.
POST/presetsCreate one or many presets.
PATCH/presetsUpdate many presets (three body shapes).
PATCH/presets/<id>Update a single preset.
DELETE/presetsDelete many presets.
DELETE/presets/<id>Delete a single preset.
  • id (auto-incrementing integer) — primary key.
  • bookmark (string) — display name when the preset is a saved bookmark. null for view-default presets that should not appear in the bookmarks selector.
  • icon, color — bookmark display metadata.
  • collection — the collection this preset applies to.
  • layout — the active layout (tabular, cards, calendar, map, or any custom layout-extension key).
  • layout_query — JSON object holding the layout-specific query state (sort, fields, depth, and so on).
  • layout_options — JSON object holding layout-specific UI options (column widths for tabular, card size for cards, and so on).
  • filter — JSON filter expression in the standard query DSL.
  • search — full-text search term.
  • refresh_interval — auto-refresh interval in seconds, or null for no auto-refresh.
  • user — UUID of the user the preset applies to. Null means the preset applies regardless of user.
  • role — UUID of the role the preset applies to. Null means the preset applies regardless of role.

The combination of user and role determines a preset’s scope:

  • User-specific (user set, role set or null) — applies only to that user. Bookmarks the user creates land here by default.
  • Role-specific (user null, role set) — applies to every user in that role.
  • Global (user null, role null) — applies to every user. Operators use this for default views that should apply across the organization.

Resolution is most-specific-wins: a user-specific preset overrides a role-specific one, which overrides a global one for the same collection. The resolution happens client-side in the admin app, not on the server. The /presets API returns whatever rows match the request’s filter; the consumer is responsible for picking the right row when more than one applies. Custom clients that surface user-facing layouts should follow the same rule (fetch user, role, and global rows, pick most-specific).

The bookmark field is the discriminator. Presets with a non-null bookmark show up in the admin app’s bookmarks selector and represent named saved configurations. Presets with bookmark: null are view defaults: the platform applies them silently when a user opens the collection.

A user can have many bookmarks for the same collection (one per saved view) but typically only one view default. Operators creating role or global view defaults should leave bookmark: null so the configuration applies as a default rather than appearing in users’ bookmark lists.

Folders are open to app-access roles by default. The platform’s app-access minimum permissions include create, read, update, and delete on directus_folders for any role with app_access: true, so app users can organize the file library without operators having to grant explicit permissions. Permissions on directus_folders follow the same model as any other system collection if you need to scope access more tightly than the default; the minimum permissions are projected at read time and do not appear as rows in /permissions.

Presets are scoped at the row level by user and role. The recommended permission shape for non-admin app-access roles:

  • Read — allow when user matches $CURRENT_USER or role matches $CURRENT_ROLE or both are null. This lets the user see their own bookmarks plus any role-level or global view defaults.
  • Create / Update / Delete — allow when user matches $CURRENT_USER. This lets the user manage their own bookmarks without being able to modify role-level or global presets.

Operators who want to create role-level or global presets need either admin access or a custom permission that allows writes with user: null. Treat that grant as a deliberate decision; a role with permission to write user: null presets can change defaults seen by every user in that role (or every user in the project, for global presets).

Both collections are exposed on /graphql/system with the standard generated CRUD shape (folders, folders_by_id, create_folders_item, etc.; same for presets). The query DSL options work the same way as in REST; see Filters and queries / GraphQL.

  • Files — the file collection that uses directus_folders.id to organize uploads.
  • Filters and queries — the query DSL stored in preset filter and layout_query fields.
  • Filters — operator-side reference for designing filters that end up serialized into preset rows.