Page fields
v0.0.1
This page lists every field of a page in Mishka CMS: its label on the form, the name it is stored under, its default and the rules the CMS holds it to. For the steps that use them, see Build a page in the visual editor. The full page form is a Mishka Gervaz form, and the trimming and length limits on its text fields are GuardedStruct derives on the page record.
Where the fields appear
- Pages list, + Add Page menu: Create with the full form and Fill in the form, then open the editor show all fields. The second opens the visual editor after it saves.
- Pages list, View, Configuration tab: all fields except Site. The pencil icon, Edit, on the row opens the same tab.
- Visual editor, page settings, General tab: Title, Path, This page shows each, Layout and Permissions. The SEO tab is a separate form. See Set a page's SEO and share card.
- The editor's Name your page dialog asks for Title and Path, plus Site for a master, and saves the page as inactive.

The Configuration tab holds the whole page form.
Name and address
| Field | Stored as | Type | Default | Rules |
|---|---|---|---|---|
| Title | title | Text | None | Required. 1 to 200 characters, trimmed. What the page is called in its own language. |
| Path | path | Text | None | Required. See “Path rules” below. Unique within the site. |
| Site | site_id | Search | None | A master only, when creating. Required. A page belongs to one site for good. |
Layout, records and access
| Field | Stored as | Type | Default | Rules |
|---|---|---|---|---|
| Layout | layout_id | Search | No Layout | A layout of the page's site, or a shared one. |
| This page shows each | extra["record"] | Choice | Custom page: one page, one address | The kinds of record that installed extensions offer, grouped by extension. The extension then fills the page's SEO, and the record's own permissions apply. |
| Permissions | permissions | Search, several | Empty | Permission names that may open the page. Each is trimmed and lowercased, and repeats are dropped. Empty means everyone. Leave it empty on a page that shows records. |
Settings
| Field | Stored as | Type | Default | Rules |
|---|---|---|---|---|
| Section | section | Text | None | A free label, such as blog or docs. The list shows it under the path. |
| Format | format | Choice | HEEx | HEEx, EEx or Markdown. |
| Priority | priority | Number | 0 | 0 to 100. Breaks a tie between two patterns that match an address equally well. The higher number wins. |
| Language | language | Combobox | en | A language code, lowercased, up to 10 characters, such as fa or pt-br. The help line under the field reads “Add this code under Settings → Languages as well, or its URL answers 404.” Add the code to the site's Languages setting as well. |
| Language Group | language_group_id | Search | New Language Group (auto-generate) | The group of editions the page belongs to. Choices are the pages of the same site, shown as name and language. A page that names none starts a group of its own. One page per language in a group. |
| Active | active | Switch | On | Only active pages are compiled and routed. |
| Precompile | precompile | Switch | Off | On compiles the page when the application starts. Off compiles it on first use. |
Content and SEO
| Field | Stored as | Type | Default | Rules |
|---|---|---|---|---|
| Template | template | Text area | An empty starter | At least 3 characters once stored. A blank template on create is replaced by one empty container. On update, a blank template is refused. |
| Description | description | Text | None | Up to 500 characters. Shown by search engines and link previews. |
| SEO Tags | seo_tags | List | Empty | See “SEO tag entries” below. A title replaces the page's title. Meta and link tags each replace the default with the same name. A value may name a variable such as {{page.title}}. |
| Schema (JSON) | schema | List | Empty | Structured data objects, such as [{"@type": "WebPage", "name": "My Page"}]. Empty uses the default WebPage. |
Data and code
| Field | Stored as | Type | Default | Rules |
|---|---|---|---|---|
| Content (JSON) | content | JSON object | {} | Dynamic content data for the page. |
| Extra (JSON) | extra | JSON object | {} | Metadata. The CMS reads the keys in “Keys of Extra” below. |
| State (starting assigns) | state | List | Empty | See “State entries” below. Holds Elixir. |
| Events (handle_event) | events | List | Empty | See “Event and info entries” below. Holds Elixir. |
| Infos (handle_info) | infos | List | Empty | See “Event and info entries” below. Holds Elixir. |
| Resource Links | resource_links | List | Empty | See “Resource link entries” below. |
| Helper Functions | helpers | List | Empty | See “Helper entries” below. Holds Elixir. |
| File Uploads | uploads | List | Empty | See “Upload entries” below. The box sits after all the others. |
Path rules
- Starts with
/and is 1 to 255 characters long. - Holds only letters, digits and the characters
- _ / . : *. - Holds no
..and no//. - A segment that starts with
:, such as:langor:slug, is a capture. A segment that starts with*, such as*rest, takes the rest of the address. - A capture named
langmust match one of the site's languages. - Between two patterns that match, the one with fewer captures wins. A literal path wins over any pattern.
SEO tag entries
| Box | Stored as | Rules |
|---|---|---|
| Tag | tag | Required. title, meta or link. |
| Attributes | attrs | Required. A JSON object, such as {"name": "description"}. |
| Content | content | Text, optional. |
State entries
| Box | Stored as | Rules |
|---|---|---|
| Assign Name | name | Required. Starts with a lowercase letter or _, then letters, digits or _. Does not start with __. Used once in the list. Not a name the runtime owns, such as page, flash, socket, uploads or current_user. |
| First Value | code | Required. An Elixir expression. It runs once for each visitor when the page mounts. |
Event and info entries
| Field | Box | Stored as | Rules |
|---|---|---|---|
| Events (handle_event) | Event Name | name | Required. The event the template sends, such as click or submit. |
| Events (handle_event) | Event Code | code | Required. Elixir that runs on the socket. |
| Infos (handle_info) | Info Name | name | Required. The message the page handles. |
| Infos (handle_info) | Info Code | code | Required. Elixir that runs on the socket. |
Resource link entries
| Key | Rules |
|---|---|
type | Required text. |
href | Required text. |
rel, media, integrity, crossorigin | Optional text. |
attrs | Optional JSON object. |
Helper entries
| Box | Stored as | Rules |
|---|---|---|
| Function Name | name | Required. |
| Arguments | args | Comma-separated names. Empty makes a function with no arguments. |
| Function Body | code | Required. Elixir. |
Upload entries
Each entry becomes one allow_upload/3 that the page registers when a visitor opens it. Use the name in a file field as upload={@uploads.<name>}.
| Box | Stored as | Default | Rules |
|---|---|---|---|
| Upload Name | name | None | Required. Two entries with one name keep the first. |
| Accepted Types | accept | Any | Comma-separated types, such as image/*, .pdf. |
| Maximum Files | max_entries | 1 | At most 10. |
| Maximum Size (bytes) | max_file_size | 8000000 | At most 20000000. |
| Upload Immediately | auto_upload | Off | A switch. |
Keys of Extra
| Key | Value | What it does |
|---|---|---|
record | Text, such as blog:post | Set by This page shows each. Names the kind of record each address of the page shows. |
serves_all_languages | true | The page answers every language of its site. Needs :lang in the path, or the CMS refuses the page. |
visitors_only | true | The page opens only for a visitor who is not signed in. A signed-in visitor goes to the site root. |
signed_in_only | true | The page opens only for a signed-in visitor. Anyone else goes to the sign-in page. |
auth_role | Text, such as sign_in | Marks a sign-in page. Like visitors_only, it keeps the page out of search indexes unless its SEO tags say otherwise. |
provisioned_by, provisioned_version | Text | Written by the installer and by extensions to say who gave the site the page, and at which version. |
Stored without a box
| Stored as | What it holds |
|---|---|
name | The page's key, made from Title on create. 3 to 200 characters. If a page on any site already holds the name, the CMS adds -2, -3 and so on, so a second site's Pricing page is named pricing-2. A name that a caller passes, as the API and the installer do, has to be free only on its own site. |
preview_token | An unguessable token for the shareable preview address. Made on create. |
inserted_at, updated_at | When the page was created and last changed. |
archived_at | When the page was archived. An archived page cannot be edited. |
Rules and messages
| Rule | Message |
|---|---|
The path starts with / | Path must start with '/' |
| The path is at most 255 characters | Path must be at most 255 characters |
The path has no .. | Path must not contain '..' (directory traversal) |
The path has no // | Path must not contain '//' (empty path segment) |
| The path uses allowed characters | Path may only contain letters, digits, and the characters - _ / . : * |
| The path is unique within the site | Page path must be unique within a site. |
| The name is unique within the site | Page name must be unique within a site. |
| One page per language in a group | A page with this language already exists in this language group. |
| No other active page answers an address of an active page | The page “<title>” at <path> already answers at <address>. |
serves_all_languages needs :lang | Only a path with :lang in it can answer every language. Add :lang to the path, or let the page answer its own language. |
| A saved template is not blank | Template cannot be empty. |
| An archived page is not edited | Cannot edit archived page. Please unarchive it first. |
| A state name is a free assign name | <name> is an assign the runtime already sets; choose another name. |
| Every state entry has a name | Every state entry needs a name. |
| A state name is used once | The state names <name> twice; each name is one assign. |
The CMS also checks that the template, events, infos, state and helpers compile before it stores them, and puts the error under the box it came from. The layout and the language group must belong to the page's site, and a layout may be a shared one.
Who can write
- On the dashboard,
admin:dashboard:runtime:pageoradmin:dashboard:code. On the admin API,admin:api:runtime:pageoradmin:api:code. A wildcard that covers one of them also works. - Any account with a permission under the surface can read pages. A site account reads only its own site's pages.
- A deployment can turn off code that the server runs with
config :mishka_cms_core, :page_code, :off. Then no one can add Elixir to Events, Infos, State or Helper Functions, or type a template by hand, a master included. Code already stored keeps compiling.